//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1998
//
//  File:       cliqwork.cpp
//
//--------------------------------------------------------------------------

//
//	cliqwork.cpp
//

#include <basetsd.h>
#include "cliqset.h"
#include "clique.h"
#include "cliqwork.h"

#ifdef _DEBUG
//	#define DUMP
#endif

//  Sort helper 'less' function for sorting arrays of node pointers into 'mark' sequence.
class MARKSRTPGND : public binary_function<const GNODEMBN *, const GNODEMBN *, bool>
{	
  public:
	bool operator () (const GNODEMBN * pa, const GNODEMBN * pb) const
		{	return pa->IMark() < pb->IMark() ;	}
};

#ifdef _DEBUG
static void seqchkVpnodeByMark (const VPGNODEMBN & vpgnd)
{
	int imrk = INT_MIN;
	int imrk2;
	for ( int i = 0; i < vpgnd.size(); i++, imrk = imrk2)
	{
		imrk2 = vpgnd[i]->IMark();
		assert( imrk2 >= 0 );
		assert( imrk2 >= imrk );
	}
}
#endif

//  Sort the clique information array into topological sequence
void CLIQSETWORK :: TopSortNodeCliqueInfo ()
{
	sort( _vndcqInfo.begin(), _vndcqInfo.end() );
}

//  Sort the given node pointer array in to "mark" (cliquing order) sequence
void CLIQSETWORK :: MarkSortNodePtrArray ( VPGNODEMBN & vpgnd )
{
	MARKSRTPGND marksorter;
	sort( vpgnd.begin(), vpgnd.end(), marksorter );

#ifdef _DEBUG
	seqchkVpnodeByMark( vpgnd );
#endif
}

//  Establish an absolute ordering based upon the topological ordering
void  CLIQSETWORK :: RenumberNodesForCliquing ()
{
	//  Perform a topological sort of the network
	Model().TopSortNodes();

	MODEL::MODELENUM mdlenum( Model() );
	GELEMLNK * pgelm;
	_vndcqInfo.clear();

	//  Collect all the nodes into a pointer array
	while ( pgelm = mdlenum.PlnkelNext() )
	{	
		if ( pgelm->EType() != GOBJMBN::EBNO_NODE )
			continue;
			
		NDCQINFO ndcq;
		DynCastThrow( pgelm, ndcq._pgnd );

		_vndcqInfo.push_back( ndcq );
	}
	//  Sort the array into topological sequence.
	TopSortNodeCliqueInfo();

#ifdef _DEBUG
	int iTop = -1;
#endif

	//  Establish the total ordering based upon topological level.
	for ( int i = 0; i < _vndcqInfo.size() ; i++ )
	{
		GNODEMBN * pgnd = _vndcqInfo[i]._pgnd;
		assert( pgnd );
#ifdef _DEBUG
		//  Check sequence.
		assert( iTop <= pgnd->ITopLevel() );
		iTop = pgnd->ITopLevel();
#endif
		pgnd->IMark() = i;
	}
}

void CLIQSETWORK :: PrepareForBuild ()
{
	//  Resize and initialize the work arrays
	int cCliques = _vvpgnd.size();

	_viParent.resize( cCliques );
	_viOrder.resize( cCliques );
	_viCNodesCommon.resize( cCliques );
	_viICliqCommon.resize( cCliques );
	_viOrdered.clear();

	for ( int iClique = 0; iClique < cCliques; iClique++ )
	{
		MarkSortNodePtrArray( _vvpgnd[iClique] );

		_viParent[iClique]			= INT_MIN;
		_viOrder[iClique]			= INT_MIN;
		_viCNodesCommon[iClique]	= INT_MIN;
		_viICliqCommon[iClique]		= INT_MIN;
	}
}

//	Return the number of nodes in common between the two cliques
int CLIQSETWORK :: CNodesCommon ( int iClique1, int iClique2 )
{
	assert( iClique1 < _vvpgnd.size() && iClique2 < _vvpgnd.size() );

	return CNodesCommon( _vvpgnd[iClique1], _vvpgnd[iClique2] );
}

//	Return the number of nodes in common between the two node lists
int CLIQSETWORK :: CNodesCommon ( const VPGNODEMBN & vpgnd1, const VPGNODEMBN & vpgnd2 )
{
	MARKSRTPGND marksorter;

#ifdef _DEBUG
	seqchkVpnodeByMark( vpgnd1 );
	seqchkVpnodeByMark( vpgnd2 );
#endif

	int cCommon = count_set_intersection( vpgnd1.begin(),
										   vpgnd1.end(),
										   vpgnd2.begin(),
										   vpgnd2.end(),
										   marksorter );
	return cCommon;
}


