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

  FileName    [giaSopb.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [SOP balancing for a window.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: giaSopb.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

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

#include "gia.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManHighlight_rec( Gia_Man_t * p, int iObj )
{
    Gia_Obj_t * pObj;
    if ( Gia_ObjIsTravIdCurrentId(p, iObj) )
        return;
    Gia_ObjSetTravIdCurrentId(p, iObj);
    pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsAnd(pObj) )
        Gia_ManHighlight_rec( p, Gia_ObjFaninId0(pObj, iObj) );
    if ( Gia_ObjIsAnd(pObj) )
        Gia_ManHighlight_rec( p, Gia_ObjFaninId1(pObj, iObj) );
}
void Gia_ManPrepareWin( Gia_Man_t * p, Vec_Int_t * vOuts, Vec_Int_t ** pvPis, Vec_Int_t ** pvPos, Vec_Int_t ** pvAnds, int fPoOnly )
{
    Gia_Obj_t * pObj;
    int i;
    // mark the section
    Gia_ManIncrementTravId( p );
    Gia_ManForEachCoVec( vOuts, p, pObj, i )
        Gia_ManHighlight_rec( p, Gia_ObjFaninId0p(p, pObj) );
    // mark fanins of the outside area
    Gia_ManCleanMark0( p );
    if ( fPoOnly )
    {
        Gia_ManForEachCoVec( vOuts, p, pObj, i )
            Gia_ObjFanin0(pObj)->fMark0 = 1;
    }
    else
    {
        Gia_ManForEachObj1( p, pObj, i )
        {
            if ( Gia_ObjIsCi(pObj) )
                continue;
            if ( Gia_ObjIsAnd(pObj) && !Gia_ObjIsTravIdCurrentId(p, i) )
                continue;
            Gia_ObjFanin0(pObj)->fMark0 = 1;
            if ( Gia_ObjIsAnd(pObj) )
                Gia_ObjFanin1(pObj)->fMark0 = 1;
        }
    }
    // collect pointed nodes
    *pvPis  = Vec_IntAlloc( 1000 );
    *pvPos  = Vec_IntAlloc( 1000 );
    *pvAnds = Vec_IntAlloc( 1000 );
    Gia_ManForEachObj1( p, pObj, i )
    {
        if ( !Gia_ObjIsTravIdCurrentId(p, i) )
            continue;
        if ( Gia_ObjIsCi(pObj) )
            Vec_IntPush( *pvPis, i );
        else if ( pObj->fMark0 )
            Vec_IntPush( *pvPos, i );
        if ( Gia_ObjIsAnd(pObj) )
            Vec_IntPush( *pvAnds, i );
    }
    Gia_ManCleanMark0( p );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManExtractWin( Gia_Man_t * p, Vec_Int_t * vOuts, int fPoOnly )
{
    Vec_Int_t * vPis, * vPos, * vAnds;
    Gia_Man_t * pNew;
    Gia_Obj_t * pObj;
    int i;
    Gia_ManPrepareWin( p, vOuts, &vPis, &vPos, &vAnds, fPoOnly );
    // create AIG
    pNew = Gia_ManStart( Vec_IntSize(vPis) + Vec_IntSize(vPos) + Vec_IntSize(vAnds) + 1 );
    pNew->pName = Abc_UtilStrsav( p->pName );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachObjVec( vPis, p, pObj, i )
        pObj->Value = Gia_ManAppendCi( pNew );
    Gia_ManForEachObjVec( vAnds, p, pObj, i )
        pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
    Gia_ManForEachObjVec( vPos, p, pObj, i )
        Gia_ManAppendCo( pNew, pObj->Value );
    Vec_IntFree( vPis );
    Vec_IntFree( vPos );
    Vec_IntFree( vAnds );
    return pNew;
}
Gia_Man_t * Gia_ManInsertWin( Gia_Man_t * p, Vec_Int_t * vOuts, Gia_Man_t * pWin )
{
    Vec_Int_t * vPos, * vPis, * vAnds;
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj;
    int i;
    Gia_ManPrepareWin( p, vOuts, &vPis, &vPos, &vAnds, 0 );
    // create AIG
    pNew = Gia_ManStart( Gia_ManObjNum(p) - Vec_IntSize(vAnds) + Gia_ManAndNum(pWin) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    // inputs
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi( pNew );
    Gia_ManConst0(pWin)->Value = 0;
    Gia_ManForEachCi( pWin, pObj, i )
        pObj->Value = Gia_ManObj(p, Vec_IntEntry(vPis, i))->Value;
    // internal nodes
    Gia_ManHashAlloc( pNew );
    Gia_ManForEachAnd( pWin, pObj, i )
        pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
    Gia_ManForEachCo( pWin, pObj, i )
        Gia_ManObj( p, Vec_IntEntry(vPos, i) )->Value = Gia_ObjFanin0Copy(pObj);
    Gia_ManForEachAnd( p, pObj, i )
        if ( !Gia_ObjIsTravIdCurrentId(p, i) )
            pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Gia_ManHashStop( pNew );
    // cleanup
    Vec_IntFree( vPis );
    Vec_IntFree( vPos );
    Vec_IntFree( vAnds );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Gia_ManFindLatest( Gia_Man_t * p, int LevelMax, int nTimeWindow )
{
    Gia_Obj_t * pObj;
    Vec_Int_t * vOuts;
    vOuts = Vec_IntAlloc( 1000 );
    if ( Gia_ManHasMapping(p) )
    {
        int i, k, iFan, nLevels = 0;
        int * pLevels = ABC_CALLOC( int, Gia_ManObjNum(p) );
        Gia_ManForEachLut( p, i )
        {
            Gia_LutForEachFanin( p, i, iFan, k )
                pLevels[i] = Abc_MaxInt( pLevels[i], pLevels[iFan] );
            pLevels[i]++;
            nLevels = Abc_MaxInt( nLevels, pLevels[i] );
        }
        if ( nTimeWindow )
            LevelMax = (int)((1.0 - 0.01 * nTimeWindow) * nLevels);
        if ( nLevels < LevelMax )
            printf( "The maximum mapped level (%d) is less than the target level (%d).\n", nLevels, LevelMax );
        Gia_ManForEachCo( p, pObj, i )
            if ( pLevels[Gia_ObjFaninId0p(p, pObj)] >= LevelMax )
                Vec_IntPush( vOuts, i );
        ABC_FREE( pLevels );
    }
    else
    {
        int i, nLevels = Gia_ManLevelNum( p );
        if ( nTimeWindow )
            LevelMax = (int)((1.0 - 0.01 * nTimeWindow) * nLevels);
        if ( nLevels < LevelMax )
            printf( "The maximum AIG level (%d) is less than the target level (%d).\n", nLevels, LevelMax );
        Gia_ManForEachCo( p, pObj, i )
            if ( Gia_ObjLevel(p, pObj) >= LevelMax )
                Vec_IntPush( vOuts, i );
    }
    return vOuts;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManExtractWindow( Gia_Man_t * p, int LevelMax, int nTimeWindow, int fVerbose )
{
    Vec_Int_t * vOuts;
    Gia_Man_t * pWin;
    assert( !LevelMax != !nTimeWindow );
    vOuts = Gia_ManFindLatest( p, LevelMax, nTimeWindow );
    if ( fVerbose )
        printf( "Collected %d outputs to extract.\n", Vec_IntSize(vOuts) );
    if ( Vec_IntSize(vOuts) == 0 )
    {
        Vec_IntFree( vOuts );
        return Gia_ManDup( p );
    }
    pWin = Gia_ManExtractWin( p, vOuts, 1 );
    Vec_IntFree( vOuts );
    return pWin;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManPerformSopBalanceWin( Gia_Man_t * p, int LevelMax, int nTimeWindow, int nCutNum, int nRelaxRatio, int fVerbose )
{
    Vec_Int_t * vOuts;
    Gia_Man_t * pNew, * pWin, * pWinNew;
    assert( !LevelMax != !nTimeWindow );
    vOuts = Gia_ManFindLatest( p, LevelMax, nTimeWindow );
    if ( fVerbose )
        printf( "Collected %d outputs to extract.\n", Vec_IntSize(vOuts) );
    if ( Vec_IntSize(vOuts) == 0 )
    {
        Vec_IntFree( vOuts );
        return Gia_ManDup( p );
    }
    pWin = Gia_ManExtractWin( p, vOuts, 0 );
    pWinNew = Gia_ManPerformSopBalance( pWin, nCutNum, nRelaxRatio, fVerbose );
    Gia_ManStop( pWin );
    pNew = Gia_ManInsertWin( p, vOuts, pWinNew );
    Gia_ManStop( pWinNew );
    Vec_IntFree( vOuts );
    return pNew;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManPerformDsdBalanceWin( Gia_Man_t * p, int LevelMax, int nTimeWindow, int nLutSize, int nCutNum, int nRelaxRatio, int fVerbose )
{
    Vec_Int_t * vOuts;
    Gia_Man_t * pNew, * pWin, * pWinNew;
    assert( !LevelMax != !nTimeWindow );
    vOuts = Gia_ManFindLatest( p, LevelMax, nTimeWindow );
    if ( fVerbose )
        printf( "Collected %d outputs to extract.\n", Vec_IntSize(vOuts) );
    if ( Vec_IntSize(vOuts) == 0 )
    {
        Vec_IntFree( vOuts );
        return Gia_ManDup( p );
    }
    pWin = Gia_ManExtractWin( p, vOuts, 0 );
    pWinNew = Gia_ManPerformDsdBalance( pWin, nLutSize, nCutNum, nRelaxRatio, fVerbose );
    Gia_ManStop( pWin );
    pNew = Gia_ManInsertWin( p, vOuts, pWinNew );
    Gia_ManStop( pWinNew );
    Vec_IntFree( vOuts );
    return pNew;
}

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


ABC_NAMESPACE_IMPL_END

