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

  FileName    [sclUpsize.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Standard-cell library representation.]

  Synopsis    [Selective increase of gate sizes.]

  Author      [Alan Mishchenko, Niklas Een]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - August 24, 2012.]

  Revision    [$Id: sclUpsize.c,v 1.0 2012/08/24 00:00:00 alanmi Exp $]

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

#include "sclSize.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Collect TFO of nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclFindTFO_rec( Abc_Obj_t * pObj, Vec_Int_t * vNodes, Vec_Int_t * vCos )
{
    Abc_Obj_t * pNext;
    int i;
    if ( Abc_NodeIsTravIdCurrent( pObj ) )
        return;
    Abc_NodeSetTravIdCurrent( pObj );
    if ( Abc_ObjIsCo(pObj) )
    {
        Vec_IntPush( vCos, Abc_ObjId(pObj) );
        return;
    }
    assert( Abc_ObjIsNode(pObj) );
    Abc_ObjForEachFanout( pObj, pNext, i )
        Abc_SclFindTFO_rec( pNext, vNodes, vCos );
    if ( Abc_ObjFaninNum(pObj) > 0 )
        Vec_IntPush( vNodes, Abc_ObjId(pObj) );
}
Vec_Int_t * Abc_SclFindTFO( Abc_Ntk_t * p, Vec_Int_t * vPath )
{
    Vec_Int_t * vNodes, * vCos;
    Abc_Obj_t * pObj, * pFanin;
    int i, k;
    assert( Vec_IntSize(vPath) > 0 );
    vCos = Vec_IntAlloc( 100 );
    vNodes = Vec_IntAlloc( 100 );
    // collect nodes in the TFO
    Abc_NtkIncrementTravId( p ); 
    Abc_NtkForEachObjVec( vPath, p, pObj, i )
        Abc_ObjForEachFanin( pObj, pFanin, k )
            if ( Abc_ObjIsNode(pFanin) )
                Abc_SclFindTFO_rec( pFanin, vNodes, vCos );
    // reverse order
    Vec_IntReverseOrder( vNodes );
//    Vec_IntSort( vNodes, 0 );
//Vec_IntPrint( vNodes );
//Vec_IntPrint( vCos );
    Vec_IntAppend( vNodes, vCos );
    Vec_IntFree( vCos );
    return vNodes;
}

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

  Synopsis    [Collect near-critical COs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Abc_SclFindCriticalCoWindow( SC_Man * p, int Window )
{
    float fMaxArr = Abc_SclReadMaxDelay( p ) * (100.0 - Window) / 100.0;
    Vec_Int_t * vPivots;
    Abc_Obj_t * pObj;
    int i;
    vPivots = Vec_IntAlloc( 100 );
    Abc_NtkForEachCo( p->pNtk, pObj, i )
        if ( Abc_SclObjTimeMax(p, pObj) >= fMaxArr )
            Vec_IntPush( vPivots, Abc_ObjId(pObj) );
    assert( Vec_IntSize(vPivots) > 0 );
    return vPivots;
}

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

  Synopsis    [Collect near-critical internal nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclFindCriticalNodeWindow_rec( SC_Man * p, Abc_Obj_t * pObj, Vec_Int_t * vPath, float fSlack, int fDept )
{
    Abc_Obj_t * pNext;
    float fArrMax, fSlackFan;
    int i;
    if ( Abc_ObjIsCi(pObj) )
        return;
    if ( Abc_NodeIsTravIdCurrent( pObj ) )
        return;
    Abc_NodeSetTravIdCurrent( pObj );
    assert( Abc_ObjIsNode(pObj) );
    // compute the max arrival time of the fanins
    if ( fDept )
//        fArrMax = p->pSlack[Abc_ObjId(pObj)];
        fArrMax = Abc_SclObjGetSlack(p, pObj, p->MaxDelay);
    else
        fArrMax = Abc_SclGetMaxDelayNodeFanins( p, pObj );
    assert( fArrMax >= -1 );
    fArrMax = Abc_MaxFloat( fArrMax, 0 );
    // traverse all fanins whose arrival times are within a window
    Abc_ObjForEachFanin( pObj, pNext, i )
    {
        if ( Abc_ObjIsCi(pNext) || Abc_ObjFaninNum(pNext) == 0 )
            continue;
        assert( Abc_ObjIsNode(pNext) );
        if ( fDept )
//            fSlackFan = fSlack - (p->pSlack[Abc_ObjId(pNext)] - fArrMax);
            fSlackFan = fSlack - (Abc_SclObjGetSlack(p, pNext, p->MaxDelay) - fArrMax);
        else
            fSlackFan = fSlack - (fArrMax - Abc_SclObjTimeMax(p, pNext));
        if ( fSlackFan >= 0 )
            Abc_SclFindCriticalNodeWindow_rec( p, pNext, vPath, fSlackFan, fDept );
    }
    if ( Abc_ObjFaninNum(pObj) > 0 )
        Vec_IntPush( vPath, Abc_ObjId(pObj) );
}
Vec_Int_t * Abc_SclFindCriticalNodeWindow( SC_Man * p, Vec_Int_t * vPathCos, int Window, int fDept )
{
    float fMaxArr = Abc_SclReadMaxDelay( p );
    float fSlackMax = fMaxArr * Window / 100.0;
    Vec_Int_t * vPath = Vec_IntAlloc( 100 );
    Abc_Obj_t * pObj;
    int i;
    Abc_NtkIncrementTravId( p->pNtk ); 
    Abc_NtkForEachObjVec( vPathCos, p->pNtk, pObj, i )
    {
        float fSlackThis = fSlackMax - (fMaxArr - Abc_SclObjTimeMax(p, pObj));
        if ( fSlackThis >= 0 )
            Abc_SclFindCriticalNodeWindow_rec( p, Abc_ObjFanin0(pObj), vPath, fSlackThis, fDept );
    }
    // label critical nodes
    Abc_NtkForEachObjVec( vPathCos, p->pNtk, pObj, i )
        pObj->fMarkA = 1;
    Abc_NtkForEachObjVec( vPath, p->pNtk, pObj, i )
        pObj->fMarkA = 1;
    return vPath;  
}
void Abc_SclUnmarkCriticalNodeWindow( SC_Man * p, Vec_Int_t * vPath )
{
    Abc_Obj_t * pObj;
    int i;
    Abc_NtkForEachObjVec( vPath, p->pNtk, pObj, i )
        pObj->fMarkA = 0;
}
int Abc_SclCountNearCriticalNodes( SC_Man * p )
{
    int RetValue;
    Vec_Int_t * vPathPos, * vPathNodes;
    vPathPos   = Abc_SclFindCriticalCoWindow( p, 5 );
    vPathNodes = Abc_SclFindCriticalNodeWindow( p, vPathPos, 5, 0 );
    RetValue   = Vec_IntSize(vPathNodes);
    Abc_SclUnmarkCriticalNodeWindow( p, vPathNodes );
    Abc_SclUnmarkCriticalNodeWindow( p, vPathPos );
    Vec_IntFree( vPathPos );
    Vec_IntFree( vPathNodes );
    return RetValue;
}


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

  Synopsis    [Find the array of nodes to be updated.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclFindNodesToUpdate( Abc_Obj_t * pPivot, Vec_Int_t ** pvNodes, Vec_Int_t ** pvEvals, Abc_Obj_t * pExtra )
{
    Abc_Ntk_t * p = Abc_ObjNtk(pPivot);
    Abc_Obj_t * pObj, * pNext, * pNext2;
    Vec_Int_t * vNodes = *pvNodes;
    Vec_Int_t * vEvals = *pvEvals;
    int i, k;
    assert( Abc_ObjIsNode(pPivot) );
    assert( pPivot->fMarkA );
    // collect fanins, node, and fanouts
    Vec_IntClear( vNodes );
    Abc_ObjForEachFanin( pPivot, pNext, i )
//        if ( Abc_ObjIsNode(pNext) && Abc_ObjFaninNum(pNext) > 0 )
        if ( Abc_ObjIsCi(pNext) || Abc_ObjFaninNum(pNext) > 0 )
            Vec_IntPush( vNodes, Abc_ObjId(pNext) );
    Vec_IntPush( vNodes, Abc_ObjId(pPivot) );
    if ( pExtra )
        Vec_IntPush( vNodes, Abc_ObjId(pExtra) );
    Abc_ObjForEachFanout( pPivot, pNext, i )
        if ( Abc_ObjIsNode(pNext) && pNext->fMarkA )
        {
            Vec_IntPush( vNodes, Abc_ObjId(pNext) );
            Abc_ObjForEachFanout( pNext, pNext2, k )
                if ( Abc_ObjIsNode(pNext2) && pNext2->fMarkA )
                    Vec_IntPush( vNodes, Abc_ObjId(pNext2) );
        }
    Vec_IntUniqify( vNodes );
    // label nodes
    Abc_NtkForEachObjVec( vNodes, p, pObj, i )
    {
        assert( pObj->fMarkB == 0 );
        pObj->fMarkB = 1;
    }
    // collect nodes visible from the critical paths
    Vec_IntClear( vEvals );
    Abc_NtkForEachObjVec( vNodes, p, pObj, i )
        Abc_ObjForEachFanout( pObj, pNext, k )
            if ( pNext->fMarkA && !pNext->fMarkB )
//            if ( !pNext->fMarkB )
            {
                assert( pObj->fMarkB );
                Vec_IntPush( vEvals, Abc_ObjId(pObj) );
                break;
            }
    assert( Vec_IntSize(vEvals) > 0 );
    // label nodes
    Abc_NtkForEachObjVec( vNodes, p, pObj, i )
        pObj->fMarkB = 0;
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_SclFindBestCell( SC_Man * p, Abc_Obj_t * pObj, Vec_Int_t * vRecalcs, Vec_Int_t * vEvals, int Notches, int DelayGap, float * pGainBest )
{
    SC_Cell * pCellOld, * pCellNew;
    float dGain, dGainBest;
    int k, gateBest, NoChange = 0;
    // save old gate, timing, fanin load
    pCellOld = Abc_SclObjCell( pObj );
    Abc_SclConeStore( p, vRecalcs );
    Abc_SclEvalStore( p, vEvals );
    Abc_SclLoadStore( p, pObj );
    // try different gate sizes for this node
    gateBest = -1;
    dGainBest = -SC_LibTimeFromPs(p->pLib, DelayGap);
    SC_RingForEachCell( pCellOld, pCellNew, k )
    {
        if ( pCellNew == pCellOld )
            continue;
        if ( k > Notches )
            break;
        if ( p->vInDrive && !Abc_SclInputDriveOk( p, pObj, pCellNew ) )
            continue;
        // set new cell
        Abc_SclObjSetCell( pObj, pCellNew );
        Abc_SclUpdateLoad( p, pObj, pCellOld, pCellNew );
        // recompute timing
        Abc_SclTimeCone( p, vRecalcs );
        // set old cell
        Abc_SclObjSetCell( pObj, pCellOld );
        Abc_SclLoadRestore( p, pObj );
        // save best gain
        dGain = Abc_SclEvalPerform( p, vEvals );
        if ( dGainBest < dGain )
        {
            dGainBest = dGain;
            gateBest = pCellNew->Id;
            NoChange = 1;
        }
        else if ( NoChange )
            NoChange++;
        if ( NoChange == 4 )
            break;
//        printf( "%.2f ", dGain );
    }
//    printf( "Best = %.2f   ", dGainBest );
//    printf( "\n" );
    // put back old cell and timing
    Abc_SclObjSetCell( pObj, pCellOld );
    Abc_SclConeRestore( p, vRecalcs );
    *pGainBest = dGainBest;
    return gateBest;
}

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

  Synopsis    [Computes the set of gates to upsize.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_SclFindBypasses( SC_Man * p, Vec_Int_t * vPathNodes, int Ratio, int Notches, int iIter, int DelayGap, int fVeryVerbose )
{
    SC_Cell * pCellOld, * pCellNew;
    Vec_Ptr_t * vFanouts;
    Vec_Int_t * vRecalcs, * vEvals;
    Abc_Obj_t * pBuf, * pFanin, * pFanout, * pExtra = NULL;
    int i, j, iNode, gateBest, gateBest2, fanBest, Counter = 0;
    float dGainBest, dGainBest2;

    // compute savings due to bypassing buffers
    vFanouts = Vec_PtrAlloc( 100 );
    vRecalcs = Vec_IntAlloc( 100 );
    vEvals = Vec_IntAlloc( 100 );
    Vec_QueClear( p->vNodeByGain );
    Abc_NtkForEachObjVec( vPathNodes, p->pNtk, pBuf, i )
    {
        assert( pBuf->fMarkB == 0 );
        if ( Abc_ObjFaninNum(pBuf) != 1 )
            continue;
        pFanin = Abc_ObjFanin0(pBuf);
        if ( !Abc_ObjIsNode(pFanin) )
            continue;
        if ( p->pNtk->vPhases == NULL )
        {
            if ( Abc_SclIsInv(pBuf) )
            {
                if ( !Abc_SclIsInv(pFanin) )
                    continue;
                pFanin = Abc_ObjFanin0(pFanin);
                if ( !Abc_ObjIsNode(pFanin) )
                    continue;
                pExtra = pBuf;
                // we make pBuf and pFanin are in the same phase and pFanin is a node
            }
        }
        // here we have pBuf and its fanin pFanin, which is a logic node
        // compute nodes to recalculate timing and nodes to evaluate afterwards
        Abc_SclFindNodesToUpdate( pFanin, &vRecalcs, &vEvals, pExtra );
        assert( Vec_IntSize(vEvals) > 0 );
        // consider fanouts of this node
        fanBest    = -1;
        gateBest2  = -1;
        dGainBest2 =  0;
        Abc_NodeCollectFanouts( pBuf, vFanouts );
        Vec_PtrForEachEntry( Abc_Obj_t *, vFanouts, pFanout, j )
        {
            // skip COs
            if ( Abc_ObjIsCo(pFanout) )
                continue;
            // skip non-critical fanouts
            if ( !pFanout->fMarkA )
                continue;
            // skip if fanin already has fanout as a fanout
            if ( Abc_NodeFindFanin(pFanout, pFanin) >= 0 )
                continue;
            // prepare
            Abc_SclLoadStore3( p, pBuf );
            Abc_SclUpdateLoadSplit( p, pBuf, pFanout );
            Abc_ObjPatchFanin( pFanout, pBuf, pFanin );
            // size the fanin
            gateBest = Abc_SclFindBestCell( p, pFanin, vRecalcs, vEvals, Notches, DelayGap, &dGainBest );
            // unprepare
            Abc_SclLoadRestore3( p, pBuf );
            Abc_ObjPatchFanin( pFanout, pFanin, pBuf );
            if ( gateBest == -1 )
                continue;
            // compare gain
            if ( dGainBest2 < dGainBest )
            {
                dGainBest2 = dGainBest;
                gateBest2 = gateBest;
                fanBest = Abc_ObjId(pFanout);
            }
        }
        // remember savings
        if ( gateBest2 >= 0 )
        {
            assert( dGainBest2 > 0.0 );
            Vec_FltWriteEntry( p->vNode2Gain, Abc_ObjId(pBuf), dGainBest2 );
            Vec_IntWriteEntry( p->vNode2Gate, Abc_ObjId(pBuf), gateBest2 );
            Vec_QuePush( p->vNodeByGain, Abc_ObjId(pBuf) );
            Vec_IntWriteEntry( p->vBestFans, Abc_ObjId(pBuf), fanBest );
        }
//        if ( ++Counter == 17 )
//            break;
    }
    Vec_PtrFree( vFanouts );
    Vec_IntFree( vRecalcs );
    Vec_IntFree( vEvals );
    if ( Vec_QueSize(p->vNodeByGain) == 0 )
        return 0;
    if ( fVeryVerbose ) 
        printf( "\n" );

    // accept changes for that are half above the average and do not overlap
    Counter = 0;
    dGainBest2 = -1;
    vFanouts = Vec_PtrAlloc( 100 );
    while ( Vec_QueSize(p->vNodeByGain) )
    {
        iNode   = Vec_QuePop(p->vNodeByGain);
        pFanout = Abc_NtkObj( p->pNtk, Vec_IntEntry(p->vBestFans, iNode) );
        pBuf    = Abc_NtkObj( p->pNtk, iNode );
        pFanin  = Abc_ObjFanin0(pBuf);
        if ( pFanout->fMarkB || pBuf->fMarkB )
            continue;
        if ( p->pNtk->vPhases == NULL )
        {
            // update fanin
            if ( Abc_SclIsInv(pBuf) )
            {
                if ( !Abc_SclIsInv(pFanin) )
                {
                    assert( 0 );
                    continue;
                }
                pFanin = Abc_ObjFanin0(pFanin);
                if ( !Abc_ObjIsNode(pFanin) )
                {
                    assert( 0 );
                    continue;
                }
            }
        }
        if ( pFanin->fMarkB )
            continue;
        pFanout->fMarkB = 1;
        pBuf->fMarkB = 1;
        pFanin->fMarkB = 1;
        Vec_PtrPush( vFanouts, pFanout );
        Vec_PtrPush( vFanouts, pBuf );
        Vec_PtrPush( vFanouts, pFanin );
        // remember gain
        if ( dGainBest2 == -1 )
            dGainBest2 = Vec_FltEntry(p->vNode2Gain, iNode);
//        else if ( dGainBest2 > 2*Vec_FltEntry(p->vNode2Gain, iNode) )
//            break;
        // redirect
        Abc_SclUpdateLoadSplit( p, pBuf, pFanout );
        Abc_SclAddWireLoad( p, pBuf, 1 );
        Abc_SclAddWireLoad( p, pFanin, 1 );
        Abc_ObjPatchFanin( pFanout, pBuf, pFanin );
        Abc_SclAddWireLoad( p, pBuf, 0 );
        Abc_SclAddWireLoad( p, pFanin, 0 );
        Abc_SclTimeIncUpdateLevel( pFanout );
        // remember
        Vec_IntPush( p->vUpdates2, Abc_ObjId(pFanout) );
        Vec_IntPush( p->vUpdates2, Abc_ObjId(pFanin) );
        Vec_IntPush( p->vUpdates2, Abc_ObjId(pBuf) );
        // update cell
        pCellOld = Abc_SclObjCell( pFanin );
        pCellNew = SC_LibCell( p->pLib, Vec_IntEntry(p->vNode2Gate, iNode) );
        p->SumArea += pCellNew->area - pCellOld->area;
        Abc_SclObjSetCell( pFanin, pCellNew );
        Abc_SclUpdateLoad( p, pFanin, pCellOld, pCellNew );
        // record the update
        Vec_IntPush( p->vUpdates, Abc_ObjId(pFanin) );
        Vec_IntPush( p->vUpdates, pCellNew->Id );
        Abc_SclTimeIncInsert( p, pFanout );
        Abc_SclTimeIncInsert( p, pBuf );
        Abc_SclTimeIncInsert( p, pFanin );
        // remember when this node was upsized
        Vec_IntWriteEntry( p->vNodeIter, Abc_ObjId(pFanout), -1 );
        Vec_IntWriteEntry( p->vNodeIter, Abc_ObjId(pBuf), -1 );
        Vec_IntWriteEntry( p->vNodeIter, Abc_ObjId(pFanin), -1 );
        // update polarity
        if ( p->pNtk->vPhases && Abc_SclIsInv(pBuf) )
            Abc_NodeInvUpdateObjFanoutPolarity( pFanin, pFanout );
        // report
        if ( fVeryVerbose )
        {
            printf( "Node %6d  Redir fanout %6d to fanin %6d.  Gain = %7.1f ps. ", 
                Abc_ObjId(pBuf), Abc_ObjId(pFanout), Abc_ObjId(pFanin), Vec_FltEntry(p->vNode2Gain, iNode) );
            printf( "Gate %12s (%2d/%2d)  -> %12s (%2d/%2d) \n", 
                pCellOld->pName, pCellOld->Order, pCellOld->nGates, 
                pCellNew->pName, pCellNew->Order, pCellNew->nGates );
        }
/*
        // check if the node became useless
        if ( Abc_ObjFanoutNum(pBuf) == 0 )
        {
            pCellOld = Abc_SclObjCell( pBuf );
            p->SumArea -= pCellOld->area;
            Abc_NtkDeleteObj_rec( pBuf, 1 );
            printf( "Removed node %d.\n", iNode );
        }
*/
        Counter++;
    }
    Vec_PtrForEachEntry( Abc_Obj_t *, vFanouts, pFanout, j )
        pFanout->fMarkB = 0;
    Vec_PtrFree( vFanouts );
    return Counter;
}

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

  Synopsis    [Check marked fanin/fanouts.]

  Description []
               
  SideEffects []

  SeeAlso     []
 
***********************************************************************/
int Abc_SclObjCheckMarkedFanFans( Abc_Obj_t * pObj )
{
    Abc_Obj_t * pNext;
    int i;
    if ( pObj->fMarkB )
        return 1;
    Abc_ObjForEachFanin( pObj, pNext, i )
        if ( pNext->fMarkB )
            return 1;
    Abc_ObjForEachFanout( pObj, pNext, i )
        if ( pNext->fMarkB )
            return 1;
    return 0;
}
void Abc_SclObjMarkFanFans( Abc_Obj_t * pObj, Vec_Ptr_t * vNodes )
{
//    Abc_Obj_t * pNext;
//    int i;
    if ( pObj->fMarkB == 0 )
    {
        Vec_PtrPush( vNodes, pObj );
        pObj->fMarkB = 1;
    }
/*
    Abc_ObjForEachFanin( pObj, pNext, i )
        if ( pNext->fMarkB == 0 )
        {
            Vec_PtrPush( vNodes, pNext );
            pNext->fMarkB = 1;
        }
    Abc_ObjForEachFanout( pObj, pNext, i )
        if ( pNext->fMarkB == 0 )
        {
            Vec_PtrPush( vNodes, pNext );
            pNext->fMarkB = 1;
        }
*/
}

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

  Synopsis    [Computes the set of gates to upsize.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_SclFindUpsizes( SC_Man * p, Vec_Int_t * vPathNodes, int Ratio, int Notches, int iIter, int DelayGap, int fMoreConserf )
{
    SC_Cell * pCellOld, * pCellNew;
    Vec_Int_t * vRecalcs, * vEvals;
    Vec_Ptr_t * vFanouts;
    Abc_Obj_t * pObj;
    float dGainBest, dGainBest2;
    int i, gateBest, Limit, Counter, iIterLast;

    // compute savings due to upsizing each node
    vRecalcs = Vec_IntAlloc( 100 );
    vEvals = Vec_IntAlloc( 100 );
    Vec_QueClear( p->vNodeByGain );
    Abc_NtkForEachObjVec( vPathNodes, p->pNtk, pObj, i )
    {
        assert( pObj->fMarkB == 0 );
        iIterLast = Vec_IntEntry(p->vNodeIter, Abc_ObjId(pObj));
        if ( iIterLast >= 0 && iIterLast + 5 > iIter )
            continue;
        // compute nodes to recalculate timing and nodes to evaluate afterwards
        Abc_SclFindNodesToUpdate( pObj, &vRecalcs, &vEvals, NULL );
        assert( Vec_IntSize(vEvals) > 0 );
        //printf( "%d -> %d\n", Vec_IntSize(vRecalcs), Vec_IntSize(vEvals) );
        gateBest = Abc_SclFindBestCell( p, pObj, vRecalcs, vEvals, Notches, DelayGap, &dGainBest );
        // remember savings
        if ( gateBest >= 0 )
        {
            assert( dGainBest > 0.0 );
            Vec_FltWriteEntry( p->vNode2Gain, Abc_ObjId(pObj), dGainBest );
            Vec_IntWriteEntry( p->vNode2Gate, Abc_ObjId(pObj), gateBest );
            Vec_QuePush( p->vNodeByGain, Abc_ObjId(pObj) );
        }
    }
    Vec_IntFree( vRecalcs );
    Vec_IntFree( vEvals );
    if ( Vec_QueSize(p->vNodeByGain) == 0 )
        return 0;
/*
    Limit = Abc_MinInt( Vec_QueSize(p->vNodeByGain), Abc_MaxInt((int)(0.01 * Ratio * Vec_IntSize(vPathNodes)), 1) ); 
    //printf( "\nSelecting %d out of %d\n", Limit, Vec_QueSize(p->vNodeByGain) );
    for ( i = 0; i < Limit; i++ )
    {
        // get the object
        pObj = Abc_NtkObj( p->pNtk, Vec_QuePop(p->vNodeByGain) );
        assert( pObj->fMarkA );
        // find old and new gates
        pCellOld = Abc_SclObjCell( pObj );
        pCellNew = SC_LibCell( p->pLib, Vec_IntEntry(p->vNode2Gate, Abc_ObjId(pObj)) );
        assert( pCellNew != NULL );
        //printf( "%6d  %20s -> %20s  ", Abc_ObjId(pObj), pCellOld->pName, pCellNew->pName );
        //printf( "gain is %f\n", Vec_FltEntry(p->vNode2Gain, Abc_ObjId(pObj)) );
        // update gate
        Abc_SclUpdateLoad( p, pObj, pCellOld, pCellNew );
        p->SumArea += pCellNew->area - pCellOld->area;
        Abc_SclObjSetCell( pObj, pCellNew );
        // record the update
        Vec_IntPush( p->vUpdates, Abc_ObjId(pObj) );
        Vec_IntPush( p->vUpdates, pCellNew->Id );
        Abc_SclTimeIncInsert( p, pObj );
        // remember when this node was upsized
        Vec_IntWriteEntry( p->vNodeIter, Abc_ObjId(pObj), iIter );
    }
return Limit;
*/

    Limit = Abc_MinInt( Vec_QueSize(p->vNodeByGain), Abc_MaxInt((int)(0.01 * Ratio * Vec_IntSize(vPathNodes)), 1) ); 
    dGainBest2 = -1;
    Counter = 0;
    vFanouts = Vec_PtrAlloc( 100 );
    while ( Vec_QueSize(p->vNodeByGain) )
    {
        int iNode = Vec_QuePop(p->vNodeByGain);
        Abc_Obj_t * pObj = Abc_NtkObj( p->pNtk, iNode );
        assert( pObj->fMarkA );
        if ( Abc_SclObjCheckMarkedFanFans( pObj ) )
            continue;
        Abc_SclObjMarkFanFans( pObj, vFanouts );
        // remember gain
        if ( dGainBest2 == -1 )
            dGainBest2 = Vec_FltEntry(p->vNode2Gain, iNode);
//        else if ( dGainBest2 > 3*Vec_FltEntry(p->vNode2Gain, iNode) )
//            break;
//        printf( "%.1f ", Vec_FltEntry(p->vNode2Gain, iNode) );

        // find old and new gates
        pCellOld = Abc_SclObjCell( pObj );
        pCellNew = SC_LibCell( p->pLib, Vec_IntEntry(p->vNode2Gate, Abc_ObjId(pObj)) );
        assert( pCellNew != NULL );
        //printf( "%6d  %20s -> %20s  ", Abc_ObjId(pObj), pCellOld->pName, pCellNew->pName );
        //printf( "gain is %f\n", Vec_FltEntry(p->vNode2Gain, Abc_ObjId(pObj)) );
//        if ( pCellOld->Order > 0 )
//            printf( "%.2f  %d -> %d(%d)   ", Vec_FltEntry(p->vNode2Gain, iNode), pCellOld->Order, pCellNew->Order, pCellNew->nGates );
        // update gate
        p->SumArea += pCellNew->area - pCellOld->area;
        Abc_SclObjSetCell( pObj, pCellNew );
        Abc_SclUpdateLoad( p, pObj, pCellOld, pCellNew );
        // record the update
        Vec_IntPush( p->vUpdates, Abc_ObjId(pObj) );
        Vec_IntPush( p->vUpdates, pCellNew->Id );
        Abc_SclTimeIncInsert( p, pObj );
        // remember when this node was upsized
        Vec_IntWriteEntry( p->vNodeIter, Abc_ObjId(pObj), iIter );
        Counter++;
        if ( Counter == Limit )
            break;
    }
//    printf( "\n" );

    Vec_PtrForEachEntry( Abc_Obj_t *, vFanouts, pObj, i )
        pObj->fMarkB = 0;
    Vec_PtrFree( vFanouts );
    return Counter;
}
void Abc_SclApplyUpdateToBest( Vec_Int_t * vGatesBest, Vec_Int_t * vGates, Vec_Int_t * vUpdate )
{
    int i, ObjId, GateId, GateId2; 
    Vec_IntForEachEntryDouble( vUpdate, ObjId, GateId, i )
        Vec_IntWriteEntry( vGatesBest, ObjId, GateId );
    Vec_IntClear( vUpdate );
    Vec_IntForEachEntryTwo( vGatesBest, vGates, GateId, GateId2, i )
        assert( GateId == GateId2 );
//    Vec_IntClear( vGatesBest );
//    Vec_IntAppend( vGatesBest, vGates );
}
void Abc_SclUndoRecentChanges( Abc_Ntk_t * pNtk, Vec_Int_t * vTrans )
{
    int i;
    assert( Vec_IntSize(vTrans) % 3 == 0 );
    for ( i = Vec_IntSize(vTrans)/3 - 1; i >= 0; i-- )
    {
        Abc_Obj_t * pFanout = Abc_NtkObj( pNtk, Vec_IntEntry(vTrans, 3*i+0) );
        Abc_Obj_t * pFanin  = Abc_NtkObj( pNtk, Vec_IntEntry(vTrans, 3*i+1) );
        Abc_Obj_t * pObj    = Abc_NtkObj( pNtk, Vec_IntEntry(vTrans, 3*i+2) );
        // we do not update load here because times will be recomputed
        Abc_ObjPatchFanin( pFanout, pFanin, pObj );
        Abc_SclTimeIncUpdateLevel( pFanout );
//        printf( "Node %6d  Redir fanout %6d from fanin %6d. \n", 
//            Abc_ObjId(pObj), Abc_ObjId(pFanout), Abc_ObjId(pFanin) );
        // update polarity
        if ( pNtk->vPhases && Abc_SclIsInv(pObj) )
            Abc_NodeInvUpdateObjFanoutPolarity( pObj, pFanout );
    }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclUpsizePrintDiffs( SC_Man * p, SC_Lib * pLib, Abc_Ntk_t * pNtk )
{
    float fDiff = (float)0.001;
    int k;
    Abc_Obj_t * pObj;

    SC_Pair * pTimes = ABC_ALLOC( SC_Pair, p->nObjs );
    SC_Pair * pSlews = ABC_ALLOC( SC_Pair, p->nObjs );
    SC_Pair * pLoads = ABC_ALLOC( SC_Pair, p->nObjs );

    memcpy( pTimes, p->pTimes, sizeof(SC_Pair) * p->nObjs );
    memcpy( pSlews, p->pSlews, sizeof(SC_Pair) * p->nObjs );
    memcpy( pLoads, p->pLoads, sizeof(SC_Pair) * p->nObjs );

    Abc_SclTimeNtkRecompute( p, NULL, NULL, 0, 0 );

    Abc_NtkForEachNode( pNtk, pObj, k )
    {
        if ( Abc_AbsFloat(p->pLoads[k].rise - pLoads[k].rise) > fDiff )
            printf( "%6d : load rise differs %12.6f   %f %f\n", k, SC_LibCapFf(pLib, p->pLoads[k].rise)-SC_LibCapFf(pLib, pLoads[k].rise), SC_LibCapFf(pLib, p->pLoads[k].rise), SC_LibCapFf(pLib, pLoads[k].rise) );
        if ( Abc_AbsFloat(p->pLoads[k].fall - pLoads[k].fall) > fDiff )
            printf( "%6d : load fall differs %12.6f   %f %f\n", k, SC_LibCapFf(pLib, p->pLoads[k].fall)-SC_LibCapFf(pLib, pLoads[k].fall), SC_LibCapFf(pLib, p->pLoads[k].fall), SC_LibCapFf(pLib, pLoads[k].fall) );

        if ( Abc_AbsFloat(p->pSlews[k].rise - pSlews[k].rise) > fDiff )
            printf( "%6d : slew rise differs %12.6f   %f %f\n", k, SC_LibTimePs(pLib, p->pSlews[k].rise)-SC_LibTimePs(pLib, pSlews[k].rise), SC_LibTimePs(pLib, p->pSlews[k].rise), SC_LibTimePs(pLib, pSlews[k].rise) );
        if ( Abc_AbsFloat(p->pSlews[k].fall - pSlews[k].fall) > fDiff )
            printf( "%6d : slew fall differs %12.6f   %f %f\n", k, SC_LibTimePs(pLib, p->pSlews[k].fall)-SC_LibTimePs(pLib, pSlews[k].fall), SC_LibTimePs(pLib, p->pSlews[k].fall), SC_LibTimePs(pLib, pSlews[k].fall) );

        if ( Abc_AbsFloat(p->pTimes[k].rise - pTimes[k].rise) > fDiff )
            printf( "%6d : time rise differs %12.6f   %f %f\n", k, SC_LibTimePs(pLib, p->pTimes[k].rise)-SC_LibTimePs(pLib, pTimes[k].rise), SC_LibTimePs(pLib, p->pTimes[k].rise), SC_LibTimePs(pLib, pTimes[k].rise) );
        if ( Abc_AbsFloat(p->pTimes[k].fall - pTimes[k].fall) > fDiff )
            printf( "%6d : time fall differs %12.6f   %f %f\n", k, SC_LibTimePs(pLib, p->pTimes[k].fall)-SC_LibTimePs(pLib, pTimes[k].fall), SC_LibTimePs(pLib, p->pTimes[k].fall), SC_LibTimePs(pLib, pTimes[k].fall) );
    }

/*
if ( memcmp( pTimes, p->pTimes, sizeof(SC_Pair) * p->nObjs ) )
    printf( "Times differ!\n" );
if ( memcmp( pSlews, p->pSlews, sizeof(SC_Pair) * p->nObjs ) )
    printf( "Slews differ!\n" );
if ( memcmp( pLoads, p->pLoads, sizeof(SC_Pair) * p->nObjs ) )
    printf( "Loads differ!\n" );
*/

    ABC_FREE( pTimes );
    ABC_FREE( pSlews );
    ABC_FREE( pLoads );
}

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

  Synopsis    [Print cumulative statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclUpsizePrint( SC_Man * p, int Iter, int win, int nPathPos, int nPathNodes, int nUpsizes, int nTFOs, int fVerbose )
{
    printf( "%4d ",          Iter );
    printf( "Win:%3d. ",     win );
    printf( "PO:%6d. ",      nPathPos );
    printf( "Path:%7d. ",    nPathNodes );
    printf( "Gate:%5d. ",    nUpsizes );
    printf( "TFO:%7d. ",     nTFOs );
    printf( "A: " );
    printf( "%.2f ",         p->SumArea );
    printf( "(%+5.1f %%)  ", 100.0 * (p->SumArea - p->SumArea0)/ p->SumArea0 );
    printf( "D: " );
    printf( "%.2f ps ",      SC_LibTimePs(p->pLib, p->MaxDelay) );
    printf( "(%+5.1f %%)  ", 100.0 * (p->MaxDelay - p->MaxDelay0)/ p->MaxDelay0 );
    printf( "B: " );
    printf( "%.2f ps ",      SC_LibTimePs(p->pLib, p->BestDelay) );
    printf( "(%+5.1f %%)",   100.0 * (p->BestDelay - p->MaxDelay0)/ p->MaxDelay0 );
    printf( "%8.2f sec    ", 1.0*(Abc_Clock() - p->timeTotal)/(CLOCKS_PER_SEC) );
    printf( "%c", fVerbose ? '\n' : '\r' );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclUpsizeRemoveDangling( SC_Man * p, Abc_Ntk_t * pNtk )
{
    SC_Cell * pCell;
    Abc_Obj_t * pObj;
    int i;
    Abc_NtkForEachNode( pNtk, pObj, i )
        if ( Abc_ObjFanoutNum(pObj) == 0 )
        {
            pCell = Abc_SclObjCell( pObj );
            p->SumArea -= pCell->area;
            Abc_NtkDeleteObj_rec( pObj, 1 );
//            printf( "Removed node %d.\n", i );
        }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_SclUpsizePerform( SC_Lib * pLib, Abc_Ntk_t * pNtk, SC_SizePars * pPars )
{
    SC_Man * p;
    Vec_Int_t * vPathPos = NULL;    // critical POs
    Vec_Int_t * vPathNodes = NULL;  // critical nodes and PIs
    abctime clk, nRuntimeLimit = pPars->TimeOut ? pPars->TimeOut * CLOCKS_PER_SEC + Abc_Clock() : 0;
    int i = 0, win, nUpsizes = -1, nFramesNoChange = 0, nConeSize = 0;
    int nAllPos, nAllNodes, nAllTfos, nAllUpsizes;
    if ( pPars->fVerbose )
    {
        printf( "Parameters: " );
        printf( "Iters =%5d.  ",          pPars->nIters   );
        printf( "Time win =%3d %%. ",     pPars->Window   );
        printf( "Update ratio =%3d %%. ", pPars->Ratio    );
        printf( "UseDept =%2d. ",         pPars->fUseDept );
        printf( "UseWL =%2d. ",           pPars->fUseWireLoads );
        printf( "Target =%5d ps. ",       pPars->DelayUser );
        printf( "DelayGap =%3d ps. ",     pPars->DelayGap );
        printf( "Timeout =%4d sec",       pPars->TimeOut  );
        printf( "\n" );
    }
    // increase window for larger networks
    if ( pPars->Window == 1 )
        pPars->Window += (Abc_NtkNodeNum(pNtk) > 40000);
    // prepare the manager; collect init stats
    p = Abc_SclManStart( pLib, pNtk, pPars->fUseWireLoads, pPars->fUseDept, 0, pPars->BuffTreeEst );
    p->timeTotal  = Abc_Clock();
    assert( p->vGatesBest == NULL );
    p->vGatesBest = Vec_IntDup( p->pNtk->vGates );
    p->BestDelay  = p->MaxDelay0;
    // perform upsizing
    nAllPos = nAllNodes = nAllTfos = nAllUpsizes = 0;
    if ( p->BestDelay <= SC_LibTimeFromPs(p->pLib, (float)pPars->DelayUser) )
        printf( "Current delay (%.2f ps) is better than the target delay (%.2f ps).\n", SC_LibTimePs(p->pLib, p->BestDelay), (float)pPars->DelayUser );
    else
    for ( i = 0; i < pPars->nIters; i++ )
    {
        for ( win = pPars->Window + ((i % 7) == 6); win <= 100;  win *= 2 )
        {
            // detect critical path
            clk = Abc_Clock();
            vPathPos   = Abc_SclFindCriticalCoWindow( p, win );
            vPathNodes = Abc_SclFindCriticalNodeWindow( p, vPathPos, win, pPars->fUseDept );
            p->timeCone += Abc_Clock() - clk;

            // selectively upsize the nodes
            clk = Abc_Clock();
            if ( pPars->BypassFreq && i && (i % pPars->BypassFreq) == 0 )
                nUpsizes = Abc_SclFindBypasses( p, vPathNodes, pPars->Ratio, pPars->Notches, i, pPars->DelayGap, pPars->fVeryVerbose );
            else
                nUpsizes = Abc_SclFindUpsizes( p, vPathNodes, pPars->Ratio, pPars->Notches, i, pPars->DelayGap, (pPars->BypassFreq > 0) );
            p->timeSize += Abc_Clock() - clk;

            // unmark critical path
            clk = Abc_Clock();
            Abc_SclUnmarkCriticalNodeWindow( p, vPathNodes );
            Abc_SclUnmarkCriticalNodeWindow( p, vPathPos );
            p->timeCone += Abc_Clock() - clk;
            if ( nUpsizes > 0 )
                break;
            Vec_IntFree( vPathPos );
            Vec_IntFree( vPathNodes );
        }
        if ( nUpsizes == 0 )
            break;

        // update timing information
        clk = Abc_Clock();
        if ( pPars->fUseDept )
        {
            if ( Vec_IntSize(p->vChanged) && !(pPars->BypassFreq && i && (i % pPars->BypassFreq) == 0) )
                nConeSize = Abc_SclTimeIncUpdate( p );
            else
                Abc_SclTimeNtkRecompute( p, NULL, NULL, pPars->fUseDept, 0 );
        }
        else
        {
            Vec_Int_t * vTFO = Abc_SclFindTFO( p->pNtk, vPathNodes );
            Abc_SclTimeCone( p, vTFO );
            nConeSize = Vec_IntSize( vTFO );
            Vec_IntFree( vTFO );
        }
        p->timeTime += Abc_Clock() - clk;
//        Abc_SclUpsizePrintDiffs( p, pLib, pNtk );

        // save the best network
        p->MaxDelay = Abc_SclReadMaxDelay( p );
        if ( p->BestDelay > p->MaxDelay )
        {
            p->BestDelay = p->MaxDelay;
            Abc_SclApplyUpdateToBest( p->vGatesBest, p->pNtk->vGates, p->vUpdates );
            Vec_IntClear( p->vUpdates2 );
            nFramesNoChange = 0;
        }
        else
            nFramesNoChange++;

        // report and cleanup
        Abc_SclUpsizePrint( p, i, win, Vec_IntSize(vPathPos), Vec_IntSize(vPathNodes), nUpsizes, nConeSize, pPars->fVeryVerbose || (pPars->fVerbose && nFramesNoChange == 0) ); //|| (i == nIters-1) );
        nAllPos     += Vec_IntSize(vPathPos);
        nAllNodes   += Vec_IntSize(vPathNodes);
        nAllTfos    += nConeSize;
        nAllUpsizes += nUpsizes;
        Vec_IntFree( vPathPos );
        Vec_IntFree( vPathNodes );
        // check timeout
        if ( nRuntimeLimit && Abc_Clock() > nRuntimeLimit )
            break;
        // check no change
        if ( nFramesNoChange > pPars->nIterNoChange )
            break;
        // check best delay
        if ( p->BestDelay <= SC_LibTimeFromPs(p->pLib, (float)pPars->DelayUser) )
            break;
    }
    // update for best gates and recompute timing
    ABC_SWAP( Vec_Int_t *, p->vGatesBest, p->pNtk->vGates );
    if ( pPars->BypassFreq != 0 )
        Abc_SclUndoRecentChanges( p->pNtk, p->vUpdates2 );
    if ( pPars->BypassFreq != 0 )
        Abc_SclUpsizeRemoveDangling( p, pNtk );
    Abc_SclTimeNtkRecompute( p, &p->SumArea, &p->MaxDelay, 0, 0 );
    if ( pPars->fVerbose )
        Abc_SclUpsizePrint( p, i, pPars->Window, nAllPos/(i?i:1), nAllNodes/(i?i:1), nAllUpsizes/(i?i:1), nAllTfos/(i?i:1), 1 );
    else
        printf( "                                                                                                                                                  \r" );
    // report runtime
    p->timeTotal = Abc_Clock() - p->timeTotal;
    if ( pPars->fVerbose )
    {
        p->timeOther = p->timeTotal - p->timeCone - p->timeSize - p->timeTime;
        ABC_PRTP( "Runtime: Critical path", p->timeCone,  p->timeTotal );
        ABC_PRTP( "Runtime: Sizing eval  ", p->timeSize,  p->timeTotal );
        ABC_PRTP( "Runtime: Timing update", p->timeTime,  p->timeTotal );
        ABC_PRTP( "Runtime: Other        ", p->timeOther, p->timeTotal );
        ABC_PRTP( "Runtime: TOTAL        ", p->timeTotal, p->timeTotal );
    }
    if ( pPars->fDumpStats )
        Abc_SclDumpStats( p, "stats2.txt", p->timeTotal );
    if ( nRuntimeLimit && Abc_Clock() > nRuntimeLimit )
        printf( "Gate sizing timed out at %d seconds.\n", pPars->TimeOut );

    // save the result and quit
    Abc_SclSclGates2MioGates( pLib, pNtk ); // updates gate pointers
    Abc_SclManFree( p );
//    Abc_NtkCleanMarkAB( pNtk );
}

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


ABC_NAMESPACE_IMPL_END

