/**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"

ABC_NAMESPACE_IMPL_START


////////////////////////////////////////////////////////////////////////
///                        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 = NULL, * pNewPlane21 = NULL; // Suppress "might be used uninitialized"
        reo_unit * 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, HKey, fInteract, temp, c;
        int nWidthCofs = -1; // Suppress "might be used uninitialized"
    // statistical variables
    int nNodesUpMovedDown  = 0;
    int nNodesDownMovedUp  = 0;
    int nNodesUnrefRemoved = 0;
    int nNodesUnrefAdded   = 0;
    int nWidthReduction    = 0;
    double AplWeightTotalLev0 = 0.0; // Suppress "might be used uninitialized"
    double AplWeightTotalLev1 = 0.0; // Suppress "might be used uninitialized"
    double AplWeightHalf      = 0.0; // Suppress "might be used uninitialized"
    double AplWeightPrev      = 0.0; // Suppress "might be used uninitialized"
    double AplWeightAfter     = 0.0; // Suppress "might be used uninitialized"
    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                              ///
////////////////////////////////////////////////////////////////////////

ABC_NAMESPACE_IMPL_END