//  Return the ordered index of a clique or -1 if not in the tree yet.
inline
int CLIQSETWORK :: IOrdered ( int iClique )
{
	return ifind( _viOrdered, iClique );
}

//  Update the "most common clique" info of iClique1 based upon iClique2.  This is
//  used to count the number of nodes in common between a candidate clique and a
//	clique already in the tree.
void CLIQSETWORK :: SetCNodeMaxCommon ( int iClique1, int iCliqueOrdered2 )
{
	assert( iCliqueOrdered2 < _viOrdered.size() );

	int iClique2 = _viOrdered[iCliqueOrdered2];
	int cCommon = CNodesCommon( iClique1, iClique2 );
	if ( cCommon > _viCNodesCommon[iClique1] )
	{
		_viCNodesCommon[iClique1] = cCommon;
		_viICliqCommon[iClique1] = iCliqueOrdered2;
	}
}

//
//	Completely update the "most common clique" information for this clique.
//	This is necessary because cliques can change membership due to subsumption
//	during generation of the clique tree.
//	Return true if there is any overlap with a clique already in the tree.
//
bool CLIQSETWORK :: BUpdateCNodeMaxCommon ( int iClique )
{
	assert( _viOrder[iClique] == INT_MIN );
	int & cNodesCommon = _viCNodesCommon[iClique];
	int & iCliqCommon = _viICliqCommon[iClique];
	cNodesCommon = INT_MIN;
	iCliqCommon = INT_MIN;
	for ( int iord = 0; iord < _viOrdered.size(); iord++ )		
	{
		SetCNodeMaxCommon( iClique, iord );
	}
	return cNodesCommon > 0;
}

//  Return true if clique 1 has more nodes in common with a clique that is already in
//		the tree than clique2.  If they have the same number of nodes in common, return
//		true if clique 1 has fewer nodes than clique2.
bool CLIQSETWORK :: BBetter ( int iClique1, int iClique2 )
{
	assert( _viCNodesCommon[iClique1] >= 0 );
	assert( _viCNodesCommon[iClique2] >= 0 );

	if ( _viCNodesCommon[iClique1] != _viCNodesCommon[iClique2] )
		return _viCNodesCommon[iClique1] > _viCNodesCommon[iClique2];

	return _vvpgnd[iClique1].size() < _vvpgnd[iClique2].size();
}


