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

  FileName    [reoSift.c]

  PackageName [REO: A specialized DD reordering engine.]

  Synopsis    [Implementation of the sifting algorihtm.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "reo.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Implements the variable sifting algorithm.]

  Description [Performs a sequence of adjacent variable swaps known as "sifting".
  Uses the cost functions determined by the flag.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
void reoReorderSift( reo_man * p )
{
    double CostCurrent;  // the cost of the current permutation
    double CostLimit;    // the maximum increase in cost that can be tolerated
    double CostBest;     // the best cost
    int BestQ;           // the best position
    int VarCurrent;      // the current variable to move   
    int q;               // denotes the current position of the variable
    int c;               // performs the loops over variables until all of them are sifted
    int v;               // used for other purposes

    assert( p->nSupp > 0 );

    // set the current cost depending on the minimization criteria
    if ( p->fMinWidth )
        CostCurrent = p->nWidthCur;
    else if ( p->fMinApl )
        CostCurrent = p->nAplCur;
    else
        CostCurrent = p->nNodesCur;

    // find the upper bound on tbe cost growth
    CostLimit = 1 + (int)(REO_REORDER_LIMIT * CostCurrent);

    // perform sifting for each of p->nSupp variables
    for ( c = 0; c < p->nSupp; c++ )
    {
        // select the current variable to be the one with the largest number of nodes that is not sifted yet
        VarCurrent = -1;
        CostBest   = -1.0;
        for ( v = 0; v < p->nSupp; v++ )
        {
            p->pVarCosts[v] = REO_HIGH_VALUE;
            if ( !p->pPlanes[v].fSifted )
            {
//                VarCurrent = v;
//                if ( CostBest < p->pPlanes[v].statsCost )
                if ( CostBest < p->pPlanes[v].statsNodes )
                {
//                    CostBest   = p->pPlanes[v].statsCost;
                    CostBest   = p->pPlanes[v].statsNodes;
                    VarCurrent = v;
                }

            }
        }
        assert( VarCurrent != -1 );
        // mark this variable as sifted
        p->pPlanes[VarCurrent].fSifted = 1;

        // set the current value
        p->pVarCosts[VarCurrent] = CostCurrent;

        // set the best cost
        CostBest = CostCurrent;
        BestQ    = VarCurrent; 

        // determine which way to move the variable first (up or down)
        // the rationale is that if we move the shorter way first
        // it is more likely that the best position will be found on the longer way
        // and the reverse movement (to take the best position) will be faster
        if ( VarCurrent < p->nSupp/2 ) // move up first, then down
        {
            // set the total cost on all levels above the current level
            p->pPlanes[0].statsCostAbove = 0;
            for ( v = 1; v <= VarCurrent; v++ )
                p->pPlanes[v].statsCostAbove = p->pPlanes[v-1].statsCostAbove + p->pPlanes[v-1].statsCost;
            // set the total cost on all levels below the current level
            p->pPlanes[p->nSupp].statsCostBelow = 0;
            for ( v = p->nSupp - 1; v >= VarCurrent; v-- )
                p->pPlanes[v].statsCostBelow = p->pPlanes[v+1].statsCostBelow + p->pPlanes[v+1].statsCost;

            assert( CostCurrent == p->pPlanes[VarCurrent].statsCostAbove + 
                                    p->pPlanes[VarCurrent].statsCost +
                                    p->pPlanes[VarCurrent].statsCostBelow );

            // move up
            for ( q = VarCurrent-1; q >= 0; q-- )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q, 1 );
                // now q points to the position of this var in the order
                p->pVarCosts[q] = CostCurrent;
                // update the lower bound (assuming that for level q+1 it is set correctly)
                p->pPlanes[q].statsCostBelow = p->pPlanes[q+1].statsCostBelow + p->pPlanes[q+1].statsCost;
                // check the upper bound
                if ( CostCurrent >= CostLimit )
                    break;
                // check the lower bound
                if ( p->pPlanes[q].statsCostBelow + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostAbove/REO_QUAL_PAR >= CostBest )
                    break;
                // update the best cost
                if ( CostBest > CostCurrent )
                {
                    CostBest = CostCurrent;
                    BestQ    = q;
                    // adjust node limit
                    CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) );
                }

                // when we are reordering for width or APL, it may happen that
                // the number of nodes has grown above certain limit,
                // in which case we have to resize the data structures
                if ( p->fMinWidth || p->fMinApl )
                {
                    if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc )
                    {
//                        printf( "Resizing data structures. Old size = %6d. New size = %6d.\n",  p->nNodesMaxAlloc, p->nNodesCur );
                        reoResizeStructures( p, 0, p->nNodesCur, 0 );
                    }
                }
            }
            // fix the plane index
            if ( q == -1 )
                q++;
            // now p points to the position of this var in the order

            // move down
            for ( ; q < p->nSupp-1; )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 );
                q++;    // change q to point to the position of this var in the order
                // sanity check: the number of nodes on the back pass should be the same
                if ( p->pVarCosts[q] != REO_HIGH_VALUE && fabs( p->pVarCosts[q] - CostCurrent ) > REO_COST_EPSILON )
                    printf("reoReorderSift(): Error! On the backward move, the costs are different.\n");
                p->pVarCosts[q] = CostCurrent;
                // update the lower bound (assuming that for level q-1 it is set correctly)
                p->pPlanes[q].statsCostAbove = p->pPlanes[q-1].statsCostAbove + p->pPlanes[q-1].statsCost;
                // check the bounds only if the variable already reached its previous position
                if ( q >= BestQ )
                {
                    // check the upper bound
                    if ( CostCurrent >= CostLimit )
                        break;
                    // check the lower bound
                    if ( p->pPlanes[q].statsCostAbove + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostBelow/REO_QUAL_PAR >= CostBest )
                        break;
                }
                // update the best cost
                if ( CostBest >= CostCurrent )
                {
                    CostBest = CostCurrent;
                    BestQ    = q;
                    // adjust node limit
                    CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) );
                }

                // when we are reordering for width or APL, it may happen that
                // the number of nodes has grown above certain limit,
                // in which case we have to resize the data structures
                if ( p->fMinWidth || p->fMinApl )
                {
                    if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc )
                    {
//                        printf( "Resizing data structures. Old size = %6d. New size = %6d.\n",  p->nNodesMaxAlloc, p->nNodesCur );
                        reoResizeStructures( p, 0, p->nNodesCur, 0 );
                    }
                }
            }
            // move the variable up from the given position (q) to the best position (BestQ)
            assert( q >= BestQ );
            for ( ; q > BestQ; q-- )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q-1, 1 );
                // sanity check: the number of nodes on the back pass should be the same
                if ( fabs( p->pVarCosts[q-1] - CostCurrent ) > REO_COST_EPSILON )
                {
                    printf("reoReorderSift():  Error! On the return move, the costs are different.\n" );
                    fflush(stdout);
                }
            }
        }
        else // move down first, then up
        {
            // set the current number of nodes on all levels above the given level
            p->pPlanes[0].statsCostAbove = 0;
            for ( v = 1; v <= VarCurrent; v++ )
                p->pPlanes[v].statsCostAbove = p->pPlanes[v-1].statsCostAbove + p->pPlanes[v-1].statsCost;
            // set the current number of nodes on all levels below the given level
            p->pPlanes[p->nSupp].statsCostBelow = 0;
            for ( v = p->nSupp - 1; v >= VarCurrent; v-- )
                p->pPlanes[v].statsCostBelow = p->pPlanes[v+1].statsCostBelow + p->pPlanes[v+1].statsCost;
            
            assert( CostCurrent == p->pPlanes[VarCurrent].statsCostAbove + 
                                    p->pPlanes[VarCurrent].statsCost +
                                    p->pPlanes[VarCurrent].statsCostBelow );

            // move down
            for ( q = VarCurrent; q < p->nSupp-1; )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 );
                q++;    // change q to point to the position of this var in the order
                p->pVarCosts[q] = CostCurrent;
                // update the lower bound (assuming that for level q-1 it is set correctly)
                p->pPlanes[q].statsCostAbove = p->pPlanes[q-1].statsCostAbove + p->pPlanes[q-1].statsCost;
                // check the upper bound
                if ( CostCurrent >= CostLimit )
                    break;
                // check the lower bound
                if ( p->pPlanes[q].statsCostAbove + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostBelow/REO_QUAL_PAR >= CostBest )
                    break;
                // update the best cost
                if ( CostBest > CostCurrent )
                {
                    CostBest = CostCurrent;
                    BestQ    = q;
                    // adjust node limit
                    CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) );
                }

                // when we are reordering for width or APL, it may happen that
                // the number of nodes has grown above certain limit,
                // in which case we have to resize the data structures
                if ( p->fMinWidth || p->fMinApl )
                {
                    if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc )
                    {
//                        printf( "Resizing data structures. Old size = %6d. New size = %6d.\n",  p->nNodesMaxAlloc, p->nNodesCur );
                        reoResizeStructures( p, 0, p->nNodesCur, 0 );
                    }
                }
            }

            // move up
            for ( --q; q >= 0; q-- )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q, 1 );
                // now q points to the position of this var in the order
                // sanity check: the number of nodes on the back pass should be the same
                if ( p->pVarCosts[q] != REO_HIGH_VALUE && fabs( p->pVarCosts[q] - CostCurrent ) > REO_COST_EPSILON )
                    printf("reoReorderSift(): Error! On the backward move, the costs are different.\n");
                p->pVarCosts[q] = CostCurrent;
                // update the lower bound (assuming that for level q+1 it is set correctly)
                p->pPlanes[q].statsCostBelow = p->pPlanes[q+1].statsCostBelow + p->pPlanes[q+1].statsCost;
                // check the bounds only if the variable already reached its previous position
                if ( q <= BestQ )
                {
                    // check the upper bound
                    if ( CostCurrent >= CostLimit )
                        break;
                    // check the lower bound
                    if ( p->pPlanes[q].statsCostBelow + (REO_QUAL_PAR-1)*p->pPlanes[q].statsCostAbove/REO_QUAL_PAR >= CostBest )
                        break;
                }
                // update the best cost
                if ( CostBest >= CostCurrent )
                {
                    CostBest = CostCurrent;
                    BestQ    = q;
                    // adjust node limit
                    CostLimit = ddMin( CostLimit, 1 + (int)(REO_REORDER_LIMIT * CostCurrent) );
                }

                // when we are reordering for width or APL, it may happen that
                // the number of nodes has grown above certain limit,
                // in which case we have to resize the data structures
                if ( p->fMinWidth || p->fMinApl )
                {
                    if ( p->nNodesCur >= 2 * p->nNodesMaxAlloc )
                    {
//                        printf( "Resizing data structures. Old size = %6d. New size = %6d.\n",  p->nNodesMaxAlloc, p->nNodesCur );
                        reoResizeStructures( p, 0, p->nNodesCur, 0 );
                    }
                }
            }
            // fix the plane index
            if ( q == -1 )
                q++;
            // now q points to the position of this var in the order
            // move the variable down from the given position (q) to the best position (BestQ)
            assert( q <= BestQ );
            for ( ; q < BestQ; q++ )
            {
                CostCurrent -= reoReorderSwapAdjacentVars( p, q, 0 );
                // sanity check: the number of nodes on the back pass should be the same
                if ( fabs( p->pVarCosts[q+1] - CostCurrent ) > REO_COST_EPSILON )
                {
                    printf("reoReorderSift(): Error! On the return move, the costs are different.\n" );
                    fflush(stdout);
                }
            }
        }
        assert( fabs( CostBest - CostCurrent ) < REO_COST_EPSILON );

        // update the cost 
        if ( p->fMinWidth )
            p->nWidthCur = (int)CostBest;
        else if ( p->fMinApl )
            p->nAplCur = CostCurrent;
        else
            p->nNodesCur = (int)CostBest;
    }

    // remove the sifted attributes if any
    for ( v = 0; v < p->nSupp; v++ )
        p->pPlanes[v].fSifted = 0;
}

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

ABC_NAMESPACE_IMPL_END