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

  FileName    [sbdCut2.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT-based optimization using internal don't-cares.]

  Synopsis    [Cut computation.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "sbdInt.h"

ABC_NAMESPACE_IMPL_START


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

#define SBD_MAX_CUTSIZE   10
#define SBD_MAX_CUTNUM    501

#define SBD_CUT_NO_LEAF   0xF

typedef struct Sbd_Cut_t_ Sbd_Cut_t; 
struct Sbd_Cut_t_
{
    word            Sign;                      // signature
    int             iFunc;                     // functionality
    int             Cost;                      // cut cost
    int             CostLev;                   // cut cost
    unsigned        nTreeLeaves  :  9;         // tree leaves
    unsigned        nSlowLeaves  :  9;         // slow leaves
    unsigned        nTopLeaves   : 10;         // top leaves
    unsigned        nLeaves      :  4;         // leaf count
    int             pLeaves[SBD_MAX_CUTSIZE];  // leaves
};

struct Sbd_Srv_t_
{
    int             nLutSize;
    int             nCutSize;
    int             nCutNum;
    int             fVerbose;
    Gia_Man_t *     pGia;                      // user's AIG manager (will be modified by adding nodes)
    Vec_Int_t *     vMirrors;                  // mirrors for each node
    Vec_Int_t *     vLutLevs;                  // delays for each node
    Vec_Int_t *     vLevs;                     // levels for each node
    Vec_Int_t *     vRefs;                     // refs for each node
    Sbd_Cut_t       pCuts[SBD_MAX_CUTNUM];     // temporary cuts
    Sbd_Cut_t *     ppCuts[SBD_MAX_CUTNUM];    // temporary cut pointers
    abctime         clkStart;                  // starting time
    Vec_Int_t *     vCut0;                     // current cut
    Vec_Int_t *     vCut;                      // current cut
    Vec_Int_t *     vCutTop;                   // current cut
    Vec_Int_t *     vCutBot;                   // current cut
};

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Sbd_Srv_t * Sbd_ManCutServerStart( Gia_Man_t * pGia, Vec_Int_t * vMirrors, 
                                   Vec_Int_t * vLutLevs, Vec_Int_t * vLevs, Vec_Int_t * vRefs, 
                                   int nLutSize, int nCutSize, int nCutNum, int fVerbose )
{
    Sbd_Srv_t * p;
    assert( nLutSize <= nCutSize );
    assert( nCutSize < SBD_CUT_NO_LEAF );
    assert( nCutSize > 1 && nCutSize <= SBD_MAX_CUTSIZE );
    assert( nCutNum > 1 && nCutNum < SBD_MAX_CUTNUM );
    p = ABC_CALLOC( Sbd_Srv_t, 1 );
    p->clkStart = Abc_Clock();
    p->nLutSize = nLutSize;
    p->nCutSize = nCutSize;
    p->nCutNum  = nCutNum;
    p->fVerbose = fVerbose;
    p->pGia     = pGia;
    p->vMirrors = vMirrors;
    p->vLutLevs = vLutLevs;
    p->vLevs    = vLevs;
    p->vRefs    = vRefs;
    p->vCut0    = Vec_IntAlloc( 100 );
    p->vCut     = Vec_IntAlloc( 100 );
    p->vCutTop  = Vec_IntAlloc( 100 );
    p->vCutBot  = Vec_IntAlloc( 100 );
    return p;
}
void Sbd_ManCutServerStop( Sbd_Srv_t * p )
{
    Vec_IntFree( p->vCut0 );
    Vec_IntFree( p->vCut );
    Vec_IntFree( p->vCutTop );
    Vec_IntFree( p->vCutBot );
    ABC_FREE( p );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sbd_ManCutIsTopo_rec( Gia_Man_t * p, Vec_Int_t * vMirrors, int iObj )
{
    Gia_Obj_t * pObj; 
    int Ret0, Ret1;
    if ( Vec_IntEntry(vMirrors, iObj) >= 0 )
        iObj = Abc_Lit2Var(Vec_IntEntry(vMirrors, iObj));
    if ( !iObj || Gia_ObjIsTravIdCurrentId(p, iObj) )
        return 1;
    Gia_ObjSetTravIdCurrentId(p, iObj);
    pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsCi(pObj) )
        return 0;
    assert( Gia_ObjIsAnd(pObj) );
    Ret0 = Sbd_ManCutIsTopo_rec( p, vMirrors, Gia_ObjFaninId0(pObj, iObj) );
    Ret1 = Sbd_ManCutIsTopo_rec( p, vMirrors, Gia_ObjFaninId1(pObj, iObj) );
    return Ret0 && Ret1;
}
int Sbd_ManCutIsTopo( Gia_Man_t * p, Vec_Int_t * vMirrors, Vec_Int_t * vCut, int iObj )
{
    int i, Entry, RetValue;
    Gia_ManIncrementTravId( p );
    Vec_IntForEachEntry( vCut, Entry, i )
        Gia_ObjSetTravIdCurrentId( p, Entry );        
    RetValue = Sbd_ManCutIsTopo_rec( p, vMirrors, iObj );
    if ( RetValue == 0 )
        printf( "Cut of node %d is not tological\n", iObj );
    assert( RetValue );
    return RetValue;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Sbd_ManCutExpandOne( Gia_Man_t * p, Vec_Int_t * vMirrors, Vec_Int_t * vLutLevs, Vec_Int_t * vCut, int iThis, int iObj )
{
    int Lit0m, Lit1m, Fan0, Fan1, iPlace0, iPlace1;
    int LutLev = Vec_IntEntry( vLutLevs, iObj );
    Gia_Obj_t * pObj = Gia_ManObj(p, iObj);
    if ( Gia_ObjIsCi(pObj) )
        return 0;
    assert( Gia_ObjIsAnd(pObj) );
    Lit0m = Vec_IntEntry( vMirrors, Gia_ObjFaninId0(pObj, iObj) );
    Lit1m = Vec_IntEntry( vMirrors, Gia_ObjFaninId1(pObj, iObj) );
    Fan0 = Lit0m >= 0 ? Abc_Lit2Var(Lit0m) : Gia_ObjFaninId0(pObj, iObj);
    Fan1 = Lit1m >= 0 ? Abc_Lit2Var(Lit1m) : Gia_ObjFaninId1(pObj, iObj);
    iPlace0 = Vec_IntFind( vCut, Fan0 );
    iPlace1 = Vec_IntFind( vCut, Fan1 );
    if ( iPlace0 == -1 && iPlace1 == -1 )
        return 0;
    if ( Vec_IntEntry(vLutLevs, Fan0) > LutLev || Vec_IntEntry(vLutLevs, Fan1) > LutLev )
        return 0;
    Vec_IntDrop( vCut, iThis );
    if ( iPlace0 == -1 && Fan0 )
        Vec_IntPushOrder( vCut, Fan0 );
    if ( iPlace1 == -1 && Fan1 )
        Vec_IntPushOrder( vCut, Fan1 );
    return 1;
}
void Vec_IntIsOrdered( Vec_Int_t * vCut )
{
    int i, Prev, Entry;
    Prev = Vec_IntEntry( vCut, 0 );
    Vec_IntForEachEntryStart( vCut, Entry, i, 1 )
    {
        assert( Prev < Entry );
        Prev = Entry;
    }
}
void Sbd_ManCutExpand( Gia_Man_t * p, Vec_Int_t * vMirrors, Vec_Int_t * vLutLevs, Vec_Int_t * vCut )
{
    int i, Entry;
    do
    {
        Vec_IntForEachEntry( vCut, Entry, i )
            if ( Sbd_ManCutExpandOne( p, vMirrors, vLutLevs, vCut, i, Entry ) )
                break;
    } 
    while ( i < Vec_IntSize(vCut) );
}
void Sbd_ManCutReload( Vec_Int_t * vMirrors, Vec_Int_t * vLutLevs, int LevStop, Vec_Int_t * vCut, Vec_Int_t * vCutTop, Vec_Int_t * vCutBot )
{
    int i, Entry;
    Vec_IntClear( vCutTop );
    Vec_IntClear( vCutBot );
    Vec_IntForEachEntry( vCut, Entry, i )
    {
        assert( Entry );
        assert( Vec_IntEntry(vMirrors, Entry) == -1 );
        assert( Vec_IntEntry(vLutLevs, Entry) <= LevStop );
        if ( Vec_IntEntry(vLutLevs, Entry) == LevStop )
            Vec_IntPush( vCutTop, Entry );
        else
            Vec_IntPush( vCutBot, Entry );
    }
    Vec_IntIsOrdered( vCut );
}
int Sbd_ManCutCollect_rec( Gia_Man_t * p, Vec_Int_t * vMirrors, int iObj, int LevStop, Vec_Int_t * vLutLevs, Vec_Int_t * vCut )
{
    Gia_Obj_t * pObj; 
    int Ret0, Ret1;
    if ( Vec_IntEntry(vMirrors, iObj) >= 0 )
        iObj = Abc_Lit2Var(Vec_IntEntry(vMirrors, iObj));
    if ( !iObj || Gia_ObjIsTravIdCurrentId(p, iObj) )
        return 1;
    Gia_ObjSetTravIdCurrentId(p, iObj);
    pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsCi(pObj) || Vec_IntEntry(vLutLevs, iObj) <= LevStop )
    {
        Vec_IntPush( vCut, iObj );
        return Vec_IntEntry(vLutLevs, iObj) <= LevStop;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Ret0 = Sbd_ManCutCollect_rec( p, vMirrors, Gia_ObjFaninId0(pObj, iObj), LevStop, vLutLevs, vCut );
    Ret1 = Sbd_ManCutCollect_rec( p, vMirrors, Gia_ObjFaninId1(pObj, iObj), LevStop, vLutLevs, vCut );
    return Ret0 && Ret1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sbd_ManCutReduceTop( Gia_Man_t * p, Vec_Int_t * vMirrors, int iObj, Vec_Int_t * vLutLevs, Vec_Int_t * vCut, Vec_Int_t * vCutTop, int nCutSize )
{
    int i, Entry, Lit0m, Lit1m, Fan0, Fan1;
    int LevStop = Vec_IntEntry(vLutLevs, iObj) - 2;
    Vec_IntIsOrdered( vCut );
    Vec_IntForEachEntryReverse( vCutTop, Entry, i )
    {
        Gia_Obj_t * pObj = Gia_ManObj( p, Entry );
        if ( Gia_ObjIsCi(pObj) )
            continue;
        assert( Gia_ObjIsAnd(pObj) );
        assert( Vec_IntEntry(vLutLevs, Entry) == LevStop );
        Lit0m = Vec_IntEntry( vMirrors, Gia_ObjFaninId0(pObj, Entry) );
        Lit1m = Vec_IntEntry( vMirrors, Gia_ObjFaninId1(pObj, Entry) );
        Fan0 = Lit0m >= 0 ? Abc_Lit2Var(Lit0m) : Gia_ObjFaninId0(pObj, Entry);
        Fan1 = Lit1m >= 0 ? Abc_Lit2Var(Lit1m) : Gia_ObjFaninId1(pObj, Entry);
        if ( Vec_IntEntry(vLutLevs, Fan0) > LevStop || Vec_IntEntry(vLutLevs, Fan1) > LevStop )
            continue;
        assert( Vec_IntEntry(vLutLevs, Fan0) <= LevStop );
        assert( Vec_IntEntry(vLutLevs, Fan1) <= LevStop );
        if ( Vec_IntEntry(vLutLevs, Fan0) == LevStop && Vec_IntEntry(vLutLevs, Fan1) == LevStop )
            continue;
        Vec_IntRemove( vCut, Entry );
        if ( Fan0 ) Vec_IntPushUniqueOrder( vCut, Fan0 );
        if ( Fan1 ) Vec_IntPushUniqueOrder( vCut, Fan1 );
        //Sbd_ManCutIsTopo( p, vMirrors, vCut, iObj );
        return 1;
    }
    return 0;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sbd_ManCutServerFirst( Sbd_Srv_t * p, int iObj, int * pLeaves )
{
    int RetValue, LevStop = Vec_IntEntry(p->vLutLevs, iObj) - 2;

    Vec_IntClear( p->vCut );
    Gia_ManIncrementTravId( p->pGia );
    RetValue = Sbd_ManCutCollect_rec( p->pGia, p->vMirrors, iObj, LevStop, p->vLutLevs, p->vCut );
    if ( RetValue == 0 ) // cannot build delay-improving cut
        return -1;
    // check if the current cut is good
    Vec_IntSort( p->vCut, 0 );
/*
    Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
    if ( Vec_IntSize(p->vCut) <= p->nCutSize && Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
    {
        //printf( "%d ", Vec_IntSize(p->vCut) );
        memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
        return Vec_IntSize(p->vCut);
    }
*/
    // try to expand the cut
    Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
    Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
    if ( Vec_IntSize(p->vCut) <= p->nCutSize && Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
    {
        //printf( "1=(%d,%d) ", Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
        //printf( "%d ", Vec_IntSize(p->vCut) );
        memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
        return Vec_IntSize(p->vCut);
    }

    // try to reduce the topmost
    Vec_IntClear( p->vCut0 );
    Vec_IntAppend( p->vCut0, p->vCut );
    if ( Vec_IntSize(p->vCut) < p->nCutSize && Sbd_ManCutReduceTop( p->pGia, p->vMirrors, iObj, p->vLutLevs, p->vCut, p->vCutTop, p->nCutSize ) )
    {
        Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
        Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
        assert( Vec_IntSize(p->vCut) <= p->nCutSize );
        if ( Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
        {
            //printf( "%d -> %d (%d + %d)\n", Vec_IntSize(p->vCut0), Vec_IntSize(p->vCut), Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
            memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
            return Vec_IntSize(p->vCut);
        }
        // try again
        if ( Vec_IntSize(p->vCut) < p->nCutSize && Sbd_ManCutReduceTop( p->pGia, p->vMirrors, iObj, p->vLutLevs, p->vCut, p->vCutTop, p->nCutSize ) )
        {
            Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
            Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
            assert( Vec_IntSize(p->vCut) <= p->nCutSize );
            if ( Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
            {
                //printf( "* %d -> %d (%d + %d)\n", Vec_IntSize(p->vCut0), Vec_IntSize(p->vCut), Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
                memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
                return Vec_IntSize(p->vCut);
            }
            // try again
            if ( Vec_IntSize(p->vCut) < p->nCutSize && Sbd_ManCutReduceTop( p->pGia, p->vMirrors, iObj, p->vLutLevs, p->vCut, p->vCutTop, p->nCutSize ) )
            {
                Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
                Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
                assert( Vec_IntSize(p->vCut) <= p->nCutSize );
                if ( Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
                {
                    //printf( "** %d -> %d (%d + %d)\n", Vec_IntSize(p->vCut0), Vec_IntSize(p->vCut), Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
                    memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
                    return Vec_IntSize(p->vCut);
                }
                // try again
                if ( Vec_IntSize(p->vCut) < p->nCutSize && Sbd_ManCutReduceTop( p->pGia, p->vMirrors, iObj, p->vLutLevs, p->vCut, p->vCutTop, p->nCutSize ) )
                {
                    Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
                    Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
                    assert( Vec_IntSize(p->vCut) <= p->nCutSize );
                    if ( Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
                    {
                        //printf( "*** %d -> %d (%d + %d)\n", Vec_IntSize(p->vCut0), Vec_IntSize(p->vCut), Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
                        memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
                        return Vec_IntSize(p->vCut);
                    }
                }
            }
        }
    }

    // recompute the cut
    Vec_IntClear( p->vCut );
    Gia_ManIncrementTravId( p->pGia );
    RetValue = Sbd_ManCutCollect_rec( p->pGia, p->vMirrors, iObj, LevStop-1, p->vLutLevs, p->vCut );
    if ( RetValue == 0 ) // cannot build delay-improving cut
        return -1;
    // check if the current cut is good
    Vec_IntSort( p->vCut, 0 );
/*
    Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
    if ( Vec_IntSize(p->vCut) <= p->nCutSize && Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
    {
        //printf( "%d ", Vec_IntSize(p->vCut) );
        memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
        return Vec_IntSize(p->vCut);
    }
*/
    // try to expand the cut
    Sbd_ManCutExpand( p->pGia, p->vMirrors, p->vLutLevs, p->vCut );
    Sbd_ManCutReload( p->vMirrors, p->vLutLevs, LevStop, p->vCut, p->vCutTop, p->vCutBot );
    if ( Vec_IntSize(p->vCut) <= p->nCutSize && Vec_IntSize(p->vCutTop) <= p->nLutSize-1 )
    {
        //printf( "2=(%d,%d) ", Vec_IntSize(p->vCutTop), Vec_IntSize(p->vCutBot) );
        //printf( "%d ", Vec_IntSize(p->vCut) );
        memcpy( pLeaves, Vec_IntArray(p->vCut), sizeof(int) * Vec_IntSize(p->vCut) );
        return Vec_IntSize(p->vCut);
    }

    return -1;
}

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


ABC_NAMESPACE_IMPL_END