//  After building the cliques, topologically sort them and anchor each node
//  to the highest clique in the tree to which it belongs.
void CLIQSETWORK :: SetTopologicalInfo ()
{
#ifdef DUMP
	DumpTree();
#endif

	//  First, set up the ordered parent information array
	int cCliqueOrdered = _viOrdered.size();
	assert( cCliqueOrdered > 0 );
	int cClique = _viOrder.size();

	_viParentOrdered.resize(cCliqueOrdered);
	for ( int icq = 0; icq < cCliqueOrdered; ++icq )
	{
		int iClique = _viOrdered[icq];
		assert( iClique < cClique && iClique >= 0 );
		int iCliqueParent = _viParent[iClique];
		assert( iCliqueParent < cClique && iCliqueParent >= 0 );
		assert( CNodesCommon( iClique, iCliqueParent ) > 0 );
		int iCliqueParentOrdered = IOrdered( iCliqueParent );
		assert( iCliqueParentOrdered < cCliqueOrdered && iCliqueParentOrdered >= 0 );
		_viParentOrdered[icq] = iCliqueParentOrdered;
	}

	//  Next, follow each ordered clique's parentage to compute its topological level
	_viTopLevelOrdered.resize(cCliqueOrdered);
	int cTrees = 0;
	for ( icq = 0; icq < cCliqueOrdered; ++icq )
	{
		int icqParent  = icq;
		//  Follow until we get to a (the) root clique
		for ( int itop = 0; icqParent != _viParentOrdered[icqParent]; ++itop )
		{
			assert( itop < cCliqueOrdered );
			icqParent = _viParentOrdered[icqParent];
		}
		if ( itop == 0 )
			cTrees++ ;
		_viTopLevelOrdered[icq] = itop;
	}
	assert( cTrees == _cTrees );

	//  Next, find each node's "family" clique.  This is the smallest clique containing
	//  it and its parents.

	VPGNODEMBN vpgnd;
	for ( int ind = 0 ; ind < _vndcqInfo.size(); ind++ )
	{
		NDCQINFO & ndcq = _vndcqInfo[ind];
		vpgnd.clear();
		//  Get the "family" set and sort it for matching other cliques.
		ndcq._pgnd->GetFamily( vpgnd );
		MarkSortNodePtrArray( vpgnd );

		int cFamily = vpgnd.size();
		int cCommonSize = INT_MAX;
		int iCqCommon = -1;

		//  Find the smallest clique containing the family
		for ( icq = 0; icq < cCliqueOrdered; ++icq )
		{
			const VPGNODEMBN & vpgndClique = _vvpgnd[ _viOrdered[icq] ];
			int cCqCommon = CNodesCommon( vpgnd, vpgndClique );
			//  See if this clique contains the family and is smaller than any other.
			if ( cCqCommon == cFamily && vpgndClique.size() < cCommonSize )
			{	
				iCqCommon = icq;
			}
		}
		assert( iCqCommon >= 0 );
		ndcq._iCliqOrdFamily = iCqCommon;
		
		//  Now, find the highest clique in the tree containing this node.
		int itop = INT_MAX;
		int iCqTop = -1;
		for ( icq = 0; icq < cCliqueOrdered; ++icq )
		{
			const VPGNODEMBN & vpgndClique = _vvpgnd[ _viOrdered[icq] ];
			int ind = ifind( vpgndClique, ndcq._pgnd );
			if ( ind >= 0 && _viTopLevelOrdered[icq] < itop )
			{	
				iCqTop = icq;
				itop = _viTopLevelOrdered[icq];
			}
		}
		assert( iCqTop >= 0 );
		ndcq._iCliqOrdSelf = iCqTop;
	}

#ifdef DUMP
	DumpTopInfo();
#endif
}

void CLIQSETWORK :: BuildCliques ()
{
	//  Prepare tables for junction tree construction
	PrepareForBuild() ;

	//  Choose the zeroth arbitrarily as a starting point; set it as its own parent.
	//  As we iterate over the array, we assign an ordering to cliques.  If the clique has
	//  already been ordered, its value in _viOrder will either >= 0 (order in clique tree)
	//	
	_cTrees = 1;

	_viParent[0] = 0;
	_viOrder[0] = 0;
	_viOrdered.clear();
	_viOrdered.push_back(0);

	for (;;)
	{
		int iCliqueBest = INT_MAX;			//  Best clique found so far

		// Find a new clique that has the largest overlap with any of the cliques already in the tree.
		for ( int iClique = 0; iClique < _vvpgnd.size(); iClique++ )
		{
			int iord = _viOrder[iClique];
			if ( iord != INT_MIN )
				continue;	// Clique has already been ordered or dealt with

			//  Update the "most common clique already in tree" info between this clique
			//		and all the cliques in the trees
			BUpdateCNodeMaxCommon( iClique );

			//MSRDEVBUG:  SetCNodeMaxCommon( iClique, _viOrdered.size() - 1 );

			if ( iCliqueBest == INT_MAX )
			{
				// first time through the loop
				iCliqueBest = iClique;
			}
			else
			if ( BBetter( iClique, iCliqueBest ) )
			{
				//  This clique has an overlap as large as any other yet found.
				iCliqueBest = iClique;
			}
		}
		//  See if we're done
		if ( iCliqueBest == INT_MAX )
			break;

		// Get the ordered index and absolute index of the most common clique
		int iCliqueCommonOrdered = _viICliqCommon[iCliqueBest];
		assert( iCliqueCommonOrdered >= 0 && iCliqueCommonOrdered < _viOrdered.size() );
		int iCliqueCommon = _viOrdered[ iCliqueCommonOrdered ];
		assert( iCliqueCommon >= 0 );
		assert( iCliqueBest != iCliqueCommon );
		int cNodesCommon = _viCNodesCommon[iCliqueBest];
		assert( cNodesCommon <= _vvpgnd[iCliqueCommon].size() );
		assert( cNodesCommon <= _vvpgnd[iCliqueBest].size() );
		assert( cNodesCommon == CNodesCommon( iCliqueCommon, iCliqueBest ) ) ;

		//  Index of clique to be added to ordered clique set
		int iCliqueNew = INT_MAX;

		//  If the candidate clique has the same number of nodes in common with its most
		//  common clique as that clique has members, then this clique is either identical
		//  to or a superset of that clique.

		if ( cNodesCommon == _vvpgnd[iCliqueCommon].size() )
		{
			//  New clique is superset of its most common clique.
			assert( cNodesCommon != 0 );
			assert( iCliqueCommon != iCliqueBest );
			assert( _vvpgnd[iCliqueCommon].size() < _vvpgnd[iCliqueBest].size() );

			//  Assign this clique's node set to the previously ordered subset clique
			_vvpgnd[iCliqueCommon] = _vvpgnd[iCliqueBest] ;
			assert ( _vvpgnd[iCliqueCommon].size() == _vvpgnd[iCliqueBest].size() );
			//  Leave the parent the same as it was
			iCliqueNew = iCliqueCommon;
		}
		else
		if ( cNodesCommon == 0 )
		{
			//  This is the start of a new tree
			_cTrees++;
			//  Self and parent are the same
			_viParent[iCliqueBest] = iCliqueNew = iCliqueBest;
			_viOrdered.push_back( iCliqueNew );
		}
		else
		if ( cNodesCommon != _vvpgnd[iCliqueBest].size() )
		{
			//  New clique is child of existing clique.
			iCliqueNew = iCliqueBest;
			_viParent[iCliqueBest] = iCliqueCommon ;
			//  Keep this clique by adding it to the ordered clique set.
			_viOrdered.push_back( iCliqueNew );
		}
		else
		{
			//  Child is subset of parent; ignore by marking as "subsumed"
			iCliqueNew = - iCliqueCommon;
		}

		//  Mark the clique as either ordered or subsumed.
		_viOrder[iCliqueBest] = iCliqueNew;
	}	

#ifdef DUMP
	cout << "\n\nBuild cliques;  generated " << _cTrees << " clique trees\n\n";
#endif
}

