/**CFile****************************************************************

  FileName    [reoCore.c]

  PackageName [REO: A specialized DD reordering engine.]

  Synopsis    [Implementation of the core reordering procedure.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - October 15, 2002.]

  Revision    [$Id: reoCore.c,v 1.0 2002/15/10 03:00:00 alanmi Exp $]

***********************************************************************/

#include "reo.h"

////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////

#define CALLOC(type, num)  ((type *) calloc((long)(num), (long)sizeof(type)))

static int  reoRecursiveDeref( reo_unit * pUnit );
static int  reoCheckZeroRefs( reo_plane * pPlane );
static int  reoCheckLevels( reo_man * p );

double s_AplBefore;
double s_AplAfter;

////////////////////////////////////////////////////////////////////////
///                    FUNCTION DEFINITIONS                          ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void reoReorderArray( reo_man * p, DdManager * dd, DdNode * Funcs[], DdNode * FuncsRes[], int nFuncs, int * pOrder )
{
	int Counter, i;

	// set the initial parameters
	p->dd     = dd;
	p->pOrder = pOrder;
	p->nTops  = nFuncs;
	// get the initial number of nodes
	p->nNodesBeg = Cudd_SharingSize( Funcs, nFuncs );     
	// resize the internal data structures of the manager if necessary
	reoResizeStructures( p, ddMax(dd->size,dd->sizeZ), p->nNodesBeg, nFuncs );
	// compute the support
	p->pSupp = Extra_VectorSupportArray( dd, Funcs, nFuncs, p->pSupp );
	// get the number of support variables
	p->nSupp = 0;
	for ( i = 0; i < dd->size; i++ )
		p->nSupp += p->pSupp[i];

	// if it is the constant function, no need to reorder
	if ( p->nSupp == 0 )
	{
		for ( i = 0; i < nFuncs; i++ )
		{
			FuncsRes[i] = Funcs[i]; Cudd_Ref( FuncsRes[i] );
		}
		return;
	}

	// create the internal variable maps
	// go through variable levels in the manager
	Counter = 0;
	for ( i = 0; i < dd->size; i++ )
		if ( p->pSupp[ dd->invperm[i] ] )
		{
			p->pMapToPlanes[ dd->invperm[i] ] = Counter;
			p->pMapToDdVarsOrig[Counter]      = dd->invperm[i];
			if ( !p->fRemapUp )
				p->pMapToDdVarsFinal[Counter] = dd->invperm[i];
			else
				p->pMapToDdVarsFinal[Counter] = dd->invperm[Counter];
			p->pOrderInt[Counter]        = Counter;
			Counter++;
		}

	// set the initial parameters
	p->nUnitsUsed = 0;
	p->nNodesCur  = 0;
	p->fThisIsAdd = 0;
	p->Signature++;
	// transfer the function from the CUDD package into REO"s internal data structure
	for ( i = 0; i < nFuncs; i++ )
		p->pTops[i] = reoTransferNodesToUnits_rec( p, Funcs[i] );
	assert( p->nNodesBeg == p->nNodesCur );

	if ( !p->fThisIsAdd && p->fMinWidth )
	{
		printf( "An important message from the REO reordering engine:\n" );
		printf( "The BDD given to the engine for reordering contains complemented edges.\n" );
		printf( "Currently, such BDDs cannot be reordered for the minimum width.\n" );
		printf( "Therefore, minimization for the number of BDD nodes is performed.\n" );
		fflush( stdout );
		p->fMinApl   = 0;
		p->fMinWidth = 0;
	}

	if ( p->fMinWidth )
		reoProfileWidthStart(p);
	else if ( p->fMinApl )
		reoProfileAplStart(p);
	else 
		reoProfileNodesStart(p);

	if ( p->fVerbose )
	{
		printf( "INITIAL: " );
		if ( p->fMinWidth )
			reoProfileWidthPrint(p);
		else if ( p->fMinApl )
			reoProfileAplPrint(p);
		else
			reoProfileNodesPrint(p);
	}
 
	///////////////////////////////////////////////////////////////////
	// performs the reordering
	p->nSwaps   = 0;
	p->nNISwaps = 0;
	for ( i = 0; i < p->nIters; i++ )
	{
		reoReorderSift( p );
		// print statistics after each iteration
		if ( p->fVerbose )
		{
			printf( "ITER #%d: ", i+1 );
			if ( p->fMinWidth )
				reoProfileWidthPrint(p);
			else if ( p->fMinApl )
				reoProfileAplPrint(p);
			else
				reoProfileNodesPrint(p);
		}
		// if the cost function did not change, stop iterating
		if ( p->fMinWidth )
		{
			p->nWidthEnd = p->nWidthCur;
			assert( p->nWidthEnd <= p->nWidthBeg );
			if ( p->nWidthEnd == p->nWidthBeg )
				break;
		}
		else if ( p->fMinApl )
		{
			p->nAplEnd = p->nAplCur;
			assert( p->nAplEnd <= p->nAplBeg );
			if ( p->nAplEnd == p->nAplBeg )
				break;
		}
		else
		{
			p->nNodesEnd = p->nNodesCur;
			assert( p->nNodesEnd <= p->nNodesBeg );
			if ( p->nNodesEnd == p->nNodesBeg )
				break;
		}
	}
	assert( reoCheckLevels( p ) );
	///////////////////////////////////////////////////////////////////

s_AplBefore = p->nAplBeg;
s_AplAfter  = p->nAplEnd;

	// set the initial parameters
	p->nRefNodes  = 0;
	p->nNodesCur  = 0;
	p->Signature++;
	// transfer the BDDs from REO's internal data structure to CUDD
	for ( i = 0; i < nFuncs; i++ )
	{
		FuncsRes[i] = reoTransferUnitsToNodes_rec( p, p->pTops[i] ); Cudd_Ref( FuncsRes[i] );
	}
	// undo the DDs referenced for storing in the cache
	for ( i = 0; i < p->nRefNodes; i++ )
		Cudd_RecursiveDeref( dd, p->pRefNodes[i] );
	// verify zero refs of the terminal nodes
	for ( i = 0; i < nFuncs; i++ )
	{
		assert( reoRecursiveDeref( p->pTops[i] ) );
	}
	assert( reoCheckZeroRefs( &(p->pPlanes[p->nSupp]) ) );

	// prepare the variable map to return to the user
	if ( p->pOrder )
	{
		// i is the current level in the planes data structure
		// p->pOrderInt[i] is the original level in the planes data structure
		// p->pMapToDdVarsOrig[i] is the variable, into which we remap when we construct the BDD from planes
		// p->pMapToDdVarsOrig[ p->pOrderInt[i] ] is the original BDD variable corresponding to this level
		// Therefore, p->pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ]
		// creates the permutation, which remaps the resulting BDD variable into the original BDD variable
		for ( i = 0; i < p->nSupp; i++ )
			p->pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ]; 
	}

	if ( p->fVerify )
	{
		int fVerification;
		DdNode * FuncRemapped;
		int * pOrder;

		if ( p->pOrder == NULL )
		{
			pOrder = ALLOC( int, p->nSupp );
			for ( i = 0; i < p->nSupp; i++ )
				pOrder[ p->pMapToDdVarsFinal[i] ] = p->pMapToDdVarsOrig[ p->pOrderInt[i] ]; 
		}
		else
			pOrder = p->pOrder;

		fVerification = 1;
		for ( i = 0; i < nFuncs; i++ )
		{
			// verify the result
			if ( p->fThisIsAdd )
				FuncRemapped = Cudd_addPermute( dd, FuncsRes[i], pOrder );
			else
				FuncRemapped = Cudd_bddPermute( dd, FuncsRes[i], pOrder );
			Cudd_Ref( FuncRemapped );

			if ( FuncRemapped != Funcs[i] )
			{
				fVerification = 0;
				printf( "REO: Internal verification has failed!\n" );
				fflush( stdout );
			}
			Cudd_RecursiveDeref( dd, FuncRemapped );
		}
		if ( fVerification )
			printf( "REO: Internal verification is okay!\n" );

		if ( p->pOrder == NULL )
			free( pOrder );
	}

	// recycle the data structure
	for ( i = 0; i <= p->nSupp; i++ )
		reoUnitsRecycleUnitList( p, p->pPlanes + i );
}

