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

  FileName    [giaSatLE.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Mapping with edges.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "misc/extra/extra.h"
#include "misc/tim/tim.h"
#include "sat/bsat/satStore.h"

ABC_NAMESPACE_IMPL_START


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

static inline int   Sle_CutSize( int * pCut )          { return pCut[0] & 0xF;  }  //  4 bits
static inline int   Sle_CutSign( int * pCut )          { return ((unsigned)pCut[0]) >> 4;   }  // 28 bits
static inline int   Sle_CutSetSizeSign( int s, int S ) { return (S << 4) | s;   }
static inline int * Sle_CutLeaves( int * pCut )        { return pCut + 1;       } 

static inline int   Sle_CutIsUsed( int * pCut )        { return pCut[1] != 0;   }
static inline void  Sle_CutSetUnused( int * pCut )     { pCut[1] = 0;           }

static inline int   Sle_ListCutNum( int * pList )      { return pList[0];       }

#define Sle_ForEachCut( pList, pCut, i )   for ( i = 0, pCut = pList + 1; i <  pList[0]; i++, pCut += Sle_CutSize(pCut) + 1 ) // cuts with unit-cut
#define Sle_ForEachCut1( pList, pCut, i )  for ( i = 0, pCut = pList + 1; i <= pList[0]; i++, pCut += Sle_CutSize(pCut) + 1 ) // only non-unit cuts

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

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

  Synopsis    [Cut computation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Sle_CutMergeOrder( int * pCut0, int * pCut1, int * pCut, int nLutSize )
{ 
    int nSize0   = Sle_CutSize(pCut0);
    int nSize1   = Sle_CutSize(pCut1);
    int i, * pC0 = Sle_CutLeaves(pCut0);
    int k, * pC1 = Sle_CutLeaves(pCut1);
    int c, * pC  = Sle_CutLeaves(pCut);
    // the case of the largest cut sizes
    if ( nSize0 == nLutSize && nSize1 == nLutSize )
    {
        for ( i = 0; i < nSize0; i++ )
        {
            if ( pC0[i] != pC1[i] )  return 0;
            pC[i] = pC0[i];
        }
        pCut[0] = Sle_CutSetSizeSign( nLutSize, Sle_CutSign(pCut0) | Sle_CutSign(pCut1) );
        return 1;
    }
    // compare two cuts with different numbers
    i = k = c = 0;
    if ( nSize0 == 0 ) goto FlushCut1;
    if ( nSize1 == 0 ) goto FlushCut0;
    while ( 1 )
    {
        if ( c == nLutSize ) return 0;
        if ( pC0[i] < pC1[k] )
        {
            pC[c++] = pC0[i++];
            if ( i >= nSize0 ) goto FlushCut1;
        }
        else if ( pC0[i] > pC1[k] )
        {
            pC[c++] = pC1[k++];
            if ( k >= nSize1 ) goto FlushCut0;
        }
        else
        {
            pC[c++] = pC0[i++]; k++;
            if ( i >= nSize0 ) goto FlushCut1;
            if ( k >= nSize1 ) goto FlushCut0;
        }
    }

FlushCut0:
    if ( c + nSize0 > nLutSize + i ) return 0;
    while ( i < nSize0 )
        pC[c++] = pC0[i++];
    pCut[0] = Sle_CutSetSizeSign( c, Sle_CutSign(pCut0) | Sle_CutSign(pCut1) );
    return 1;

FlushCut1:
    if ( c + nSize1 > nLutSize + k ) return 0;
    while ( k < nSize1 )
        pC[c++] = pC1[k++];
    pCut[0] = Sle_CutSetSizeSign( c, Sle_CutSign(pCut0) | Sle_CutSign(pCut1) );
    return 1;
}
static inline int Sle_SetCutIsContainedOrder( int * pBase, int * pCut ) // check if pCut is contained in pBase
{
    int i, nSizeB = Sle_CutSize(pBase);
    int k, nSizeC = Sle_CutSize(pCut);
    int * pLeaveB = Sle_CutLeaves(pBase);
    int * pLeaveC = Sle_CutLeaves(pCut);
    if ( nSizeB == nSizeC )
    {
        for ( i = 0; i < nSizeB; i++ )
            if ( pLeaveB[i] != pLeaveC[i] )
                return 0;
        return 1;
    }
    assert( nSizeB > nSizeC ); 
    if ( nSizeC == 0 )
        return 1;
    for ( i = k = 0; i < nSizeB; i++ )
    {
        if ( pLeaveB[i] > pLeaveC[k] )
            return 0;
        if ( pLeaveB[i] == pLeaveC[k] )
        {
            if ( ++k == nSizeC )
                return 1;
        }
    }
    return 0;
}
static inline int Sle_CutCountBits( unsigned i )
{
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = ((i + (i >> 4)) & 0x0F0F0F0F);
    return (i*(0x01010101))>>24;
}
static inline int Sle_SetLastCutIsContained( Vec_Int_t * vTemp, int * pBase )
{
    int i, * pCut, * pList = Vec_IntArray(vTemp);
    Sle_ForEachCut( pList, pCut, i )
        if ( Sle_CutIsUsed(pCut) && Sle_CutSize(pCut) <= Sle_CutSize(pBase) && (Sle_CutSign(pCut) & Sle_CutSign(pBase)) == Sle_CutSign(pCut) && Sle_SetCutIsContainedOrder(pBase, pCut) )
            return 1;
    return 0;
}
static inline void Sle_SetAddCut( Vec_Int_t * vTemp, int * pCut )
{
    int i, * pBase, * pList = Vec_IntArray(vTemp);
    Sle_ForEachCut( pList, pBase, i )
        if ( Sle_CutIsUsed(pBase) && Sle_CutSize(pCut) < Sle_CutSize(pBase) && (Sle_CutSign(pCut) & Sle_CutSign(pBase)) == Sle_CutSign(pCut) && Sle_SetCutIsContainedOrder(pBase, pCut) )
            Sle_CutSetUnused( pBase );
    Vec_IntPushArray( vTemp, pCut, Sle_CutSize(pCut)+1 );
    Vec_IntAddToEntry( vTemp, 0, 1 );
}
int Sle_ManCutMerge( Gia_Man_t * p, int iObj, Vec_Int_t * vCuts, Vec_Int_t * vTemp, int nLutSize )
{
    Gia_Obj_t * pObj = Gia_ManObj( p, iObj );
    int * pList0 = Vec_IntEntryP( vCuts, Vec_IntEntry(vCuts, Gia_ObjFaninId0(pObj, iObj)) );
    int * pList1 = Vec_IntEntryP( vCuts, Vec_IntEntry(vCuts, Gia_ObjFaninId1(pObj, iObj)) );
    int * pCut0, * pCut1, i, k, Cut[8], nCuts = 0;
    Vec_IntFill( vTemp, 1, 0 );
    Sle_ForEachCut1( pList0, pCut0, i )
    Sle_ForEachCut1( pList1, pCut1, k )
    {
        if ( Sle_CutSize(pCut0) + Sle_CutSize(pCut1) > nLutSize && Sle_CutCountBits(Sle_CutSign(pCut0) | Sle_CutSign(pCut1)) > nLutSize )
            continue;
        if ( !Sle_CutMergeOrder(pCut0, pCut1, Cut, nLutSize) )
            continue;
        if ( Sle_SetLastCutIsContained(vTemp, Cut) )
            continue;
        Sle_SetAddCut( vTemp, Cut );
    }
    // reload
    Vec_IntWriteEntry( vCuts, iObj, Vec_IntSize(vCuts) );
    Vec_IntPush( vCuts, -1 );
    pList0 = Vec_IntArray(vTemp);
    Sle_ForEachCut( pList0, pCut0, i )
    {
        if ( !Sle_CutIsUsed(pCut0) )
            continue;
        Vec_IntPushArray( vCuts, pCut0, Sle_CutSize(pCut0)+1 );
        nCuts++;
    }
    // add unit cut
    Vec_IntPush( vCuts, Sle_CutSetSizeSign(1, 1<<(iObj % 28)) );
    Vec_IntPush( vCuts, iObj );
    Vec_IntWriteEntry( vCuts, Vec_IntEntry(vCuts, iObj), nCuts );
    return nCuts;
}
Vec_Int_t * Sle_ManComputeCuts( Gia_Man_t * p, int nLutSize, int fVerbose )
{
    int i, iObj, nCuts = 0;
    Vec_Int_t * vTemp = Vec_IntAlloc( 1000 );
    Vec_Int_t * vCuts = Vec_IntAlloc( 30 * Gia_ManAndNum(p) );
    assert( nLutSize <= 6 );
    Vec_IntFill( vCuts, Gia_ManObjNum(p), 0 );
    Gia_ManForEachCiId( p, iObj, i )
    {
        Vec_IntWriteEntry( vCuts, iObj, Vec_IntSize(vCuts) );
        Vec_IntPush( vCuts, 0 );
        Vec_IntPush( vCuts, Sle_CutSetSizeSign(1, 1<<(iObj % 28)) );
        Vec_IntPush( vCuts, iObj );
    }
    Gia_ManForEachAndId( p, iObj )
        nCuts += Sle_ManCutMerge( p, iObj, vCuts, vTemp, nLutSize );
    if ( fVerbose )
        printf( "Nodes = %d.  Cuts = %d.  Cuts/Node = %.2f.  Ints/Node = %.2f.  Mem = %.2f MB.\n", 
            Gia_ManAndNum(p), nCuts, 1.0*nCuts/Gia_ManAndNum(p), 
            1.0*(Vec_IntSize(vCuts)-Gia_ManObjNum(p))/Gia_ManAndNum(p), 
            1.0*Vec_IntMemory(vCuts) / (1<<20) );
    Vec_IntFree( vTemp );
    return vCuts;
}

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

  Synopsis    [Cut delay computation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sle_ManComputeDelayCut( Gia_Man_t * p, int * pCut, Vec_Int_t * vTime )
{
    int nSize   = Sle_CutSize(pCut);
    int k, * pC = Sle_CutLeaves(pCut);
    int DelayMax = 0;
    for ( k = 0; k < nSize; k++ )
        DelayMax = Abc_MaxInt( DelayMax, Vec_IntEntry(vTime, pC[k]) );
    return DelayMax + 1;
}
int Sle_ManComputeDelayOne( Gia_Man_t * p, int iObj, Vec_Int_t * vCuts, Vec_Int_t * vTime )
{
    int i, * pCut, Delay, DelayMin = ABC_INFINITY;
    int * pList = Vec_IntEntryP( vCuts, Vec_IntEntry(vCuts, iObj) );
    Sle_ForEachCut( pList, pCut, i )
    {
        Delay = Sle_ManComputeDelayCut( p, pCut, vTime );
        DelayMin = Abc_MinInt( DelayMin, Delay );
    }
    Vec_IntWriteEntry( vTime, iObj, DelayMin );
    return DelayMin;
}
int Sle_ManComputeDelay( Gia_Man_t * p, Vec_Int_t * vCuts )
{
    int iObj, Delay, DelayMax = 0;
    Vec_Int_t * vTime = Vec_IntStart( Gia_ManObjNum(p) );
    Gia_ManForEachAndId( p, iObj )
    {
        Delay = Sle_ManComputeDelayOne( p, iObj, vCuts, vTime );
        DelayMax = Abc_MaxInt( DelayMax, Delay );
    }
    Vec_IntFree( vTime );
    //printf( "Delay = %d.\n", DelayMax );
    return DelayMax;
}

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

  Synopsis    [Cut printing.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManPrintCut( int * pCut )
{
    int nSize   = Sle_CutSize(pCut);
    int k, * pC = Sle_CutLeaves(pCut);
    printf( "{" );
    for ( k = 0; k < nSize; k++ )
        printf( " %d", pC[k] );
    printf( " }\n" );
}
void Sle_ManPrintCuts( Gia_Man_t * p, Vec_Int_t * vCuts, int iObj )
{
    int i, * pCut;
    int * pList = Vec_IntEntryP( vCuts, Vec_IntEntry(vCuts, iObj) );
    printf( "Obj %3d\n", iObj );
    Sle_ForEachCut( pList, pCut, i )
        Sle_ManPrintCut( pCut );
    printf( "\n" );
}
void Sle_ManPrintCutsAll( Gia_Man_t * p, Vec_Int_t * vCuts )
{
    int iObj;
    Gia_ManForEachAndId( p, iObj )
        Sle_ManPrintCuts( p, vCuts, iObj );
}
void Sle_ManComputeCutsTest( Gia_Man_t * p )
{
    Vec_Int_t * vCuts = Sle_ManComputeCuts( p, 4, 1 );
    //Sle_ManPrintCutsAll( p, vCuts );
    Vec_IntFree( vCuts );
}



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

  Synopsis    [Derive mask representing internal nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Bit_t * Sle_ManInternalNodeMask( Gia_Man_t * pGia )
{
    int iObj;
    Vec_Bit_t * vMask = Vec_BitStart( Gia_ManObjNum(pGia) );  
    Gia_ManForEachAndId( pGia, iObj )
        Vec_BitWriteEntry( vMask, iObj, 1 );
    return vMask;
}

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

  Synopsis    [Check if the cut contains only primary inputs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sle_ManCutHasPisOnly( int * pCut, Vec_Bit_t * vMask )
{
    int k, * pC = Sle_CutLeaves(pCut);
    for ( k = 0; k < Sle_CutSize(pCut); k++ )
        if ( Vec_BitEntry(vMask, pC[k]) ) // internal node
            return 0;
    return 1;
}

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

  Synopsis    [Derive cut fanins of each node.]

  Description [These are nodes that are fanins of some cut of this node.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManCollectCutFaninsOne( Gia_Man_t * pGia, int iObj, Vec_Int_t * vCuts, Vec_Bit_t * vMask, Vec_Int_t * vCutFanins, Vec_Bit_t * vMap )
{
    int i, iFanin, * pCut, * pList = Vec_IntEntryP( vCuts, Vec_IntEntry(vCuts, iObj) );
    Sle_ForEachCut( pList, pCut, i )
    {
        int nSize   = Sle_CutSize(pCut);
        int k, * pC = Sle_CutLeaves(pCut);
        assert( nSize > 1 );
        for ( k = 0; k < nSize; k++ )
            if ( Vec_BitEntry(vMask, pC[k]) && !Vec_BitEntry(vMap, pC[k]) )
            {
                Vec_BitWriteEntry( vMap, pC[k], 1 );
                Vec_IntPush( vCutFanins, pC[k] );
            }
    }
    Vec_IntForEachEntry( vCutFanins, iFanin, i )
        Vec_BitWriteEntry( vMap, iFanin, 0 );
}
Vec_Wec_t * Sle_ManCollectCutFanins( Gia_Man_t * pGia, Vec_Int_t * vCuts, Vec_Bit_t * vMask )
{
    int iObj;
    Vec_Bit_t * vMap = Vec_BitStart( Gia_ManObjNum(pGia) );
    Vec_Wec_t * vCutFanins = Vec_WecStart( Gia_ManObjNum(pGia) );
    Gia_ManForEachAndId( pGia, iObj )
        Sle_ManCollectCutFaninsOne( pGia, iObj, vCuts, vMask, Vec_WecEntry(vCutFanins, iObj), vMap );
    Vec_BitFree( vMap );
    return vCutFanins;
}


typedef struct Sle_Man_t_ Sle_Man_t;
struct Sle_Man_t_
{
    // user's data
    Gia_Man_t *    pGia;         // user's manager (with mapping and edges)
    int            nLevels;      // total number of levels
    int            fVerbose;     // verbose flag
    int            nSatCalls;    // the number of SAT calls
    // SAT variables
    int            nNodeVars;    // node variables (Gia_ManAndNum(pGia))
    int            nCutVars;     // cut variables (total number of non-trivial cuts)
    int            nEdgeVars;    // edge variables (total number of internal edges)
    int            nDelayVars;   // delay variables (nNodeVars * nLevelsMax)
    int            nVarsTotal;   // total number of variables
    // SAT clauses
    int            nCutClas;     // cut clauses
    int            nEdgeClas;    // edge clauses
    int            nEdgeClas2;   // edge clauses exclusivity
    int            nDelayClas;   // delay clauses
    // internal data
    sat_solver *   pSat;         // SAT solver
    Vec_Bit_t *    vMask;        // internal node mask
    Vec_Int_t *    vCuts;        // cuts for each node
    Vec_Wec_t *    vCutFanins;   // internal cut fanins of each node
    Vec_Wec_t *    vFanoutEdges; // internal cut fanins of each node
    Vec_Wec_t *    vEdgeCuts;    // cuts of each edge for one node
    Vec_Int_t *    vObjMap;      // temporary object map
    Vec_Int_t *    vCutFirst;    // first cut of each node
    Vec_Int_t *    vEdgeFirst;   // first edge of each node
    Vec_Int_t *    vDelayFirst;  // first edge of each node
    Vec_Int_t *    vPolars;      // initial 
    Vec_Int_t *    vLits;        // literals 
    // statistics
    abctime        timeStart;
};

static inline int * Sle_ManList( Sle_Man_t * p, int i ) { return Vec_IntEntryP(p->vCuts, Vec_IntEntry(p->vCuts, i)); }

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Sle_Man_t * Sle_ManAlloc( Gia_Man_t * pGia, int nLevels, int fVerbose )
{
    Sle_Man_t * p   = ABC_CALLOC( Sle_Man_t, 1 );
    p->pGia         = pGia;
    p->nLevels      = nLevels;
    p->fVerbose     = fVerbose;
    p->vMask        = Sle_ManInternalNodeMask( pGia );
    p->vCuts        = Sle_ManComputeCuts( pGia, 4, fVerbose );
    p->vCutFanins   = Sle_ManCollectCutFanins( pGia, p->vCuts, p->vMask );
    p->vFanoutEdges = Vec_WecStart( Gia_ManObjNum(pGia) );
    p->vEdgeCuts    = Vec_WecAlloc( 100 );
    p->vObjMap      = Vec_IntStartFull( Gia_ManObjNum(pGia) );
    p->vCutFirst    = Vec_IntStartFull( Gia_ManObjNum(pGia) );
    p->vEdgeFirst   = Vec_IntStartFull( Gia_ManObjNum(pGia) );
    p->vDelayFirst  = Vec_IntStartFull( Gia_ManObjNum(pGia) );
    p->vPolars      = Vec_IntAlloc( 100 );
    p->vLits        = Vec_IntAlloc( 100 );
    p->nLevels      = Sle_ManComputeDelay( pGia, p->vCuts );
    return p;
}
void Sle_ManStop( Sle_Man_t * p )
{
    sat_solver_delete( p->pSat );
    Vec_BitFree( p->vMask );
    Vec_IntFree( p->vCuts );
    Vec_WecFree( p->vCutFanins );
    Vec_WecFree( p->vFanoutEdges );
    Vec_WecFree( p->vEdgeCuts );
    Vec_IntFree( p->vObjMap );
    Vec_IntFree( p->vCutFirst );
    Vec_IntFree( p->vEdgeFirst );
    Vec_IntFree( p->vDelayFirst );
    Vec_IntFree( p->vPolars );
    Vec_IntFree( p->vLits );
    ABC_FREE( p );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManMarkupVariables( Sle_Man_t * p )
{
    int iObj, Counter = Gia_ManObjNum(p->pGia);
    // node variables
    p->nNodeVars = Counter;
    // cut variables
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        Vec_IntWriteEntry( p->vCutFirst, iObj, Counter );
        Counter += Sle_ListCutNum( Sle_ManList(p, iObj) );
    }
    p->nCutVars = Counter - p->nNodeVars;
    // edge variables
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        Vec_IntWriteEntry( p->vEdgeFirst, iObj, Counter );
        Counter += Vec_IntSize( Vec_WecEntry(p->vCutFanins, iObj) );
    }
    p->nEdgeVars = Counter - p->nCutVars - p->nNodeVars;
    // delay variables
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        Vec_IntWriteEntry( p->vDelayFirst, iObj, Counter );
        Counter += p->nLevels;
    }
    p->nDelayVars = Counter - p->nEdgeVars - p->nCutVars - p->nNodeVars;
    p->nVarsTotal = Counter;
}


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

  Synopsis    [Derive initial variable assignment.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
// returns 1 if Cut can represent LUT (Cut is equal or is contained in LUT)
static inline int Sle_ManCheckContained( int * pCutLeaves, int nCutLeaves, int * pLutFanins, int nLutFanins )
{
    int i, k;
    if ( nCutLeaves > nLutFanins )
        return 0;
    for ( i = 0; i < nCutLeaves; i++ )
    {
        for ( k = 0; k < nLutFanins; k++ )
            if ( pCutLeaves[i] == pLutFanins[k] )
                break;
        if ( k == nLutFanins ) // not found
            return 0;
    }
    return 1;
}
void Sle_ManDeriveInit( Sle_Man_t * p )
{
    Vec_Int_t * vEdges;
    int i, iObj, iFanin, iEdge;
    if ( !Gia_ManHasMapping(p->pGia) )
        return;
    // derive initial state
    Vec_IntClear( p->vPolars );
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        int nFanins, * pFanins, * pCut, * pList, iFound = -1;
        if ( !Gia_ObjIsLut(p->pGia, iObj) )
            continue;
        Vec_IntPush( p->vPolars, iObj ); // node var
        nFanins = Gia_ObjLutSize( p->pGia, iObj );
        pFanins = Gia_ObjLutFanins( p->pGia, iObj );
        // find cut
        pList = Sle_ManList( p, iObj );
        Sle_ForEachCut( pList, pCut, i )
            if ( Sle_ManCheckContained( Sle_CutLeaves(pCut), Sle_CutSize(pCut), pFanins, nFanins ) )
            {
                iFound = i;
                break;
            }
        if ( iFound == -1 )
        {
            printf( "Cannot find the following cut at node %d: {", iObj );
            for ( i = 0; i < nFanins; i++ )
                printf( " %d", pFanins[i] );
            printf( " }\n" );
            Sle_ManPrintCuts( p->pGia, p->vCuts, iObj );
            fflush( stdout );
        }
        assert( iFound >= 0 );
        Vec_IntPush( p->vPolars, Vec_IntEntry(p->vCutFirst, iObj) + iFound ); // cut var
        // check if the cut contains only primary inputs - if so, its delay is equal to 1
        if ( Sle_ManCutHasPisOnly(pCut, p->vMask) )
            Vec_IntPush( p->vPolars, Vec_IntEntry(p->vDelayFirst, iObj) ); // delay var
    }
    Vec_IntSort( p->vPolars, 0 );
    // find zero-delay edges
    if ( !p->pGia->vEdge1 )
        return;
    vEdges = Gia_ManEdgeToArray( p->pGia );
    Vec_IntForEachEntryDouble( vEdges, iFanin, iObj, i )
    {
        assert( iFanin < iObj );
        assert( Gia_ObjIsLut(p->pGia, iFanin) );
        assert( Gia_ObjIsLut(p->pGia, iObj) );
        assert( Gia_ObjIsAnd(Gia_ManObj(p->pGia, iFanin)) );
        assert( Gia_ObjIsAnd(Gia_ManObj(p->pGia, iObj)) );
        // find edge
        iEdge = Vec_IntFind( Vec_WecEntry(p->vCutFanins, iObj), iFanin );
        if ( iEdge < 0 )
            continue;
        assert( iEdge >= 0 );
        Vec_IntPush( p->vPolars, Vec_IntEntry(p->vEdgeFirst, iObj) + iEdge ); // edge
    }
    Vec_IntFree( vEdges );
}

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

  Synopsis    [Derive CNF.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManDeriveCnf( Sle_Man_t * p, int nBTLimit, int fDynamic )
{
    int nTimeOut = 0;
    int i, iObj, value;
    Vec_Int_t * vArray;

    // start the SAT solver
    p->pSat = sat_solver_new();
    sat_solver_setnvars( p->pSat, p->nVarsTotal );
    sat_solver_set_resource_limits( p->pSat, nBTLimit, 0, 0, 0 );
    sat_solver_set_runtime_limit( p->pSat, nTimeOut ? nTimeOut * CLOCKS_PER_SEC + Abc_Clock(): 0 );
    sat_solver_set_random( p->pSat, 1 );
    sat_solver_set_polarity( p->pSat, Vec_IntArray(p->vPolars), Vec_IntSize(p->vPolars) );
    sat_solver_set_var_activity( p->pSat, NULL, p->nVarsTotal );

    // set drivers to be mapped
    Gia_ManForEachCoDriverId( p->pGia, iObj, i )
        if ( Vec_BitEntry(p->vMask, iObj) ) // internal node
        {
            Vec_IntFill( p->vLits, 1, Abc_Var2Lit(iObj, 0) ); // pos lit
            value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
            assert( value );
        }

    // add cover clauses and edge-to-cut clauses
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        int e, iEdge, nEdges = 0, Entry;
        int iCutVar0  = Vec_IntEntry( p->vCutFirst, iObj );
        int iEdgeVar0 = Vec_IntEntry( p->vEdgeFirst, iObj );
        int * pCut, * pList  = Sle_ManList( p, iObj );
        Vec_Int_t * vCutFans = Vec_WecEntry( p->vCutFanins, iObj );
        assert( iCutVar0 > 0 && iEdgeVar0 > 0 );
        // node requires one of the cuts
        Vec_IntFill( p->vLits, 1, Abc_Var2Lit(iObj, 1) ); // neg lit
        for ( i = 0; i < Sle_ListCutNum(pList); i++ )
            Vec_IntPush( p->vLits, Abc_Var2Lit(iCutVar0 + i, 0) );
        value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
        assert( value );
        // cuts are mutually exclusive
        for ( i = 0; i < Sle_ListCutNum(pList); i++ )
            for ( e = i+1; e < Sle_ListCutNum(pList); e++ )
            {
                Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(iCutVar0 + i, 1), Abc_Var2Lit(iCutVar0 + e, 1) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );
            }
        // cut requires fanin nodes
        Vec_WecInit( p->vEdgeCuts, Vec_IntSize(vCutFans) );
        Sle_ForEachCut( pList, pCut, i )
        {
            int nSize   = Sle_CutSize(pCut);
            int k, * pC = Sle_CutLeaves(pCut);
            assert( nSize > 1 );
            for ( k = 0; k < nSize; k++ )
            {
                if ( !Vec_BitEntry(p->vMask, pC[k]) )
                    continue;
                Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(iCutVar0 + i, 1), Abc_Var2Lit(pC[k], 0) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );
                // find the edge ID between pC[k] and iObj
                iEdge = Vec_IntEntry(p->vObjMap, pC[k]);
                if ( iEdge == -1 )
                {
                    Vec_IntWriteEntry( p->vObjMap, pC[k], (iEdge = nEdges++) );
                    Vec_WecPush( p->vFanoutEdges, pC[k], iEdgeVar0 + iEdge );
                }
                Vec_WecPush( p->vEdgeCuts, iEdge, iCutVar0 + i );
                p->nCutClas++;
            }
            // cut requires the node
            Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(iCutVar0 + i, 1), Abc_Var2Lit(iObj, 0) );
            value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
            assert( value );
        }
        assert( nEdges == Vec_IntSize(vCutFans) );

        // edge requires one of the fanout cuts
        Vec_WecForEachLevel( p->vEdgeCuts, vArray, e )
        {
            assert( Vec_IntSize(vArray) > 0 );
            Vec_IntFill( p->vLits, 1, Abc_Var2Lit(iEdgeVar0 + e, 1) ); // neg lit (edge)
            Vec_IntForEachEntry( vArray, Entry, i )
                Vec_IntPush( p->vLits, Abc_Var2Lit(Entry, 0) ); // pos lit (cut)
            value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
            assert( value );
            p->nEdgeClas++;
        }

        // clean object map
        Vec_IntForEachEntry( vCutFans, Entry, i )
            Vec_IntWriteEntry( p->vObjMap, Entry, -1 );
    }

    // mutual exclusivity of edges
    Vec_WecForEachLevel( p->vFanoutEdges, vArray, iObj )
    {
        int j, k, EdgeJ, EdgeK;
        int iEdgeVar0 = Vec_IntEntry( p->vEdgeFirst, iObj );
        Vec_Int_t * vCutFans = Vec_WecEntry( p->vCutFanins, iObj );
        // add fanin edges
        for ( i = 0; i < Vec_IntSize(vCutFans); i++ )
            Vec_IntPush( vArray, iEdgeVar0 + i );
        // generate pairs
        if ( fDynamic )
            continue;
        Vec_IntForEachEntry( vArray, EdgeJ, j )
            Vec_IntForEachEntryStart( vArray, EdgeK, k, j+1 )
            {
                Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(EdgeJ, 1), Abc_Var2Lit(EdgeK, 1) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );
            }
        p->nEdgeClas2 += Vec_IntSize(vArray) * (Vec_IntSize(vArray) - 1) / 2;
    }

    // add delay clauses
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        int e, iFanin;
        int iEdgeVar0 = Vec_IntEntry( p->vEdgeFirst, iObj );
        int iDelayVar0 = Vec_IntEntry( p->vDelayFirst, iObj );
        Vec_Int_t * vCutFans = Vec_WecEntry( p->vCutFanins, iObj );
        // check if the node has cuts containing only primary inputs
        int * pCut, * pList = Sle_ManList( p, iObj );
        Sle_ForEachCut( pList, pCut, i )
            if ( Sle_ManCutHasPisOnly(pCut, p->vMask) )
            {
                Vec_IntFill( p->vLits, 1, Abc_Var2Lit(iDelayVar0, 0) ); // pos lit
//                Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(iObj, 1), Abc_Var2Lit(iDelayVar0, 0) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );
                break;
            }
//        if ( i < Sle_ListCutNum(pList) )
//            continue;
        // create delay requirements for each cut fanin of this node
        Vec_IntForEachEntry( vCutFans, iFanin, e )
        {
            int d, iDelayVarIn = Vec_IntEntry( p->vDelayFirst, iFanin );
            for ( d = 0; d < p->nLevels; d++ )
            {
                Vec_IntClear( p->vLits );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iObj, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iFanin, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iDelayVarIn + d, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iEdgeVar0 + e, 0) );
                if ( d < p->nLevels-1 )
                    Vec_IntPush( p->vLits, Abc_Var2Lit(iDelayVar0 + d+1, 0) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );

                Vec_IntClear( p->vLits );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iObj, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iFanin, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iDelayVarIn + d, 1) );
                if ( d < p->nLevels-1 )
                    Vec_IntPush( p->vLits, Abc_Var2Lit(iEdgeVar0 + e, 1) );
                Vec_IntPush( p->vLits, Abc_Var2Lit(iDelayVar0 + d, 0) );
                value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                assert( value );
            }
            p->nDelayClas += 2*p->nLevels;
        }
    }
}

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

  Synopsis    [Add edge compatibility constraints.]

  Description [Returns 1 if constraints have been added.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sle_ManAddEdgeConstraints( Sle_Man_t * p, int nEdges )
{
    Vec_Int_t * vArray;
    Vec_Int_t * vTemp = Vec_IntAlloc( 100 );
    int value, iObj, nAdded = 0;
    assert( nEdges == 1 || nEdges == 2 );
    Vec_WecForEachLevel( p->vFanoutEdges, vArray, iObj )
    {
        int j, k, EdgeJ, EdgeK;
        // check if they are incompatible
        Vec_IntClear( vTemp );
        Vec_IntForEachEntry( vArray, EdgeJ, j )
            if ( sat_solver_var_value(p->pSat, EdgeJ) )
                Vec_IntPush( vTemp, EdgeJ );
        if ( Vec_IntSize(vTemp) <= nEdges )
            continue;
        nAdded++;
        if ( nEdges == 1 )
        {
            // generate pairs
            Vec_IntForEachEntry( vTemp, EdgeJ, j )
                Vec_IntForEachEntryStart( vTemp, EdgeK, k, j+1 )
                {
                    Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(EdgeJ, 1), Abc_Var2Lit(EdgeK, 1) );
                    value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                    assert( value );
                }
            p->nEdgeClas2 += Vec_IntSize(vTemp) * (Vec_IntSize(vTemp) - 1) / 2;
        }
        else if ( nEdges == 2 )
        {
            int m, EdgeM;
            // generate triples
            Vec_IntForEachEntry( vTemp, EdgeJ, j )
                Vec_IntForEachEntryStart( vTemp, EdgeK, k, j+1 )
                    Vec_IntForEachEntryStart( vTemp, EdgeM, m, k+1 )
                    {
                        Vec_IntFillTwo( p->vLits, 2, Abc_Var2Lit(EdgeJ, 1), Abc_Var2Lit(EdgeK, 1) );
                        Vec_IntPush( p->vLits, Abc_Var2Lit(EdgeM, 1) );
                        value = sat_solver_addclause( p->pSat, Vec_IntArray(p->vLits), Vec_IntLimit(p->vLits)  );
                        assert( value );
                    }
            p->nEdgeClas2 += Vec_IntSize(vTemp) * (Vec_IntSize(vTemp) - 1) * (Vec_IntSize(vTemp) - 2) / 6;
        }
        else assert( 0 );
    }
    Vec_IntFree( vTemp );
    //printf( "Added clauses to %d nodes.\n", nAdded );
    return nAdded;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManDeriveResult( Sle_Man_t * p, Vec_Int_t * vEdge2, Vec_Int_t * vMapping )
{
    Vec_Int_t * vMapTemp;
    int iObj;
    // create mapping
    Vec_IntFill( vMapping, Gia_ManObjNum(p->pGia), 0 );
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        int i, iCut, iCutVar0 = Vec_IntEntry( p->vCutFirst, iObj );
        int * pCut, * pCutThis = NULL, * pList = Sle_ManList( p, iObj );
        if ( !sat_solver_var_value(p->pSat, iObj) )
            continue;
        Sle_ForEachCut( pList, pCut, iCut )
            if ( sat_solver_var_value(p->pSat, iCutVar0 + iCut) )
            {
                assert( pCutThis == NULL );
                pCutThis = pCut;
            }
        assert( pCutThis != NULL );
        Vec_IntWriteEntry( vMapping, iObj, Vec_IntSize(vMapping) );
        Vec_IntPush( vMapping, Sle_CutSize(pCutThis) );
        for ( i = 0; i < Sle_CutSize(pCutThis); i++ )
            Vec_IntPush( vMapping, Sle_CutLeaves(pCutThis)[i] );
        Vec_IntPush( vMapping, iObj );
    }
    vMapTemp = p->pGia->vMapping;
    p->pGia->vMapping = vMapping;
    // collect edges
    Vec_IntClear( vEdge2 );
    Gia_ManForEachAndId( p->pGia, iObj )
    {
        int i, iFanin, iEdgeVar0 = Vec_IntEntry( p->vEdgeFirst, iObj );
        Vec_Int_t * vCutFans = Vec_WecEntry( p->vCutFanins, iObj );
        //int * pCut, * pList  = Sle_ManList( p, iObj );
        // int iCutVar0  = Vec_IntEntry( p->vCutFirst, iObj );
        if ( !sat_solver_var_value(p->pSat, iObj) )
            continue;
        //for ( i = 0; i < Sle_ListCutNum(pList); i++ )
        //    printf( "%d", sat_solver_var_value(p->pSat, iCutVar0 + i) );
        //printf( "\n" );
        Vec_IntForEachEntry( vCutFans, iFanin, i )
            if ( sat_solver_var_value(p->pSat, iFanin) && sat_solver_var_value(p->pSat, iEdgeVar0 + i) )
            {
                // verify edge
                int * pFanins = Gia_ObjLutFanins( p->pGia, iObj );
                int k, nFanins = Gia_ObjLutSize( p->pGia, iObj );
                for ( k = 0; k < nFanins; k++ )
                {
                    //printf( "%d ", pFanins[k] );
                    if ( pFanins[k] == iFanin )
                        break;
                }
                //printf( "\n" );
                if ( k == nFanins )
//                    printf( "Cannot find LUT with input %d at node %d.\n", iFanin, iObj );
                    continue;
                Vec_IntPushTwo( vEdge2, iFanin, iObj );
            }
    }
    p->pGia->vMapping = vMapTemp;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sle_ManExplore( Gia_Man_t * pGia, int nBTLimit, int DelayInit, int fDynamic, int fTwoEdges, int fVerbose )
{
    int fVeryVerbose = 0;
    abctime clk = Abc_Clock();
    Vec_Int_t * vEdges2  = Vec_IntAlloc(1000);
    Vec_Int_t * vMapping = Vec_IntAlloc(1000);
    int i, iLut, nConfs, status, Delay, iFirstVar;
    int DelayStart = (DelayInit || !Gia_ManHasMapping(pGia)) ? DelayInit : Gia_ManLutLevel(pGia, NULL);
    Sle_Man_t * p = Sle_ManAlloc( pGia, DelayStart, fVerbose );
    if ( fVerbose )
        printf( "Running solver with %d conflicts, %d initial delay, and %d edges. Dynamic constraints = %s.\n", nBTLimit, DelayInit, 1+fTwoEdges, fDynamic?"yes":"no" );
    Sle_ManMarkupVariables( p );
    if ( fVerbose )
        printf( "Vars:  Total = %d.  Node = %d. Cut = %d. Edge = %d. Delay = %d.\n", 
            p->nVarsTotal, p->nNodeVars, p->nCutVars, p->nEdgeVars, p->nDelayVars );
    Sle_ManDeriveInit( p );
    Sle_ManDeriveCnf( p, nBTLimit, fDynamic || fTwoEdges );
    if ( fVerbose )
        printf( "Clas:  Total = %d.  Cut = %d. Edge = %d. EdgeEx = %d. Delay = %d.\n", 
            sat_solver_nclauses(p->pSat), p->nCutClas, p->nEdgeClas, p->nEdgeClas2, p->nDelayClas );
    //Sat_SolverWriteDimacs( p->pSat, "temp.cnf", NULL, NULL, 0 );
    for ( Delay = p->nLevels; Delay >= 0; Delay-- )
    {
        // we constrain COs, although it would be fine to constrain only POs
        if ( Delay < p->nLevels )
        {
            Gia_ManForEachCoDriverId( p->pGia, iLut, i )
                if ( Vec_BitEntry(p->vMask, iLut) ) // internal node
                {
                    iFirstVar = Vec_IntEntry( p->vDelayFirst, iLut );
                    if ( !sat_solver_push(p->pSat, Abc_Var2Lit(iFirstVar + Delay, 1)) )
                        break;
                }
            if ( i < Gia_ManCoNum(p->pGia) )
            {
                if ( fVerbose )
                {
                    printf( "Proved UNSAT for delay %d.  ", Delay );
                    Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
                }
                break;
            }
        }
        // solve with assumptions
        //clk = Abc_Clock();
        nConfs = sat_solver_nconflicts( p->pSat );
        while ( 1 )
        {
            p->nSatCalls++;
            status = sat_solver_solve_internal( p->pSat );
            if ( status != l_True )
                break;
            if ( !Sle_ManAddEdgeConstraints(p, 1+fTwoEdges) )
                break;
        }
        nConfs = sat_solver_nconflicts( p->pSat ) - nConfs;
        if ( status == l_True )
        {
            if ( fVerbose )
            {
                int nNodes = 0, nEdges = 0;
                for ( i = 0; i < p->nNodeVars; i++ )
                    nNodes += sat_solver_var_value(p->pSat, i);
                for ( i = 0; i < p->nEdgeVars; i++ )
                    nEdges += sat_solver_var_value(p->pSat, p->nNodeVars + p->nCutVars + i);
                printf( "Solution with delay %2d, node count %5d, and edge count %5d exists. Conf = %8d.  ", Delay, nNodes, nEdges, nConfs );
                Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
            }
            // save the result
            Sle_ManDeriveResult( p, vEdges2, vMapping );
            if ( fVeryVerbose )
            {
                printf( "Nodes:  " );
                for ( i = 0; i < p->nNodeVars; i++ )
                    if ( sat_solver_var_value(p->pSat, i) )
                        printf( "%d ", i );
                printf( "\n" );
                printf( "\n" );

                Vec_IntPrint( p->vCutFirst );
                printf( "Cuts:   " );
                for ( i = 0; i < p->nCutVars; i++ )
                    if ( sat_solver_var_value(p->pSat, p->nNodeVars + i) )
                        printf( "%d ", p->nNodeVars + i );
                printf( "\n" );
                printf( "\n" );

                Vec_IntPrint( p->vEdgeFirst );
                printf( "Edges:  " );
                for ( i = 0; i < p->nEdgeVars; i++ )
                    if ( sat_solver_var_value(p->pSat, p->nNodeVars + p->nCutVars + i) )
                        printf( "%d ", p->nNodeVars + p->nCutVars + i );
                printf( "\n" );
                printf( "\n" );

                Vec_IntPrint( p->vDelayFirst );
                printf( "Delays: " );
                for ( i = 0; i < p->nDelayVars; i++ )
                    if ( sat_solver_var_value(p->pSat, p->nNodeVars + p->nCutVars + p->nEdgeVars + i) )
                        printf( "%d ", p->nNodeVars + p->nCutVars + p->nEdgeVars + i );
                printf( "\n" );
                printf( "\n" );
            }
        }
        else
        {
            if ( fVerbose )
            {
                if ( status == l_False )
                    printf( "Proved UNSAT for delay %d. Conf = %8d.  ", Delay, nConfs );
                else
                    printf( "Resource limit reached for delay %d. Conf = %8d.  ", Delay, nConfs );
                Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
            }
            break;
        }
    }
    if ( fVerbose )
        printf( "Clas:  Total = %d.  Cut = %d. Edge = %d. EdgeEx = %d. Delay = %d.  Calls = %d.\n", 
            sat_solver_nclauses(p->pSat), p->nCutClas, p->nEdgeClas, p->nEdgeClas2, p->nDelayClas, p->nSatCalls );
    if ( Vec_IntSize(vMapping) > 0 )
    {
        Gia_ManEdgeFromArray( p->pGia, vEdges2 );
        Vec_IntFree( vEdges2 );
        Vec_IntFreeP( &p->pGia->vMapping );
        p->pGia->vMapping = vMapping;
    }
    else
    {
        Vec_IntFree( vEdges2 );
        Vec_IntFree( vMapping );
    }
    Vec_IntFreeP( &p->pGia->vPacking );
    Sle_ManStop( p );
}

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


ABC_NAMESPACE_IMPL_END