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

  FileName    [reoSwap.c]

  PackageName [REO: A specialized DD reordering engine.]

  Synopsis    [Implementation of the two-variable swap.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "reo.h"

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

#define AddToLinkedList( ppList, pLink )   (((pLink)->Next = *(ppList)), (*(ppList) = (pLink)))

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

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

  Synopsis    []

  Description [Takes the level (lev0) of the plane, which should be swapped 
  with the next plane. Returns the gain using the current cost function.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
double reoReorderSwapAdjacentVars( reo_man * p, int lev0, int fMovingUp )
{
	// the levels in the decision diagram
	int lev1 = lev0 + 1, lev2 = lev0 + 2;
	// the new nodes on lev0
	reo_unit * pLoop, * pUnit;
	// the new nodes on lev1
	reo_unit * pNewPlane20, * pNewPlane21, * pNewPlane20R;
	reo_unit * pUnitE, * pUnitER, * pUnitT;
	// the nodes below lev1
	reo_unit * pNew1E, * pNew1T, * pNew2E, * pNew2T;
	reo_unit * pNew1ER, * pNew2ER;
	// the old linked lists
	reo_unit * pListOld0 = p->pPlanes[lev0].pHead;
	reo_unit * pListOld1 = p->pPlanes[lev1].pHead;
	// working planes and one more temporary plane
	reo_unit * pListNew0 = NULL, ** ppListNew0 = &pListNew0;
	reo_unit * pListNew1 = NULL, ** ppListNew1 = &pListNew1; 
	reo_unit * pListTemp = NULL, ** ppListTemp = &pListTemp; 
	// various integer variables
	int fComp, fCompT, fFound, nWidthCofs, HKey, fInteract, temp, c;
	// statistical variables
	int nNodesUpMovedDown  = 0;
	int nNodesDownMovedUp  = 0;
	int nNodesUnrefRemoved = 0;
	int nNodesUnrefAdded   = 0;
	int nWidthReduction    = 0;
	double AplWeightTotalLev0;
	double AplWeightTotalLev1;
	double AplWeightHalf;
	double AplWeightPrev;
	double AplWeightAfter;
	double nCostGain;     

	// set the old lists
	assert( lev0 >= 0 && lev1 < p->nSupp );
	pListOld0 = p->pPlanes[lev0].pHead;
	pListOld1 = p->pPlanes[lev1].pHead;

	// make sure the planes have nodes
	assert( p->pPlanes[lev0].statsNodes && p->pPlanes[lev1].statsNodes );
	assert( pListOld0 && pListOld1 );

	if ( p->fMinWidth )
	{
		// verify that the width parameters are set correctly
		reoProfileWidthVerifyLevel( p->pPlanes + lev0, lev0 );
		reoProfileWidthVerifyLevel( p->pPlanes + lev1, lev1 );
		// start the storage for cofactors
		nWidthCofs = 0;
	}
	else if ( p->fMinApl )
	{
		AplWeightPrev      = p->nAplCur;
		AplWeightAfter     = p->nAplCur;
		AplWeightTotalLev0 = 0.0;
		AplWeightTotalLev1 = 0.0;
	}

	// check if the planes interact
	fInteract = 0; // assume that they do not interact
	for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next )
	{
		if ( pUnit->pT->lev == lev1 || Unit_Regular(pUnit->pE)->lev == lev1 )
		{
			fInteract = 1;
			break;
		}
		// change the level now, this is done for efficiency reasons
		pUnit->lev = lev1;
	}

	// set the new signature for hashing
	p->nSwaps++;
	if ( !fInteract )
//	if ( 0 )
	{
		// perform the swap without interaction
		p->nNISwaps++;

		// change the levels
		if ( p->fMinWidth )
		{
			// go through the current lower level, which will become upper
			for ( pUnit = pListOld1; pUnit; pUnit = pUnit->Next )
			{
				pUnit->lev = lev0;

				pUnitER = Unit_Regular(pUnit->pE);
				if ( pUnitER->TopRef > lev0 )
				{
					if ( pUnitER->Sign != p->nSwaps )
					{
						if ( pUnitER->TopRef == lev2 )
						{
							pUnitER->TopRef = lev1;
							nWidthReduction--;
						}
						else
						{
							assert( pUnitER->TopRef == lev1 );
						}
						pUnitER->Sign   = p->nSwaps;
					}
				}

				pUnitT  = pUnit->pT;
				if ( pUnitT->TopRef > lev0 )
				{
					if ( pUnitT->Sign != p->nSwaps )
					{
						if ( pUnitT->TopRef == lev2 )
						{
							pUnitT->TopRef = lev1;
							nWidthReduction--;
						}
						else
						{
							assert( pUnitT->TopRef == lev1 );
						}
						pUnitT->Sign   = p->nSwaps;
					}
				}

			}

			// go through the current upper level, which will become lower
			for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next )
			{
				pUnit->lev = lev1;

				pUnitER = Unit_Regular(pUnit->pE);
				if ( pUnitER->TopRef > lev0 )
				{
					if ( pUnitER->Sign != p->nSwaps )
					{
						assert( pUnitER->TopRef == lev1 );
						pUnitER->TopRef = lev2;
						pUnitER->Sign   = p->nSwaps;
						nWidthReduction++;
					}
				}

				pUnitT  = pUnit->pT;
				if ( pUnitT->TopRef > lev0 )
				{
					if ( pUnitT->Sign != p->nSwaps )
					{
						assert( pUnitT->TopRef == lev1 );
						pUnitT->TopRef = lev2;
						pUnitT->Sign   = p->nSwaps;
						nWidthReduction++;
					}
				}
			}
		}
		else
		{
//			for ( pUnit = pListOld0; pUnit; pUnit = pUnit->Next )
//				pUnit->lev = lev1;
			for ( pUnit = pListOld1; pUnit; pUnit = pUnit->Next )
				pUnit->lev = lev0;
		}

		// set the new linked lists, which will be attached to the planes
		pListNew0 = pListOld1;
		pListNew1 = pListOld0;

		if ( p->fMinApl )
		{
			AplWeightTotalLev0 = p->pPlanes[lev1].statsCost;
			AplWeightTotalLev1 = p->pPlanes[lev0].statsCost;
		}
		
		// set the changes in terms of nodes
		nNodesUpMovedDown = p->pPlanes[lev0].statsNodes; 
		nNodesDownMovedUp = p->pPlanes[lev1].statsNodes; 
		goto finish;
	}
	p->Signature++;


	// two-variable swap is done in three easy steps
	// previously I thought that steps (1) and (2) can be merged into one step
	// now it is clear that this cannot be done without changing a lot of other stuff...

	// (1) walk through the upper level, find units without cofactors in the lower level 
	//     and move them to the new lower level (while adding to the cache)
	// (2) walk through the uppoer level, and tranform all the remaning nodes 
	//     while employing cache for the new lower level
	// (3) walk through the old lower level, find those nodes whose ref counters are not zero, 
	//     and move them to the new uppoer level, free other nodes

	// (1) walk through the upper level, find units without cofactors in the lower level 
	//     and move them to the new lower level (while adding to the cache)
	for ( pLoop = pListOld0; pLoop; )
	{
		pUnit = pLoop;
		pLoop = pLoop->Next;

		pUnitE  = pUnit->pE;
		pUnitER = Unit_Regular(pUnitE);
		pUnitT  = pUnit->pT;

		if ( pUnitER->lev != lev1 && pUnitT->lev != lev1 )
		{
			//                before                        after
			//
			//                 <p1>           
			//              0 /    \ 1         
			//               /      \          
			//              /        \         
			//             /          \                     <p2n>   
			//            /            \                  0 /  \ 1 
			//           /              \                  /    \  
			//          /                \                /      \ 
			//         F0                F1              F0      F1

			// move to plane-2-new
			// nothing changes in the process (cofactors, ref counter, APL weight)
			pUnit->lev = lev1;
			AddToLinkedList( ppListNew1, pUnit );
			if ( p->fMinApl )
				AplWeightTotalLev1 += pUnit->Weight;

			// add to cache - find the cell with different signature (not the current one!)
			for (  HKey = hashKey3(p->Signature, pUnitE, pUnitT, p->nTableSize);
			       p->HTable[HKey].Sign == p->Signature;
				   HKey = (HKey+1) % p->nTableSize );
			assert( p->HTable[HKey].Sign != p->Signature );
			p->HTable[HKey].Sign = p->Signature;
			p->HTable[HKey].Arg1 = pUnitE;
			p->HTable[HKey].Arg2 = pUnitT;
			p->HTable[HKey].Arg3 = pUnit;

			nNodesUpMovedDown++;

			if ( p->fMinWidth )
			{
				// update the cofactors's top ref
				if ( pUnitER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					assert( pUnitER->TopRef == lev1 );
					pUnitER->TopRefNew = lev2;
					if ( pUnitER->Sign != p->nSwaps )
					{
						pUnitER->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pUnitER;
					}
				}
				if ( pUnitT->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					assert( pUnitT->TopRef == lev1 );
					pUnitT->TopRefNew = lev2;
					if ( pUnitT->Sign != p->nSwaps )
					{
						pUnitT->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pUnitT;
					}
				}
			}
		}
		else
		{
			// add to the temporary plane
			AddToLinkedList( ppListTemp, pUnit );
		}
	}


	// (2) walk through the uppoer level, and tranform all the remaning nodes 
	//     while employing cache for the new lower level
	for ( pLoop = pListTemp; pLoop; )
	{
		pUnit = pLoop;
		pLoop = pLoop->Next;

		pUnitE  = pUnit->pE;
		pUnitER = Unit_Regular(pUnitE);
		pUnitT  = pUnit->pT;
		fComp   = (int)(pUnitER != pUnitE);

		// count the amount of weight to reduce the APL of the children of this node
		if ( p->fMinApl )
			AplWeightHalf = 0.5 * pUnit->Weight;

		// determine what situation is this
		if ( pUnitER->lev == lev1 && pUnitT->lev == lev1 )
		{
			if ( fComp == 0 )
			{
				//                before                        after
				//
				//                 <p1>                          <p1n>
				//              0 /    \ 1                    0 /    \ 1            
				//               /      \                      /      \           
				//              /        \                    /        \         
				//           <p2>        <p2>              <p2n>       <p2n>          
				//        0 /   \ 1    0 /  \ 1          0 /  \ 1    0 /   \ 1    
				//         /     \      /    \            /    \      /     \   
				//        /       \    /      \          /      \    /       \   
				//       F0       F1  F2      F3        F0      F2  F1       F3  
				//                                 pNew1E   pNew1T  pNew2E   pNew2T
				//
				pNew1E = pUnitE->pE;   // F0
				pNew1T = pUnitT->pE;   // F2

				pNew2E = pUnitE->pT;   // F1
				pNew2T = pUnitT->pT;   // F3
			}
			else
			{
				//                before                        after
				//
				//                 <p1>                          <p1n>
				//              0 .    \ 1                    0 /    \ 1            
				//               .      \                      /      \           
				//              .        \                    /        \         
				//           <p2>        <p2>              <p2n>       <p2n>          
				//        0 /   \ 1    0 /  \ 1          0 .  \ 1    0 .   \ 1    
				//         /     \      /    \            .    \      .     \   
				//        /       \    /      \          .      \    .       \   
				//       F0       F1  F2      F3        F0      F2  F1       F3  
				//                                 pNew1E   pNew1T  pNew2E   pNew2T
				//
				pNew1E = Unit_Not(pUnitER->pE);  // F0
				pNew1T =          pUnitT->pE;    // F2

				pNew2E = Unit_Not(pUnitER->pT);  // F1
				pNew2T =          pUnitT->pT;    // F3
			}
			// subtract ref counters - on the level P2
			pUnitER->n--;
			pUnitT->n--;

			// mark the change in the APL weights
			if ( p->fMinApl )
			{
				pUnitER->Weight -= AplWeightHalf;
				pUnitT->Weight  -= AplWeightHalf;
				AplWeightAfter  -= pUnit->Weight;
			}
		}
		else if ( pUnitER->lev == lev1 )
		{
			if ( fComp == 0 )
			{
				//                before                        after
				//
				//                 <p1>                          <p1n>
				//              0 /    \ 1                    0 /    \ 1            
				//               /      \                      /      \           
				//              /        \                    /        \         
				//           <p2>         \               <p2n>       <p2n>           
				//        0 /   \ 1        \            0 /  \ 1    0 /   \ 1    
				//         /     \          \            /    \      /     \   
				//        /       \          \          /      \    /       \   
				//       F0       F1         F3        F0      F3  F1       F3  
				//                                 pNew1E   pNew1T  pNew2E   pNew2T
				//
				pNew1E = pUnitER->pE;	  // F0
				pNew1T = pUnitT;          // F3

				pNew2E = pUnitER->pT;     // F1
				pNew2T = pUnitT;          // F3
			}
			else
			{
				//                before                        after
				//
				//                 <p1>                          <p1n>
				//              0 .    \ 1                    0 /    \ 1            
				//               .      \                      /      \           
				//              .        \                    /        \         
				//           <p2>         \                <p2n>       <p2n>          
				//        0 /   \ 1        \            0 .  \ 1    0 .   \ 1    
				//         /     \          \            .    \      .     \   
				//        /       \          \          .      \    .       \   
				//       F0       F1         F3        F0      F3  F1       F3  
				//                                 pNew1E   pNew1T  pNew2E   pNew2T
				//
				pNew1E = Unit_Not(pUnitER->pE);  // F0
				pNew1T =          pUnitT;        // F3

				pNew2E = Unit_Not(pUnitER->pT);  // F1
				pNew2T =          pUnitT;        // F3
			}
			// subtract ref counter - on the level P2
			pUnitER->n--;
			// subtract ref counter - on other levels
			pUnitT->n--;  ///

			// mark the change in the APL weights
			if ( p->fMinApl )
			{
				pUnitER->Weight -= AplWeightHalf;
				AplWeightAfter  -= AplWeightHalf;
			}
		}
		else if ( pUnitT->lev == lev1 )
		{
			//                before                        after
			//
			//                 <p1>                          <p1n>
			//              0 /    \ 1                    0 /    \ 1            
			//               /      \                      /      \           
			//              /        \                    /        \         
			//             /         <p2>              <p2n>       <p2n>            
			//            /       0 /   \ 1          0 /  \ 1    0 /   \ 1    
			//           /         /     \            /    \      /     \   
			//          /         /       \          /      \    /       \   
			//         F0        F2       F3        F0      F2  F0       F3  
			//                                 pNew1E   pNew1T  pNew2E   pNew2T
			//
			pNew1E =     pUnitE;    // F0
			pNew1T = pUnitT->pE;    // F2

			pNew2E =     pUnitE;    // F0
			pNew2T = pUnitT->pT;    // F3

			// subtract incoming edge counter - on the level P2
			pUnitT->n--;
			// subtract ref counter - on other levels
			pUnitER->n--;  ///

			// mark the change in the APL weights
			if ( p->fMinApl )
			{
				pUnitT->Weight  -= AplWeightHalf;
				AplWeightAfter  -= AplWeightHalf;
			}
		}
		else 
		{
			assert( 0 ); // should never happen
		}


		// consider all the cases except the last one
		if ( pNew1E == pNew1T )
		{
			pNewPlane20 = pNew1T;
			
			if ( p->fMinWidth )
			{
				// update the cofactors's top ref
				if ( pNew1T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					pNew1T->TopRefNew = lev1;
					if ( pNew1T->Sign  != p->nSwaps )
					{
						pNew1T->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pNew1T;
					}
				}
			}
		}
		else
		{
			// pNew1T can be complemented
			fCompT = Cudd_IsComplement(pNew1T);
			if ( fCompT )
			{
				pNew1E = Unit_Not(pNew1E);
				pNew1T = Unit_Not(pNew1T);
			}

			// check the hash-table 
			fFound = 0;
			for (  HKey = hashKey3(p->Signature, pNew1E, pNew1T, p->nTableSize);
			       p->HTable[HKey].Sign == p->Signature;
				   HKey = (HKey+1) % p->nTableSize )
			if ( p->HTable[HKey].Arg1 == pNew1E && p->HTable[HKey].Arg2 == pNew1T )
			{ // the entry is present 
				// assign this entry
				pNewPlane20 = p->HTable[HKey].Arg3;
				assert( pNewPlane20->lev == lev1 );
				fFound = 1;
				p->HashSuccess++;
				break;
			}

			if ( !fFound )
			{ // create the new entry
				pNewPlane20 = reoUnitsGetNextUnit( p ); // increments the unit counter
				pNewPlane20->pE  = pNew1E;
				pNewPlane20->pT  = pNew1T;
				pNewPlane20->n   = 0;       // ref will be added later
				pNewPlane20->lev = lev1; 
				if ( p->fMinWidth )
				{
					pNewPlane20->TopRef = lev1;
					pNewPlane20->Sign   = 0;
				}
				// set the weight of this node
				if ( p->fMinApl )
					pNewPlane20->Weight = 0.0;

				// increment ref counters of children
				pNew1ER = Unit_Regular(pNew1E);
				pNew1ER->n++;  //
				pNew1T->n++;   //

				// insert into the data structure
				AddToLinkedList( ppListNew1, pNewPlane20 );

				// add this entry to cache
				assert( p->HTable[HKey].Sign != p->Signature );
				p->HTable[HKey].Sign = p->Signature;
				p->HTable[HKey].Arg1 = pNew1E;
				p->HTable[HKey].Arg2 = pNew1T;
				p->HTable[HKey].Arg3 = pNewPlane20;

				nNodesUnrefAdded++;
						
				if ( p->fMinWidth )
				{
					// update the cofactors's top ref
					if ( pNew1ER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
					{
						if ( pNew1ER->Sign != p->nSwaps )
						{
							pNew1ER->TopRefNew = lev2;
							if ( pNew1ER->Sign != p->nSwaps )
							{
								pNew1ER->Sign      = p->nSwaps;  // set the current signature
								p->pWidthCofs[ nWidthCofs++ ] = pNew1ER;
							}
						}
						// otherwise the level is already set correctly
						else
						{
							assert( pNew1ER->TopRefNew == lev1 || pNew1ER->TopRefNew == lev2 );
						}
					}
					// update the cofactors's top ref
					if ( pNew1T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
					{
						if ( pNew1T->Sign != p->nSwaps )
						{
							pNew1T->TopRefNew = lev2;
							if ( pNew1T->Sign  != p->nSwaps )
							{
								pNew1T->Sign      = p->nSwaps;  // set the current signature
								p->pWidthCofs[ nWidthCofs++ ] = pNew1T;
							}
						}
						// otherwise the level is already set correctly
						else
						{
							assert( pNew1T->TopRefNew == lev1 || pNew1T->TopRefNew == lev2 );
						}
					}
				}
			}

			if ( p->fMinApl )
			{
				// increment the weight of this node
				pNewPlane20->Weight += AplWeightHalf;
				// mark the change in the APL weight
				AplWeightAfter      += AplWeightHalf;
				// update the total weight of this level
				AplWeightTotalLev1  += AplWeightHalf;
			}

			if ( fCompT )
				pNewPlane20 = Unit_Not(pNewPlane20);
		}

		if ( pNew2E == pNew2T )
		{
			pNewPlane21 = pNew2T;
			
			if ( p->fMinWidth )
			{
				// update the cofactors's top ref
				if ( pNew2T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					pNew2T->TopRefNew = lev1;
					if ( pNew2T->Sign != p->nSwaps )
					{
						pNew2T->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pNew2T;
					}
				}
			}
		}
		else
		{
			assert( !Cudd_IsComplement(pNew2T) );

			// check the hash-table
			fFound = 0;
			for (  HKey = hashKey3(p->Signature, pNew2E, pNew2T, p->nTableSize);
				   p->HTable[HKey].Sign == p->Signature;
				   HKey = (HKey+1) % p->nTableSize )
			if ( p->HTable[HKey].Arg1 == pNew2E && p->HTable[HKey].Arg2 == pNew2T )
			{ // the entry is present 
				// assign this entry
				pNewPlane21 = p->HTable[HKey].Arg3;
				assert( pNewPlane21->lev == lev1 );
				fFound = 1;
				p->HashSuccess++;
				break;
			}

			if ( !fFound )
			{ // create the new entry
				pNewPlane21 = reoUnitsGetNextUnit( p ); // increments the unit counter
				pNewPlane21->pE  = pNew2E;
				pNewPlane21->pT  = pNew2T;
				pNewPlane21->n   = 0;       // ref will be added later
				pNewPlane21->lev = lev1; 
				if ( p->fMinWidth )
				{
					pNewPlane21->TopRef = lev1;
					pNewPlane21->Sign   = 0;
				}
				// set the weight of this node
				if ( p->fMinApl )
					pNewPlane21->Weight = 0.0;

				// increment ref counters of children
				pNew2ER = Unit_Regular(pNew2E);
				pNew2ER->n++; //
				pNew2T->n++;  //

				// insert into the data structure
//				reoUnitsAddUnitToPlane( &P2new, pNewPlane21 );
				AddToLinkedList( ppListNew1, pNewPlane21 );

				// add this entry to cache
				assert( p->HTable[HKey].Sign != p->Signature );
				p->HTable[HKey].Sign = p->Signature;
				p->HTable[HKey].Arg1 = pNew2E;
				p->HTable[HKey].Arg2 = pNew2T;
				p->HTable[HKey].Arg3 = pNewPlane21;

				nNodesUnrefAdded++;

						
				if ( p->fMinWidth )
				{
					// update the cofactors's top ref
					if ( pNew2ER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
					{
						if ( pNew2ER->Sign != p->nSwaps )
						{
							pNew2ER->TopRefNew = lev2;
							if ( pNew2ER->Sign != p->nSwaps )
							{
								pNew2ER->Sign      = p->nSwaps;  // set the current signature
								p->pWidthCofs[ nWidthCofs++ ] = pNew2ER;
							}
						}
						// otherwise the level is already set correctly
						else
						{
							assert( pNew2ER->TopRefNew == lev1 || pNew2ER->TopRefNew == lev2 );
						}
					}
					// update the cofactors's top ref
					if ( pNew2T->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
					{
						if ( pNew2T->Sign != p->nSwaps )
						{
							pNew2T->TopRefNew = lev2;
							if ( pNew2T->Sign != p->nSwaps )
							{
								pNew2T->Sign      = p->nSwaps;  // set the current signature
								p->pWidthCofs[ nWidthCofs++ ] = pNew2T;
							}
						}
						// otherwise the level is already set correctly
						else
						{
							assert( pNew2T->TopRefNew == lev1 || pNew2T->TopRefNew == lev2 );
						}
					}
				}
			}

			if ( p->fMinApl )
			{
				// increment the weight of this node
				pNewPlane21->Weight += AplWeightHalf;
				// mark the change in the APL weight
				AplWeightAfter      += AplWeightHalf;
				// update the total weight of this level
				AplWeightTotalLev1  += AplWeightHalf;
			}
		}
		// in all cases, the node will be added to the plane-1
		// this should be the same node (pUnit) as was originally there
		// because it is referenced by the above nodes

		assert( !Cudd_IsComplement(pNewPlane21) );
		// should be the case; otherwise reordering is not a local operation

		pUnit->pE  = pNewPlane20;
		pUnit->pT  = pNewPlane21;
		assert( pUnit->lev == lev0 ); 
		// reference counter remains the same; the APL weight remains the same

		// increment ref counters of children
		pNewPlane20R = Unit_Regular(pNewPlane20);
		pNewPlane20R->n++; ///
		pNewPlane21->n++;  ///

		// insert into the data structure
		AddToLinkedList( ppListNew0, pUnit );
		if ( p->fMinApl )
			AplWeightTotalLev0 += pUnit->Weight;
	}

	// (3) walk through the old lower level, find those nodes whose ref counters are not zero, 
	//     and move them to the new uppoer level, free other nodes
	for ( pLoop = pListOld1; pLoop; )
	{
		pUnit = pLoop;
		pLoop = pLoop->Next;
		if ( pUnit->n )
		{ 
			assert( !p->fMinApl || pUnit->Weight > 0.0 );
			// the node should be added to the new level
			// no need to check the hash table
			pUnit->lev = lev0;
			AddToLinkedList( ppListNew0, pUnit );
			if ( p->fMinApl )
				AplWeightTotalLev0 += pUnit->Weight;

			nNodesDownMovedUp++;

			if ( p->fMinWidth )
			{
				pUnitER = Unit_Regular(pUnit->pE);
				pUnitT  = pUnit->pT;

				// update the cofactors's top ref
				if ( pUnitER->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					pUnitER->TopRefNew = lev1;
					if ( pUnitER->Sign != p->nSwaps )
					{
						pUnitER->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pUnitER;
					}
				}
				if ( pUnitT->TopRef > lev0 ) // the cofactor's top ref level is one of the current two levels
				{
					pUnitT->TopRefNew = lev1;
					if ( pUnitT->Sign != p->nSwaps )
					{
						pUnitT->Sign      = p->nSwaps;  // set the current signature
						p->pWidthCofs[ nWidthCofs++ ] = pUnitT;
					}
				}
			}
		}
		else
		{
			assert( !p->fMinApl || pUnit->Weight == 0.0 );
			// decrement reference counters of children
			pUnitER = Unit_Regular(pUnit->pE);
			pUnitT  = pUnit->pT;
			pUnitER->n--;  ///
			pUnitT->n--;   ///
			// the node should be thrown away
			reoUnitsRecycleUnit( p, pUnit );
			nNodesUnrefRemoved++;
		}
	}

finish:

	// attach the new levels to the planes
	p->pPlanes[lev0].pHead = pListNew0;
	p->pPlanes[lev1].pHead = pListNew1;

	// swap the sift status
	temp                     = p->pPlanes[lev0].fSifted;
	p->pPlanes[lev0].fSifted = p->pPlanes[lev1].fSifted;
	p->pPlanes[lev1].fSifted = temp;

	// swap variables in the variable map
	if ( p->pOrderInt )
	{
		temp               = p->pOrderInt[lev0];
		p->pOrderInt[lev0] = p->pOrderInt[lev1];
		p->pOrderInt[lev1] = temp;
	}

	// adjust the node profile
	p->pPlanes[lev0].statsNodes  -= (nNodesUpMovedDown - nNodesDownMovedUp);
	p->pPlanes[lev1].statsNodes  -= (nNodesDownMovedUp - nNodesUpMovedDown) + nNodesUnrefRemoved - nNodesUnrefAdded;
	p->nNodesCur                 -=  nNodesUnrefRemoved - nNodesUnrefAdded;

	// adjust the node profile on this level
	if ( p->fMinWidth )
	{
		for ( c = 0; c < nWidthCofs; c++ )
		{
			if ( p->pWidthCofs[c]->TopRefNew < p->pWidthCofs[c]->TopRef )
			{
				p->pWidthCofs[c]->TopRef = p->pWidthCofs[c]->TopRefNew;
				nWidthReduction--;
			}
			else if ( p->pWidthCofs[c]->TopRefNew > p->pWidthCofs[c]->TopRef )
			{
				p->pWidthCofs[c]->TopRef = p->pWidthCofs[c]->TopRefNew;
				nWidthReduction++;
			}
		}
		// verify that the profile is okay
		reoProfileWidthVerifyLevel( p->pPlanes + lev0, lev0 );
		reoProfileWidthVerifyLevel( p->pPlanes + lev1, lev1 );

		// compute the total gain in terms of width
		nCostGain = (nNodesDownMovedUp - nNodesUpMovedDown + nNodesUnrefRemoved - nNodesUnrefAdded) + nWidthReduction;
		// adjust the width on this level
		p->pPlanes[lev1].statsWidth -= (int)nCostGain; 
		// set the cost
		p->pPlanes[lev1].statsCost   = p->pPlanes[lev1].statsWidth;
	}
	else if ( p->fMinApl )
	{
		// compute the total gain in terms of APL
		nCostGain = AplWeightPrev - AplWeightAfter;
		// make sure that the ALP is updated correctly
//		assert( p->pPlanes[lev0].statsCost + p->pPlanes[lev1].statsCost - nCostGain ==
//			    AplWeightTotalLev0 + AplWeightTotalLev1 );			    
		// adjust the profile 
		p->pPlanes[lev0].statsApl  = AplWeightTotalLev0;
		p->pPlanes[lev1].statsApl  = AplWeightTotalLev1;
		// set the cost
		p->pPlanes[lev0].statsCost = p->pPlanes[lev0].statsApl;
		p->pPlanes[lev1].statsCost = p->pPlanes[lev1].statsApl;
	}
	else
	{
		// compute the total gain in terms of the number of nodes
		nCostGain = nNodesUnrefRemoved - nNodesUnrefAdded;
		// adjust the profile (adjusted above)
		// set the cost
		p->pPlanes[lev0].statsCost   = p->pPlanes[lev0].statsNodes;
		p->pPlanes[lev1].statsCost   = p->pPlanes[lev1].statsNodes;
	}

	return nCostGain;
}

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