/**Function*************************************************************

  Synopsis    [Resizes the internal manager data structures.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void reoResizeStructures( reo_man * p, int nDdVarsMax, int nNodesMax, int nFuncs )
{
	// resize data structures depending on the number of variables in the DD manager
	if ( p->nSuppAlloc == 0 )
	{
		p->pSupp             = ALLOC( int,        nDdVarsMax + 1 );
		p->pOrderInt         = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToPlanes      = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToDdVarsOrig  = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToDdVarsFinal = ALLOC( int,        nDdVarsMax + 1 );
		p->pPlanes           = CALLOC( reo_plane, nDdVarsMax + 1 );
		p->pVarCosts         = ALLOC( double,     nDdVarsMax + 1 );
		p->pLevelOrder       = ALLOC( int,        nDdVarsMax + 1 );
		p->nSuppAlloc        = nDdVarsMax + 1;
	}
	else if ( p->nSuppAlloc < nDdVarsMax )
	{
		free( p->pSupp );
		free( p->pOrderInt );
		free( p->pMapToPlanes );
		free( p->pMapToDdVarsOrig );
		free( p->pMapToDdVarsFinal );
		free( p->pPlanes );
		free( p->pVarCosts );
		free( p->pLevelOrder );

		p->pSupp             = ALLOC( int,        nDdVarsMax + 1 );
		p->pOrderInt         = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToPlanes      = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToDdVarsOrig  = ALLOC( int,        nDdVarsMax + 1 );
		p->pMapToDdVarsFinal = ALLOC( int,        nDdVarsMax + 1 );
		p->pPlanes           = CALLOC( reo_plane, nDdVarsMax + 1 );
		p->pVarCosts         = ALLOC( double,     nDdVarsMax + 1 );
		p->pLevelOrder       = ALLOC( int,        nDdVarsMax + 1 );
		p->nSuppAlloc        = nDdVarsMax + 1;
	}

	// resize the data structures depending on the number of nodes
	if ( p->nRefNodesAlloc == 0 )
	{
		p->nNodesMaxAlloc  = nNodesMax;
		p->nTableSize      = 3*nNodesMax + 1;
		p->nRefNodesAlloc  = 3*nNodesMax + 1;
		p->nMemChunksAlloc = (10*nNodesMax + 1)/REO_CHUNK_SIZE + 1;

		p->HTable          = CALLOC( reo_hash,  p->nTableSize );
		p->pRefNodes       = ALLOC( DdNode *,   p->nRefNodesAlloc );
		p->pWidthCofs      = ALLOC( reo_unit *, p->nRefNodesAlloc );
		p->pMemChunks      = ALLOC( reo_unit *, p->nMemChunksAlloc );
	}
	else if ( p->nNodesMaxAlloc < nNodesMax )
	{
		void * pTemp;
		int nMemChunksAllocPrev = p->nMemChunksAlloc;

		p->nNodesMaxAlloc  = nNodesMax;
		p->nTableSize      = 3*nNodesMax + 1;
		p->nRefNodesAlloc  = 3*nNodesMax + 1;
		p->nMemChunksAlloc = (10*nNodesMax + 1)/REO_CHUNK_SIZE + 1;

		free( p->HTable );
		free( p->pRefNodes );
		free( p->pWidthCofs );
		p->HTable          = CALLOC( reo_hash,    p->nTableSize );
		p->pRefNodes       = ALLOC(  DdNode *,    p->nRefNodesAlloc );
		p->pWidthCofs      = ALLOC(  reo_unit *,  p->nRefNodesAlloc );
		// p->pMemChunks should be reallocated because it contains pointers currently in use
		pTemp              = ALLOC(  reo_unit *,  p->nMemChunksAlloc );
		memmove( pTemp, p->pMemChunks, sizeof(reo_unit *) * nMemChunksAllocPrev );
		free( p->pMemChunks );
		p->pMemChunks      = pTemp;
	}

	// resize the data structures depending on the number of functions
	if ( p->nTopsAlloc == 0 )
	{
		p->pTops      = ALLOC( reo_unit *, nFuncs );
		p->nTopsAlloc = nFuncs;
	}
	else if ( p->nTopsAlloc < nFuncs )
	{
		free( p->pTops );
		p->pTops      = ALLOC( reo_unit *, nFuncs );
		p->nTopsAlloc = nFuncs;
	}
}


/**Function*************************************************************

  Synopsis    [Dereferences units the data structure after reordering.]

  Description [This function is only useful for debugging.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
int reoRecursiveDeref( reo_unit * pUnit )
{
	reo_unit * pUnitR;
	pUnitR = Unit_Regular(pUnit);
	pUnitR->n--;
	if ( Unit_IsConstant(pUnitR) )
		return 1;
	if ( pUnitR->n == 0 )
	{
		reoRecursiveDeref( pUnitR->pE );
		reoRecursiveDeref( pUnitR->pT );
	}
	return 1;
}

/**Function*************************************************************

  Synopsis    [Checks the zero references for the given plane.]

  Description [This function is only useful for debugging.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
int reoCheckZeroRefs( reo_plane * pPlane )
{
	reo_unit * pUnit;
	for ( pUnit = pPlane->pHead; pUnit; pUnit = pUnit->Next )
	{
		if ( pUnit->n != 0 )
		{
			assert( 0 );
		}
	}
	return 1;
}

/**Function*************************************************************

  Synopsis    [Checks the zero references for the given plane.]

  Description [This function is only useful for debugging.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
int reoCheckLevels( reo_man * p )
{
	reo_unit * pUnit;
	int i;

	for ( i = 0; i < p->nSupp; i++ )
	{
		// there are some nodes left on each level
		assert( p->pPlanes[i].statsNodes );
		for ( pUnit = p->pPlanes[i].pHead; pUnit; pUnit = pUnit->Next )
		{
			// the level is properly set
			assert( pUnit->lev == i );
		}
	}
	return 1;
}

////////////////////////////////////////////////////////////////////////
///                         END OF FILE                              ///
////////////////////////////////////////////////////////////////////////