//  Verify that the Running Intersection Property holds for this clique tree.
bool CLIQSETWORK :: BCheckRIP ()
{
	// Check that topological information has been generated
	assert( _viOrdered.size() == _viParentOrdered.size() );

	for ( int iCliqueOrdered = 0; iCliqueOrdered < _viOrdered.size(); iCliqueOrdered++ )
	{
		if ( ! BCheckRIP( iCliqueOrdered ) )
			return false;
	}
	return true;
}

//  Verify that the Running Intersection Property holds for this clique.
bool CLIQSETWORK :: BCheckRIP ( int iCliqueOrdered )
{
	int iClique = _viOrdered[iCliqueOrdered];
	const VPGNODEMBN & vpgndClique = _vvpgnd[iClique];
	int iCliqueParent = _viParent[iClique];
	const VPGNODEMBN & vpgndCliqueParent = _vvpgnd[iCliqueParent];

	bool bRoot = iCliqueParent == iClique;

	// For every node in this clique, check that either:
	//
	//		1) this is a root clique, or
	//		2) the node is present in the parent clique.
	//
	// If this test fails, check that this is the "self" clique,
	//		which is the highest clique in the tree in which the
	//		node appears.
	//
	for ( int iNode = 0; iNode < vpgndClique.size(); iNode++ )
	{
		//  Access the node information for this node
		GNODEMBN * pgnd = vpgndClique[iNode];
		if ( bRoot || ifind( vpgndCliqueParent, pgnd ) < 0 )
		{
			NDCQINFO & ndcq = _vndcqInfo[ pgnd->IMark() ];
			if ( ndcq._iCliqOrdSelf != iCliqueOrdered )
			{			
#ifdef _DEBUG
				cout << "RIP FAILURE: node "
					 << ndcq._pgnd->ZsrefName().Szc()
					 << " is in clique "
					 << iCliqueOrdered
					 << " but absent from "
					 << _viParentOrdered[iCliqueOrdered]
					 << "("
					 << _viParent[iClique]
					 << ")"
					 ;
#endif
				return false;
			}
		}
	}
	return true;
}

//  Using the constructed tables, create the clique objects and
//  link them to each other and their member nodes.

void CLIQSETWORK :: CreateTopology ()
{
	_vpclq.resize( _viOrdered.size() ) ;
	for ( int i = 0; i < _vpclq.size(); )
		_vpclq[i++] = NULL;

	int iInferEngID = _cliqset._iInferEngID;

	int ccq = 0;	// Total cliques created

	//  Create all cliques.  Iterate in topological order, creating
	//		the cliques and linking them to their parents.
	for ( int itop = 0;; itop++)
	{
		int ccqLevel = 0;	// Number of cliques added at this topological level
		for ( int icq = 0; icq < _viOrdered.size(); icq++ )
		{
			if ( _viTopLevelOrdered[icq] != itop )
				continue;

			GOBJMBN_CLIQUE * pclqParent = NULL;
			GOBJMBN_CLIQUE * pclqThis = NULL;
			int iParentOrdered = _viParentOrdered[icq];
			if ( iParentOrdered != icq )
			{
				//  Get the parent clique pointer
				pclqParent = _vpclq[ iParentOrdered ];
				assert( pclqParent );
			}
			else
			{
				//  Root cliques have toplevel zero
				assert( itop == 0 );
			}
			//  Create the new clique and its edge to its parent clique (if any)
			pclqThis = _vpclq[icq] = new GOBJMBN_CLIQUE( icq, iInferEngID );
			Model().AddElem( pclqThis );
			if ( pclqParent )
			{
				//  This is not a root clique; link it to its parent.
				Model().AddElem( new GEDGEMBN_SEPSET( pclqParent, pclqThis ) );
			}
			else
			{
				//  This IS a root clique; mark it and link it to the clique set top.
				pclqThis->_bRoot = true;
				Model().AddElem( new GEDGEMBN_CLIQSET( & _cliqset, pclqThis ) );
			}

			++_cliqset._cCliques;

			if ( pclqParent )
			{
				++_cliqset._cSepsetArcs;
			}
			ccq++;
			ccqLevel++;
		}
		if ( ccqLevel == 0 )
			break; // No cliques added at this topological level: we're done
	}
	assert( ccq == _viOrdered.size() );

	//  For each of the new cliques, add all members
	for ( i = 0; i < _vpclq.size(); i++ )
	{
		const VPGNODEMBN & vpgndMembers = _vvpgnd[ _viOrdered[i] ];

		for ( int ind = 0; ind < vpgndMembers.size(); ind++)
		{
			//  Get the node pointer and the data pointer
			GNODEMBN * pgnd = vpgndMembers[ind];
			const NDCQINFO & ndcq = _vndcqInfo[ pgnd->IMark() ];
			assert( pgnd == ndcq._pgnd );
			int fRole = GEDGEMBN_CLIQ::NONE;
			if ( ndcq._iCliqOrdSelf == i )	
				fRole |= GEDGEMBN_CLIQ::SELF;
			if ( ndcq._iCliqOrdFamily == i )	
				fRole |= GEDGEMBN_CLIQ::FAMILY;

			Model().AddElem( new GEDGEMBN_CLIQ( _vpclq[i], pgnd, fRole ) );
			++_cliqset._cCliqueMemberArcs;
		}
	}

#ifdef _DEBUG
	for ( i = 0; i < _vpclq.size(); i++ )
	{
		const VPGNODEMBN & vpgndMembers = _vvpgnd[ _viOrdered[i] ];
		VPGNODEMBN vpgndMembers2;
		_vpclq[i]->GetMembers( vpgndMembers2 );
		assert( vpgndMembers2.size() == vpgndMembers.size() );
		MarkSortNodePtrArray( vpgndMembers2 );
		assert( vpgndMembers2 == vpgndMembers );

		//  Exercise the topology by locating the "self" and "family" cliques
		for ( int imbr = 0; imbr < vpgndMembers.size(); imbr++ )
		{
			GNODEMBN * pgnd = vpgndMembers[imbr];
			GOBJMBN_CLIQUE * pCliqueFamily = _cliqset.PCliqueFromNode( pgnd, false );
			GOBJMBN_CLIQUE * pCliqueSelf = _cliqset.PCliqueFromNode( pgnd, false );
			assert( pCliqueFamily );
			assert( pCliqueSelf );
		}
	}
#endif
}

void CLIQSETWORK :: DumpClique ( int iClique )
{
	cout << "\tClique "
		<< iClique
		<< ':'
		<< _vvpgnd[iClique]
		<< "\n";		
	cout.flush();
}

void CLIQSETWORK :: DumpCliques ()
{
	for ( int iClique = 0; iClique < _vvpgnd.size(); ++iClique )
	{
		DumpClique( iClique );
	}
}

void CLIQSETWORK :: DumpTree ()
{
	for ( int iCliqueOrd = 0; iCliqueOrd < _viOrdered.size(); ++iCliqueOrd )
	{
		int iClique = _viOrdered[iCliqueOrd];

		cout << "\tTree Clique "
			<< iCliqueOrd
			<< " ("
			<< iClique
			<< "), parent "
			<< IOrdered( _viParent[iClique] )
			<< " ("
			<< _viParent[iClique]
			<< "): "
			<< _vvpgnd[iClique]
			<< "\n";		
	}
	cout.flush();
}

void CLIQSETWORK :: DumpTopInfo()
{
	for ( int iCliqueOrd = 0; iCliqueOrd < _viOrdered.size(); ++iCliqueOrd )
	{
		cout << "\tTree Clique "
			 << iCliqueOrd
			 << " (" << _viOrdered[iCliqueOrd] << ")"
			 << ", parent is "
			 << _viParentOrdered[iCliqueOrd]
			 << " (" << _viOrdered[_viParentOrdered[iCliqueOrd]] << ")"
			 << ", top level is "
			 << _viTopLevelOrdered[iCliqueOrd]
			 << "\n";
	}

	for ( int ind = 0 ; ind < _vndcqInfo.size(); ind++ )
	{
		NDCQINFO & ndcq = _vndcqInfo[ind];
		cout << "\tNode ";
		cout.width( 20 );
		cout << ndcq._pgnd->ZsrefName().Szc()
			 << "\tfamily is clique "
			 << ndcq._iCliqOrdFamily
			 << ", self is clique "
			 << ndcq._iCliqOrdSelf
			 << "\n";
	}
	cout.flush();
}

//
//	Estimate the total size of the structures necessary to support the
//	compute clique trees.
//
REAL CLIQSETWORK :: REstimatedSize ()
{
	int cClique = 0;
	int cSepsetArc = 0;
	int cCliqsetArc = 0;
	size_t cMbrArc = 0;
	int cCliqueEntries = 0;
	int cFamEntries = 0;

	for ( int icq = 0; icq < _viOrdered.size(); icq++ )
	{
		cClique++;	
		if ( icq != _viParentOrdered[icq] )
		{
			// Clique has a parent
			cSepsetArc++;
		}
		else
		{	
			//  Clique is root
			cCliqsetArc++;
		}

		//  Account for clique membership arcs
		const VPGNODEMBN & vpgndMembers = _vvpgnd[ _viOrdered[icq] ];
		int cMbr = vpgndMembers.size();
		cMbrArc += vpgndMembers.size();

		//  Compute the size of the joint table for this clique
		VIMD vimd(cMbr);
		GNODEMBND * pgndd;
		for ( int ind = 0; ind < vpgndMembers.size(); ind++)
		{
			//  Get the discrete node pointer and the data pointer
			DynCastThrow( vpgndMembers[ind], pgndd );
			//  Add to the clique's dimensionality
			vimd[ind] = pgndd->CState();

			const NDCQINFO & ndcq = _vndcqInfo[ pgndd->IMark() ];
			assert( pgndd == ndcq._pgnd );

			//  If this is the edge to the "family" clique, it will
			//	contain the reordered discrete conditional probabilities
			//	for this node, so we must compute it size.
			if ( ndcq._iCliqOrdFamily == icq )	
			{
				//  This is the edge leading to this node's "family" clique
				VPGNODEMBN vpgndFamily;  // List of parents and self
				pgndd->GetParents( vpgndFamily, true );
				GNODEMBND * pgnddFamily;
				int cStates = 1;
				for ( int ifam = 0; ifam < vpgndFamily.size(); ifam++ )
				{
					DynCastThrow( vpgndFamily[ifam], pgnddFamily );
					cStates *= pgnddFamily->CState();
				}
				cFamEntries += cStates;
			}
		}
		MDVSLICE mdvs( vimd );
		cCliqueEntries += mdvs._Totlen();
	}

	REAL rcb = 0;
	rcb += cClique * sizeof(GOBJMBN_CLIQUE);
	rcb += cSepsetArc * sizeof(GEDGEMBN_SEPSET);
	rcb += cCliqsetArc * sizeof(GEDGEMBN_CLIQSET);
	rcb += cMbrArc * sizeof(GEDGEMBN_CLIQ);
	rcb += cCliqueEntries * sizeof(REAL);
	rcb += cFamEntries * sizeof(REAL);

#ifdef DUMP
	cout << "\nEstimated clique tree memory is " << rcb;
#endif

	return rcb;
}