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

  FileName    [giaAbsVta.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Variable time-frame abstraction.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "sat/bsat/satSolver2.h"
#include "base/main/main.h"

ABC_NAMESPACE_IMPL_START

#define VTA_LARGE  0xFFFFFFF

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

typedef struct Vta_Obj_t_ Vta_Obj_t; // object
struct Vta_Obj_t_
{
    int           iObj;
    int           iFrame;
    int           iNext;
    unsigned      Prio   : 28;  // related to VTA_LARGE
    unsigned      Value  :  2;
    unsigned      fAdded :  1;
    unsigned      fVisit :  1;
};

typedef struct Vta_Man_t_ Vta_Man_t; // manager
struct Vta_Man_t_
{
    // user data
    Gia_Man_t *   pGia;         // AIG manager
    Gia_ParVta_t* pPars;        // parameters
    // internal data
    int           nObjs;        // the number of objects
    int           nObjsAlloc;   // the number of objects allocated
    int           nBins;        // number of hash table entries
    int *         pBins;        // hash table bins
    Vta_Obj_t *   pObjs;        // storage for objects
    Vec_Int_t *   vOrder;       // objects in DPS order
    // abstraction
    int           nObjBits;     // the number of bits to represent objects
    unsigned      nObjMask;     // object mask
    Vec_Ptr_t *   vFrames;      // start abstraction for each frame
    int           nWords;       // the number of words in the record
    int           nCexes;       // the number of CEXes
    int           nObjAdded;    // objects added to the abstraction
    Vec_Int_t *   vSeens;       // seen objects
    Vec_Bit_t *   vSeenGla;     // seen objects in all frames
    int           nSeenGla;     // seen objects in all frames
    int           nSeenAll;     // seen objects in all frames
    // other data
    Vec_Int_t *   vCla2Var;     // map clauses into variables
    Vec_Ptr_t *   vCores;       // unsat core for each frame
    sat_solver2 * pSat;         // incremental SAT solver
    Vec_Int_t *   vAddedNew;    // the IDs of variables added to the solver
    // statistics 
    clock_t       timeSat;
    clock_t       timeUnsat;
    clock_t       timeCex;
    clock_t       timeOther;
};


// ternary simulation

#define VTA_VAR0   1
#define VTA_VAR1   2
#define VTA_VARX   3

static inline int Vta_ValIs0( Vta_Obj_t * pThis, int fCompl )
{
    if ( pThis->Value == VTA_VAR1 && fCompl )
        return 1;
    if ( pThis->Value == VTA_VAR0 && !fCompl )
        return 1;
    return 0;
}
static inline int Vta_ValIs1( Vta_Obj_t * pThis, int fCompl )
{
    if ( pThis->Value == VTA_VAR0 && fCompl )
        return 1;
    if ( pThis->Value == VTA_VAR1 && !fCompl )
        return 1;
    return 0;
}

static inline Vta_Obj_t *  Vta_ManObj( Vta_Man_t * p, int i )           { assert( i >= 0 && i < p->nObjs ); return i ? p->pObjs + i : NULL;                     }
static inline int          Vta_ObjId( Vta_Man_t * p, Vta_Obj_t * pObj ) { assert( pObj > p->pObjs && pObj < p->pObjs + p->nObjs ); return pObj - p->pObjs;      }

#define Vta_ManForEachObj( p, pObj, i )                                 \
    for ( i = 1; (i < p->nObjs) && ((pObj) = Vta_ManObj(p, i)); i++ )
#define Vta_ManForEachObjObj( p, pObjVta, pObjGia, i )                  \
    for ( i = 1; (i < p->nObjs) && ((pObjVta) = Vta_ManObj(p, i)) && ((pObjGia) = Gia_ManObj(p->pGia, pObjVta->iObj)); i++ )
#define Vta_ManForEachObjObjReverse( p, pObjVta, pObjGia, i )           \
    for ( i = Vec_IntSize(vVec) - 1; (i >= 1) && ((pObjVta) = Vta_ManObj(p, i)) && ((pObjGia) = Gia_ManObj(p->pGia, pObjVta->iObj)); i++ )

#define Vta_ManForEachObjVec( vVec, p, pObj, i )                        \
    for ( i = 0; (i < Vec_IntSize(vVec)) && ((pObj) = Vta_ManObj(p, Vec_IntEntry(vVec,i))); i++ )
#define Vta_ManForEachObjVecReverse( vVec, p, pObj, i )                 \
    for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && ((pObj) = Vta_ManObj(p, Vec_IntEntry(vVec,i))); i-- )

#define Vta_ManForEachObjObjVec( vVec, p, pObj, pObjG, i )              \
    for ( i = 0; (i < Vec_IntSize(vVec)) && ((pObj) = Vta_ManObj(p, Vec_IntEntry(vVec,i))) && ((pObjG) = Gia_ManObj(p->pGia, pObj->iObj)); i++ )
#define Vta_ManForEachObjObjVecReverse( vVec, p, pObj, pObjG, i )       \
    for ( i = Vec_IntSize(vVec) - 1; (i >= 0) && ((pObj) = Vta_ManObj(p, Vec_IntEntry(vVec,i))) && ((pObjG) = Gia_ManObj(p->pGia, pObj->iObj)); i-- )


// abstraction is given as an array of integers:
// - the first entry is the number of timeframes (F)
// - the next (F+1) entries give the beginning position of each timeframe
// - the following entries give the object IDs
// invariant:  assert( vec[vec[0]+1] == size(vec) );

extern void Vga_ManAddClausesOne( Vta_Man_t * p, int iObj, int iFrame );

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

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

  Synopsis    [This procedure sets default parameters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_VtaSetDefaultParams( Gia_ParVta_t * p )
{
    memset( p, 0, sizeof(Gia_ParVta_t) );
    p->nFramesMax    =      0;   // maximum frames
    p->nFramesStart  =      0;   // starting frame 
    p->nFramesPast   =      4;   // overlap frames
    p->nConfLimit    =      0;   // conflict limit
    p->nLearntMax    =   1000;   // max number of learned clauses
    p->nTimeOut      =      0;   // timeout in seconds
    p->nRatioMin     =     10;   // stop when less than this % of object is abstracted
    p->fUseTermVars  =      0;   // use terminal variables
    p->fUseRollback  =      0;   // use rollback to the starting number of frames
    p->fVerbose      =      0;   // verbose flag
    p->iFrame        =     -1;   // the number of frames covered 
}

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

  Synopsis    [Converting from one array to per-frame arrays.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Gia_VtaAbsToFrames( Vec_Int_t * vAbs )
{
    Vec_Ptr_t * vFrames;
    Vec_Int_t * vFrame;
    int i, k, Entry, iStart, iStop = -1;
    int nFrames = Vec_IntEntry( vAbs, 0 );
    assert( Vec_IntEntry(vAbs, nFrames+1) == Vec_IntSize(vAbs) );
    vFrames = Vec_PtrAlloc( nFrames );
    for ( i = 0; i < nFrames; i++ )
    {
        iStart = Vec_IntEntry( vAbs, i+1 );
        iStop  = Vec_IntEntry( vAbs, i+2 );
        vFrame = Vec_IntAlloc( iStop - iStart );
        Vec_IntForEachEntryStartStop( vAbs, Entry, k, iStart, iStop )
            Vec_IntPush( vFrame, Entry );
        Vec_PtrPush( vFrames, vFrame );
    }
    assert( iStop == Vec_IntSize(vAbs) );
    return vFrames;
}

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

  Synopsis    [Converting from per-frame arrays to one integer array.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Gia_VtaFramesToAbs( Vec_Vec_t * vFrames )
{
    Vec_Int_t * vOne, * vAbs;
    int i, k, Entry, nSize;
    vAbs = Vec_IntAlloc( 2 + Vec_VecSize(vFrames) + Vec_VecSizeSize(vFrames) );
    Vec_IntPush( vAbs, Vec_VecSize(vFrames) );
    nSize = Vec_VecSize(vFrames) + 2;
    Vec_VecForEachLevelInt( vFrames, vOne, i )
    {
        Vec_IntPush( vAbs, nSize );
        nSize += Vec_IntSize( vOne );
    }
    Vec_IntPush( vAbs, nSize );
    assert( Vec_IntSize(vAbs) == Vec_VecSize(vFrames) + 2 );
    Vec_VecForEachLevelInt( vFrames, vOne, i )
        Vec_IntForEachEntry( vOne, Entry, k )
            Vec_IntPush( vAbs, Entry );
    assert( Vec_IntEntry(vAbs, Vec_IntEntry(vAbs,0)+1) == Vec_IntSize(vAbs) );
    return vAbs;
}

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

  Synopsis    [Detects how many frames are completed.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Vec_Int_t * Vta_ManDeriveAbsAll( Vec_Int_t * p, int nWords )
{
    Vec_Int_t * vRes;
    unsigned * pThis;
    int i, w, nObjs = Vec_IntSize(p) / nWords;
    assert( Vec_IntSize(p) % nWords == 0 );
    vRes = Vec_IntAlloc( nObjs );
    for ( i = 0; i < nObjs; i++ )
    {
        pThis = (unsigned *)Vec_IntEntryP( p, nWords * i );
        for ( w = 0; w < nWords; w++ )
            if ( pThis[w] )
                break;
        Vec_IntPush( vRes, (int)(w < nWords) );
    }
    return vRes;
}

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

  Synopsis    [Collect nodes/flops involved in different timeframes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Vec_IntDoubleWidth( Vec_Int_t * p, int nWords )
{
    int * pArray = ABC_CALLOC( int, Vec_IntSize(p) * 2 );
    int i, w, nObjs = Vec_IntSize(p) / nWords;
    assert( Vec_IntSize(p) % nWords == 0 );
    for ( i = 0; i < nObjs; i++ )
        for ( w = 0; w < nWords; w++ )
            pArray[2 * nWords * i + w] = p->pArray[nWords * i + w];
    ABC_FREE( p->pArray );
    p->pArray = pArray;
    p->nSize *= 2;
    p->nCap = p->nSize;
    return 2 * nWords;
}



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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Vga_ManHash( int iObj, int iFrame, int nBins )
{
    return ((unsigned)((iObj + iFrame)*(iObj + iFrame + 1))) % nBins;
}
static inline int * Vga_ManLookup( Vta_Man_t * p, int iObj, int iFrame )
{
    Vta_Obj_t * pThis;
    int * pPlace = p->pBins + Vga_ManHash( iObj, iFrame, p->nBins );
    for ( pThis = Vta_ManObj(p, *pPlace); 
          pThis;  pPlace = &pThis->iNext, 
          pThis = Vta_ManObj(p, *pPlace) )
        if ( pThis->iObj == iObj && pThis->iFrame == iFrame )
            break;
    return pPlace;
}
static inline Vta_Obj_t * Vga_ManFind( Vta_Man_t * p, int iObj, int iFrame )
{
    int * pPlace = Vga_ManLookup( p, iObj, iFrame );
    return Vta_ManObj(p, *pPlace);
}
static inline Vta_Obj_t * Vga_ManFindOrAdd( Vta_Man_t * p, int iObj, int iFrame )
{
    Vta_Obj_t * pThis;
    int i, * pPlace;
    assert( iObj >= 0 && iFrame >= -1 );
    if ( p->nObjs == p->nObjsAlloc )
    {
        // resize objects
        p->pObjs = ABC_REALLOC( Vta_Obj_t, p->pObjs, 2 * p->nObjsAlloc );
        memset( p->pObjs + p->nObjsAlloc, 0, p->nObjsAlloc * sizeof(Vta_Obj_t) );
        p->nObjsAlloc *= 2;
        // rehash entries in the table
        ABC_FREE( p->pBins );
        p->nBins = Abc_PrimeCudd( 2 * p->nBins );
        p->pBins = ABC_CALLOC( int, p->nBins );
        Vta_ManForEachObj( p, pThis, i )
        {
            pThis->iNext = 0;
            pPlace = Vga_ManLookup( p, pThis->iObj, pThis->iFrame );
            assert( *pPlace == 0 );
            *pPlace = i;
        }
    }
    pPlace = Vga_ManLookup( p, iObj, iFrame );
    if ( *pPlace )
        return Vta_ManObj(p, *pPlace);
    *pPlace = p->nObjs++;
    pThis = Vta_ManObj(p, *pPlace);
    pThis->iObj   = iObj;
    pThis->iFrame = iFrame;
    return pThis;
}
static inline void Vga_ManDelete( Vta_Man_t * p, int iObj, int iFrame )
{
    int * pPlace = Vga_ManLookup( p, iObj, iFrame );
    Vta_Obj_t * pThis = Vta_ManObj(p, *pPlace);
    assert( pThis != NULL );
    *pPlace = pThis->iNext;
    pThis->iNext = -1;
}


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

  Synopsis    [Derives counter-example using current assignments.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Cex_t * Vga_ManDeriveCex( Vta_Man_t * p )
{
    Abc_Cex_t * pCex;
    Vta_Obj_t * pThis;
    Gia_Obj_t * pObj;
    int i;
    pCex = Abc_CexAlloc( Gia_ManRegNum(p->pGia), Gia_ManPiNum(p->pGia), p->pPars->iFrame+1 );
    pCex->iPo = 0;
    pCex->iFrame = p->pPars->iFrame;
    Vta_ManForEachObjObj( p, pThis, pObj, i )
        if ( Gia_ObjIsPi(p->pGia, pObj) && sat_solver2_var_value(p->pSat, Vta_ObjId(p, pThis)) )
            Abc_InfoSetBit( pCex->pData, pCex->nRegs + pThis->iFrame * pCex->nPis + Gia_ObjCioId(pObj) );
    return pCex;
}

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

  Synopsis    [Remaps core into frame/node pairs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vta_ManUnsatCoreRemap( Vta_Man_t * p, Vec_Int_t * vCore )
{
    Vta_Obj_t * pThis;
    int i, Entry;
    Vec_IntForEachEntry( vCore, Entry, i )
    {
        pThis = Vta_ManObj( p, Entry );
        Entry = (pThis->iFrame << p->nObjBits) | pThis->iObj;
        Vec_IntWriteEntry( vCore, i, Entry );
    }
}

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

  Synopsis    [Compares two objects by their distance.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Vta_ManComputeDepthIncrease( Vta_Obj_t ** pp1, Vta_Obj_t ** pp2 )
{
    int Diff = (*pp1)->Prio - (*pp2)->Prio;
    if ( Diff < 0 )
        return -1;
    if ( Diff > 0 ) 
        return 1;
    Diff = (*pp1) - (*pp2);
    if ( Diff < 0 )
        return -1;
    if ( Diff > 0 ) 
        return 1;
    return 0; 
}

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

  Synopsis    [Returns 1 if the object is already used.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Vta_ManObjIsUsed( Vta_Man_t * p, int iObj )
{
    int i;
    unsigned * pInfo = (unsigned *)Vec_IntEntryP( p->vSeens, p->nWords * iObj );
    for ( i = 0; i < p->nWords; i++ )
        if ( pInfo[i] )
            return 1;
    return 0;
}

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

  Synopsis    [Finds predecessors of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Vta_ObjPreds( Vta_Man_t * p, Vta_Obj_t * pThis, Gia_Obj_t * pObj, Vta_Obj_t ** ppThis0, Vta_Obj_t ** ppThis1 )
{
    *ppThis0 = NULL;
    *ppThis1 = NULL;
//    if ( !pThis->fAdded )
//        return;
    assert( !Gia_ObjIsPi(p->pGia, pObj) );
    if ( Gia_ObjIsConst0(pObj) || (Gia_ObjIsCi(pObj) && pThis->iFrame == 0) )
        return;
    if ( Gia_ObjIsAnd(pObj) )
    {
        *ppThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame );
        *ppThis1 = Vga_ManFind( p, Gia_ObjFaninId1p(p->pGia, pObj), pThis->iFrame );
//        assert( *ppThis0 && *ppThis1 );
        return;
    }
    assert( Gia_ObjIsRo(p->pGia, pObj) && pThis->iFrame > 0 );
    pObj = Gia_ObjRoToRi( p->pGia, pObj );
    *ppThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame-1 );
//    assert( *ppThis0 );
}

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

  Synopsis    [Collect const/PI/RO/AND in a topological order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vta_ManCollectNodes_rec( Vta_Man_t * p, Vta_Obj_t * pThis, Vec_Int_t * vOrder )
{
    Gia_Obj_t * pObj;
    Vta_Obj_t * pThis0, * pThis1;
    if ( pThis->fVisit )
        return;
    pThis->fVisit = 1;
    pObj = Gia_ManObj( p->pGia, pThis->iObj );
    if ( pThis->fAdded )
    {
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        if ( pThis0 ) Vta_ManCollectNodes_rec( p, pThis0, vOrder );
        if ( pThis1 ) Vta_ManCollectNodes_rec( p, pThis1, vOrder );
    }
    Vec_IntPush( vOrder, Vta_ObjId(p, pThis) );
}
Vec_Int_t * Vta_ManCollectNodes( Vta_Man_t * p, int f )
{
    Vta_Obj_t * pThis;
    Gia_Obj_t * pObj;
    Vec_IntClear( p->vOrder );
    pObj = Gia_ManPo( p->pGia, 0 );
    pThis = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), f );
    assert( pThis != NULL );
    assert( !pThis->fVisit );
    Vta_ManCollectNodes_rec( p, pThis, p->vOrder );
    assert( pThis->fVisit );
    return p->vOrder;
}

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

  Synopsis    [Refines abstraction.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vta_ManSatVerify( Vta_Man_t * p )
{
    Vta_Obj_t * pThis, * pThis0, * pThis1;
    Gia_Obj_t * pObj;
    int i;
    Vta_ManForEachObj( p, pThis, i )
        pThis->Value = (sat_solver2_var_value(p->pSat, i) ? VTA_VAR1 : VTA_VAR0);
    Vta_ManForEachObjObj( p, pThis, pObj, i )
    {
        if ( !pThis->fAdded )
            continue;
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        if ( Gia_ObjIsAnd(pObj) )
        {
            if ( pThis->Value == VTA_VAR1 )
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs1(pThis1, Gia_ObjFaninC1(pObj)) );
            else if ( pThis->Value == VTA_VAR0 )
                assert( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) || Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) );
            else assert( 0 );
        }
        else if ( Gia_ObjIsRo(p->pGia, pObj) )
        {
            pObj = Gia_ObjRoToRi( p->pGia, pObj );
            if ( pThis->iFrame == 0 )
                assert( pThis->Value == VTA_VAR0 );
            else if ( pThis->Value == VTA_VAR0 )
                assert( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) );
            else if ( pThis->Value == VTA_VAR1 )
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) );
            else assert( 0 );
        }
    }
}

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

  Synopsis    [Refines abstraction.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vta_ManProfileAddition( Vta_Man_t * p, Vec_Int_t * vTermsToAdd )
{
    Vta_Obj_t * pThis;
    Gia_Obj_t * pObj;
    // profile the added ones
    int i, * pCounters = ABC_CALLOC( int, p->pPars->iFrame+1 );
    Vta_ManForEachObjObjVec( vTermsToAdd, p, pThis, pObj, i )
        pCounters[pThis->iFrame]++;
    for ( i = 0; i <= p->pPars->iFrame; i++ )
        printf( "%2d", pCounters[i] );
    printf( "***\n" );
}

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

  Synopsis    [Refines abstraction.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Cex_t * Vta_ManRefineAbstraction( Vta_Man_t * p, int f )
{
    int fVerify = 0;
    Abc_Cex_t * pCex = NULL;
    Vec_Int_t * vOrder, * vTermsToAdd;
    Vec_Ptr_t * vTermsUsed, * vTermsUnused;
    Vta_Obj_t * pThis, * pThis0, * pThis1, * pTop;
    Gia_Obj_t * pObj;
    int i, Counter;

    if ( fVerify )
    Vta_ManSatVerify( p );

    // collect nodes in a topological order
    vOrder = Vta_ManCollectNodes( p, f );
    Vta_ManForEachObjObjVec( vOrder, p, pThis, pObj, i )
    {
        pThis->Prio = VTA_LARGE;
        pThis->Value = sat_solver2_var_value(p->pSat, Vta_ObjId(p, pThis)) ? VTA_VAR1 : VTA_VAR0;
        pThis->fVisit = 0;
    }

    // verify
    if ( fVerify )
    Vta_ManForEachObjObjVec( vOrder, p, pThis, pObj, i )
    {
        if ( !pThis->fAdded )
            continue;
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        if ( Gia_ObjIsAnd(pObj) )
        {
            if ( pThis->Value == VTA_VAR1 )
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs1(pThis1, Gia_ObjFaninC1(pObj)) );
            else if ( pThis->Value == VTA_VAR0 )
                assert( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) || Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) );
            else assert( 0 );
        }
        else if ( Gia_ObjIsRo(p->pGia, pObj) )
        {
            pObj = Gia_ObjRoToRi( p->pGia, pObj );
            if ( pThis->iFrame == 0 )
                assert( pThis->Value == VTA_VAR0 );
            else if ( pThis->Value == VTA_VAR0 )
                assert( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) );
            else if ( pThis->Value == VTA_VAR1 )
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) );
            else assert( 0 );
        }
    }

    // compute distance in reverse order
    pThis = Vta_ManObj( p, Vec_IntEntryLast(vOrder) );
    pThis->Prio  = 1;
    // collect used and unused terms
    vTermsUsed   = Vec_PtrAlloc( 1015 );
    vTermsUnused = Vec_PtrAlloc( 1016 );
    Vta_ManForEachObjObjVecReverse( vOrder, p, pThis, pObj, i )
    {
        // there is no unreachable states
        assert( pThis->Prio < VTA_LARGE );
        // skip constants and PIs
        if ( Gia_ObjIsConst0(pObj) || Gia_ObjIsPi(p->pGia, pObj) )
        {
            pThis->Prio = 0; // set highest priority
            continue;
        }
        // collect terminals
        assert( Gia_ObjIsAnd(pObj) || Gia_ObjIsRo(p->pGia, pObj) );
        if ( !pThis->fAdded )
        {
            assert( pThis->Prio > 0 );
            if ( Vta_ManObjIsUsed(p, pThis->iObj) )
                Vec_PtrPush( vTermsUsed, pThis );
            else
                Vec_PtrPush( vTermsUnused, pThis );
            continue;
        }
        // propagate
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        if ( pThis0 ) 
            pThis0->Prio = Abc_MinInt( pThis0->Prio, pThis->Prio + 1 );
        if ( pThis1 ) 
            pThis1->Prio = Abc_MinInt( pThis1->Prio, pThis->Prio + 1 );
    }

/*
    Vta_ManForEachObjObjVecReverse( vOrder, p, pThis, pObj, i )
        if ( pThis->Prio > 0 )
            pThis->Prio = 10;
*/
/*
    // update priorities according to reconvergence counters
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUsed, pThis, i )
    {
        Vta_Obj_t * pThis0, * pThis1;
        Gia_Obj_t * pObj = Gia_ManObj( p->pGia, pThis->iObj );
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        pThis->Prio += 10000000;
        if ( pThis0 )
            pThis->Prio -= 1000000 * pThis0->fAdded;
        if ( pThis1 )
            pThis->Prio -= 1000000 * pThis1->fAdded;
    }
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUnused, pThis, i )
    {
        Vta_Obj_t * pThis0, * pThis1;
        Gia_Obj_t * pObj = Gia_ManObj( p->pGia, pThis->iObj );
        Vta_ObjPreds( p, pThis, pObj, &pThis0, &pThis1 );
        pThis->Prio += 10000000;
        if ( pThis0 )
            pThis->Prio -= 1000000 * pThis0->fAdded;
        if ( pThis1 )
            pThis->Prio -= 1000000 * pThis1->fAdded;
    }
*/


    // update priorities according to reconvergence counters
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUsed, pThis, i )
        pThis->Prio = pThis->iObj;
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUnused, pThis, i )
        pThis->Prio = pThis->iObj;


    // objects with equal distance should receive priority based on number
    // those objects whose prototypes have been added in other timeframes
    // should have higher priority than the current object
    Vec_PtrSort( vTermsUsed,   (int (*)(void))Vta_ManComputeDepthIncrease );
    Vec_PtrSort( vTermsUnused, (int (*)(void))Vta_ManComputeDepthIncrease );
    if ( Vec_PtrSize(vTermsUsed) > 1 )
    {
        pThis0 = (Vta_Obj_t *)Vec_PtrEntry(vTermsUsed, 0);
        pThis1 = (Vta_Obj_t *)Vec_PtrEntryLast(vTermsUsed);
        assert( pThis0->Prio <= pThis1->Prio );
    }
    // assign the priority based on these orders
    Counter = 1;
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUsed, pThis, i )
        pThis->Prio = Counter++;
    Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUnused, pThis, i )
        pThis->Prio = Counter++;
//    Abc_Print( 1, "Used %d  Unused %d\n", Vec_PtrSize(vTermsUsed), Vec_PtrSize(vTermsUnused) );


    // propagate in the direct order
    Vta_ManForEachObjObjVec( vOrder, p, pThis, pObj, i )
    {
        assert( pThis->fVisit == 0 );
        assert( pThis->Prio < VTA_LARGE );
        // skip terminal objects
        if ( !pThis->fAdded )
            continue;
        // assumes that values are assigned!!!
        assert( pThis->Value != 0 );
        // propagate
        if ( Gia_ObjIsAnd(pObj) )
        {
            pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame );
            pThis1 = Vga_ManFind( p, Gia_ObjFaninId1p(p->pGia, pObj), pThis->iFrame );
            assert( pThis0 && pThis1 );
            if ( pThis->Value == VTA_VAR1 )
            {
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs1(pThis1, Gia_ObjFaninC1(pObj)) );
                pThis->Prio = Abc_MaxInt( pThis0->Prio, pThis1->Prio );
            }
            else if ( pThis->Value == VTA_VAR0 )
            {
                if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) )
                    pThis->Prio = Abc_MinInt( pThis0->Prio, pThis1->Prio ); // choice!!!
                else if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) )
                    pThis->Prio = pThis0->Prio;
                else if ( Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) )
                    pThis->Prio = pThis1->Prio;
                else assert( 0 );
            }
            else assert( 0 );
        }
        else if ( Gia_ObjIsRo(p->pGia, pObj) )
        {
            if ( pThis->iFrame > 0 )
            {
                pObj = Gia_ObjRoToRi( p->pGia, pObj );
                pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame-1 );
                assert( pThis0 );
                pThis->Prio = pThis0->Prio;
            }
            else
                pThis->Prio = 0;
        }
        else if ( Gia_ObjIsConst0(pObj) )
            pThis->Prio = 0;
        else
            assert( 0 );
    }

    // select important values
    pTop = Vta_ManObj( p, Vec_IntEntryLast(vOrder) );
    pTop->fVisit = 1;
    vTermsToAdd = Vec_IntAlloc( 100 );
    Vta_ManForEachObjObjVecReverse( vOrder, p, pThis, pObj, i )
    {
        if ( !pThis->fVisit )
            continue;
        pThis->fVisit = 0;
        assert( pThis->Prio >= 0 && pThis->Prio <= pTop->Prio );
        // skip terminal objects
        if ( !pThis->fAdded )
        {
            assert( Gia_ObjIsAnd(pObj) || Gia_ObjIsRo(p->pGia, pObj) || Gia_ObjIsConst0(pObj) || Gia_ObjIsPi(p->pGia, pObj) );
            Vec_IntPush( vTermsToAdd, Vta_ObjId(p, pThis) );
            continue;
        }
        // assumes that values are assigned!!!
        assert( pThis->Value != 0 );
        // propagate
        if ( Gia_ObjIsAnd(pObj) )
        {
            pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame );
            pThis1 = Vga_ManFind( p, Gia_ObjFaninId1p(p->pGia, pObj), pThis->iFrame );
            assert( pThis0 && pThis1 );
            if ( pThis->Value == VTA_VAR1 )
            {
                assert( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs1(pThis1, Gia_ObjFaninC1(pObj)) );
                assert( pThis0->Prio <= pThis->Prio );
                assert( pThis1->Prio <= pThis->Prio );
                pThis0->fVisit = 1;
                pThis1->fVisit = 1;
            }
            else if ( pThis->Value == VTA_VAR0 )
            {
                if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) )
                {
                    if ( pThis0->fVisit )
                    {
                    }
                    else if ( pThis1->fVisit )
                    {
                    }
                    else if ( pThis0->Prio <= pThis1->Prio ) // choice!!!
                    {
                        pThis0->fVisit = 1;
                        assert( pThis0->Prio == pThis->Prio );
                    }
                    else
                    {
                        pThis1->fVisit = 1;
                        assert( pThis1->Prio == pThis->Prio );
                    }
                }
                else if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) )
                {
                    pThis0->fVisit = 1;
                    assert( pThis0->Prio == pThis->Prio );
                }
                else if ( Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) )
                {
                    pThis1->fVisit = 1;
                    assert( pThis1->Prio == pThis->Prio );
                }
                else assert( 0 );
            }
            else assert( 0 );
        }
        else if ( Gia_ObjIsRo(p->pGia, pObj) )
        {
            if ( pThis->iFrame > 0 )
            {
                pObj = Gia_ObjRoToRi( p->pGia, pObj );
                pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame-1 );
                assert( pThis0 );
                pThis0->fVisit = 1;
                assert( pThis0->Prio == pThis->Prio );
            }
        }
        else if ( !Gia_ObjIsConst0(pObj) )
            assert( 0 );
    }

    if ( p->pPars->fAddLayer )
    {
        // mark those currently included
        Vta_ManForEachObjVec( vTermsToAdd, p, pThis, i )
        {
            assert( pThis->fVisit == 0 );
            pThis->fVisit = 1;
        }
        // add used terms, which have close relationship
        Counter = Vec_IntSize(vTermsToAdd);
        Vec_PtrForEachEntry( Vta_Obj_t *, vTermsUsed, pThis, i )
        {
            if ( pThis->fVisit )
                continue;
    //        Vta_ObjPreds( p, pThis, Gia_ManObj(p->pGia, pThis->iObj), &pThis0, &pThis1 );
    //        if ( (pThis0 && (pThis0->fAdded || pThis0->fVisit)) || (pThis1 && (pThis1->fAdded || pThis1->fVisit)) )
                Vec_IntPush( vTermsToAdd, Vta_ObjId(p, pThis) );
        }
        // remove those currenty included
        Vta_ManForEachObjVec( vTermsToAdd, p, pThis, i )
            pThis->fVisit = 0;
    }
//    printf( "\n%d -> %d\n", Counter, Vec_IntSize(vTermsToAdd) );
//Vec_IntReverseOrder( vTermsToAdd );
//Vec_IntSort( vTermsToAdd, 1 );


    // cleanup
    Vec_PtrFree( vTermsUsed );
    Vec_PtrFree( vTermsUnused );


    if ( fVerify )
    {
    // verify
    Vta_ManForEachObjVec( vOrder, p, pThis, i )
        pThis->Value = VTA_VARX;
    Vta_ManForEachObjVec( vTermsToAdd, p, pThis, i )
    {
        assert( !pThis->fAdded );
        pThis->Value = sat_solver2_var_value(p->pSat, Vta_ObjId(p, pThis)) ? VTA_VAR1 : VTA_VAR0;
    }
    // simulate 
    Vta_ManForEachObjObjVec( vOrder, p, pThis, pObj, i )
    {
        assert( pThis->fVisit == 0 );
        if ( !pThis->fAdded )
            continue;
        if ( Gia_ObjIsAnd(pObj) )
        {
            pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame );
            pThis1 = Vga_ManFind( p, Gia_ObjFaninId1p(p->pGia, pObj), pThis->iFrame );
            assert( pThis0 && pThis1 );
            if ( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) && Vta_ValIs1(pThis1, Gia_ObjFaninC1(pObj)) )
                pThis->Value = VTA_VAR1;
            else if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) || Vta_ValIs0(pThis1, Gia_ObjFaninC1(pObj)) )
                pThis->Value = VTA_VAR0;
            else
                pThis->Value = VTA_VARX;
        }
        else if ( Gia_ObjIsRo(p->pGia, pObj) )
        {
            if ( pThis->iFrame > 0 )
            {
                pObj = Gia_ObjRoToRi( p->pGia, pObj );
                pThis0 = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), pThis->iFrame-1 );
                assert( pThis0 );
                if ( Vta_ValIs0(pThis0, Gia_ObjFaninC0(pObj)) )
                    pThis->Value = VTA_VAR0;
                else if ( Vta_ValIs1(pThis0, Gia_ObjFaninC0(pObj)) )
                    pThis->Value = VTA_VAR1;
                else
                    pThis->Value = VTA_VARX;
            }
            else
            {
                pThis->Value = VTA_VAR0;
            }
        }
        else if ( Gia_ObjIsConst0(pObj) )
        {
            pThis->Value = VTA_VAR0;
        }
        else assert( 0 );
        // double check the solver
        assert( pThis->Value == VTA_VARX || (int)pThis->Value == (sat_solver2_var_value(p->pSat, Vta_ObjId(p, pThis)) ? VTA_VAR1 : VTA_VAR0) );
    }

    // check the output
    if ( !Vta_ValIs1(pTop, Gia_ObjFaninC0(Gia_ManPo(p->pGia, 0))) )
        Abc_Print( 1, "Vta_ManRefineAbstraction(): Terminary simulation verification failed!\n" );
//    else
//        Abc_Print( 1, "Verification OK.\n" );
    }


    // produce true counter-example
    if ( pTop->Prio == 0 )
        pCex = Vga_ManDeriveCex( p );
    else
    {
//       Vta_ManProfileAddition( p, vTermsToAdd );

        Vta_ManForEachObjObjVec( vTermsToAdd, p, pThis, pObj, i )
            if ( !Gia_ObjIsPi(p->pGia, pObj) )
                Vga_ManAddClausesOne( p, pThis->iObj, pThis->iFrame );
        sat_solver2_simplify( p->pSat );
    }
    p->nObjAdded += Vec_IntSize(vTermsToAdd);
    Vec_IntFree( vTermsToAdd );
    return pCex;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vta_Man_t * Vga_ManStart( Gia_Man_t * pGia, Gia_ParVta_t * pPars )
{
    Vta_Man_t * p;
    p = ABC_CALLOC( Vta_Man_t, 1 );
    p->pGia        = pGia;
    p->pPars       = pPars;
    // internal data
    p->nObjsAlloc  = (1 << 18);
    p->pObjs       = ABC_CALLOC( Vta_Obj_t, p->nObjsAlloc );
    p->nObjs       = 1;
    p->nBins       = Abc_PrimeCudd( 2*p->nObjsAlloc );
    p->pBins       = ABC_CALLOC( int, p->nBins );
    p->vOrder      = Vec_IntAlloc( 1013 );
    // abstraction
    p->nObjBits    = Abc_Base2Log( Gia_ManObjNum(pGia) );
    p->nObjMask    = (1 << p->nObjBits) - 1;
    assert( Gia_ManObjNum(pGia) <= (int)p->nObjMask );
    p->nWords      = 1;
    p->vSeens      = Vec_IntStart( Gia_ManObjNum(pGia) * p->nWords );
    p->vSeenGla    = Vec_BitStart( Gia_ManObjNum(pGia) );
    p->nSeenGla    = 1;
    p->nSeenAll    = 1;
    // other data
    p->vCla2Var    = Vec_IntAlloc( 1000 );  Vec_IntPush( p->vCla2Var, -1 );
    p->vCores      = Vec_PtrAlloc( 100 );
    p->pSat        = sat_solver2_new();
//    p->pSat->fVerbose = p->pPars->fVerbose;
    sat_solver2_set_learntmax( p->pSat, pPars->nLearntMax );
    // start the abstraction
    assert( pGia->vObjClasses != NULL );
    p->vFrames = Gia_VtaAbsToFrames( pGia->vObjClasses );
    p->vAddedNew   = Vec_IntAlloc( 1000 );
    return p;
}

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

  Synopsis    [Delete manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vga_ManStop( Vta_Man_t * p )
{
//    if ( p->pPars->fVerbose )
        Abc_Print( 1, "SAT solver:  Vars = %d  Clauses = %d  Confs = %d  Cexes = %d  ObjsAdded = %d\n", 
            sat_solver2_nvars(p->pSat), sat_solver2_nclauses(p->pSat), sat_solver2_nconflicts(p->pSat), p->nCexes, p->nObjAdded );
    Vec_VecFreeP( (Vec_Vec_t **)&p->vCores );
    Vec_VecFreeP( (Vec_Vec_t **)&p->vFrames );
    Vec_BitFreeP( &p->vSeenGla );
    Vec_IntFreeP( &p->vSeens );
    Vec_IntFreeP( &p->vOrder );
    Vec_IntFreeP( &p->vCla2Var );
    Vec_IntFreeP( &p->vAddedNew );
    sat_solver2_delete( p->pSat );
    ABC_FREE( p->pBins );
    ABC_FREE( p->pObjs );
    ABC_FREE( p );
}

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

  Synopsis    [Returns the output literal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Vga_ManGetOutLit( Vta_Man_t * p, int f )
{
    Gia_Obj_t * pObj = Gia_ManPo(p->pGia, 0);
    Vta_Obj_t * pThis = Vga_ManFind( p, Gia_ObjFaninId0p(p->pGia, pObj), f );
    assert( pThis != NULL && pThis->fAdded );
    if ( f == 0 && Gia_ObjIsRo(p->pGia, Gia_ObjFanin0(pObj)) && !Gia_ObjFaninC0(pObj) )
        return -Vta_ObjId(p, pThis);
    return Abc_Var2Lit( Vta_ObjId(p, pThis), Gia_ObjFaninC0(pObj) );
}

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

  Synopsis    [Finds the set of clauses involved in the UNSAT core.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Vta_ManUnsatCore( int iLit, Vec_Int_t * vCla2Var, sat_solver2 * pSat, int nConfMax, int fVerbose, int * piRetValue, int * pnConfls )
{
    Vec_Int_t * vPres;
    Vec_Int_t * vCore;
    int k, i, Entry, RetValue;
    clock_t clk = clock();
    int nConfPrev = pSat->stats.conflicts;
    if ( piRetValue )
        *piRetValue = 1;
    // consider special case when PO points to the flop
    // this leads to immediate conflict in the first timeframe
    if ( iLit < 0 )
    {
        vCore = Vec_IntAlloc( 1 );
        Vec_IntPush( vCore, -iLit );
        return vCore;
    }
    // solve the problem
    RetValue = sat_solver2_solve( pSat, &iLit, &iLit+1, (ABC_INT64_T)nConfMax, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 );
    if ( pnConfls )
        *pnConfls = (int)pSat->stats.conflicts - nConfPrev;
    if ( RetValue == l_Undef )
    {
        if ( piRetValue )
            *piRetValue = -1;
        return NULL;
    }
    if ( RetValue == l_True )
    {
        if ( piRetValue )
            *piRetValue = 0;
        return NULL;
    }
    if ( fVerbose )
    {
//        Abc_Print( 1, "%6d", (int)pSat->stats.conflicts - nConfPrev );
//        Abc_Print( 1, "UNSAT after %7d conflicts.      ", pSat->stats.conflicts );
//        Abc_PrintTime( 1, "Time", clock() - clk );
    }
    assert( RetValue == l_False );

    // derive the UNSAT core
    clk = clock();
    vCore = (Vec_Int_t *)Sat_ProofCore( pSat );
    if ( fVerbose )
    {
//        Abc_Print( 1, "Core is %8d clauses (out of %8d).   ", Vec_IntSize(vCore), sat_solver2_nclauses(pSat) );
//        Abc_PrintTime( 1, "Time", clock() - clk );
    }

    // remap core into variables
    clk = clock();
    vPres = Vec_IntStart( sat_solver2_nvars(pSat) );
    k = 0; 
    Vec_IntForEachEntry( vCore, Entry, i )
    {
        Entry = Vec_IntEntry(vCla2Var, Entry);
        if ( Vec_IntEntry(vPres, Entry) )
            continue;
        Vec_IntWriteEntry( vPres, Entry, 1 );
        Vec_IntWriteEntry( vCore, k++, Entry );
    }
    Vec_IntShrink( vCore, k );
    Vec_IntFree( vPres );
    if ( fVerbose )
    {
//        Abc_Print( 1, "Core is %8d vars    (out of %8d).   ", Vec_IntSize(vCore), sat_solver2_nvars(pSat) );
//        Abc_PrintTime( 1, "Time", clock() - clk );
    }
    return vCore;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Vta_ManAbsPrintFrame( Vta_Man_t * p, Vec_Int_t * vCore, int nFrames, int nConfls, int nCexes, clock_t Time, int fVerbose )
{
    unsigned * pInfo;
    int * pCountAll = NULL, * pCountUni = NULL;
    int i, k, iFrame, iObj, Entry, fChanges = 0;
    // print info about frames
    if ( vCore )
    {
        pCountAll = ABC_CALLOC( int, nFrames + 1 );
        pCountUni = ABC_CALLOC( int, nFrames + 1 );
        Vec_IntForEachEntry( vCore, Entry, i )
        {
            iObj   = (Entry &  p->nObjMask);
            iFrame = (Entry >> p->nObjBits);
            assert( iFrame < nFrames );
            pInfo  = (unsigned *)Vec_IntEntryP( p->vSeens, p->nWords * iObj );
            if ( !Abc_InfoHasBit(pInfo, iFrame) )
            {
                Abc_InfoSetBit( pInfo, iFrame );
                pCountUni[iFrame+1]++;
                pCountUni[0]++;
                p->nSeenAll++;
            }
            pCountAll[iFrame+1]++;
            pCountAll[0]++;
            if ( !Vec_BitEntry(p->vSeenGla, iObj) )
            {
                Vec_BitWriteEntry(p->vSeenGla, iObj, 1);
                p->nSeenGla++;
                fChanges = 1;
            }
        }
    }
    if ( !fVerbose )
    {
        ABC_FREE( pCountAll );
        ABC_FREE( pCountUni );
        return fChanges;
    }

    if ( Abc_FrameIsBatchMode() && !vCore )
        return fChanges;

//    Abc_Print( 1, "%5d%5d", pCountAll[0], pCountUni[0] ); 
    Abc_Print( 1, "%3d :", nFrames-1 );
    Abc_Print( 1, "%4d", Abc_MinInt(100, 100 * p->nSeenGla / (Gia_ManRegNum(p->pGia) + Gia_ManAndNum(p->pGia) + 1)) ); 
    Abc_Print( 1, "%6d", p->nSeenGla ); 
    Abc_Print( 1, "%4d", Abc_MinInt(100, 100 * p->nSeenAll / (p->nSeenGla * nFrames)) ); 
    Abc_Print( 1, "%8d", nConfls );
    if ( nCexes == 0 )
        Abc_Print( 1, "%5c", '-' ); 
    else
        Abc_Print( 1, "%5d", nCexes ); 
    Abc_Print( 1, " %9d", sat_solver2_nvars(p->pSat) ); 
    if ( vCore == NULL )
    {
        Abc_Print( 1, "    ..." ); 
        for ( k = 0; k < 7; k++ )
            Abc_Print( 1, "     " );
        Abc_Print( 1, "%9.2f sec", 1.0*Time/CLOCKS_PER_SEC );
        Abc_Print( 1, "%5.1f Gb", sat_solver2_memory_proof( p->pSat ) / (1<<30) );
        Abc_Print( 1, "\r" );
    }
    else
    {
        Abc_Print( 1, "%7d", pCountAll[0] ); 
        if ( nFrames > 7 )
        {
            for ( k = 0; k < 3; k++ )
                Abc_Print( 1, "%5d", pCountAll[k+1] ); 
            Abc_Print( 1, "  ..." );
            for ( k = nFrames-3; k < nFrames; k++ )
                Abc_Print( 1, "%5d", pCountAll[k+1] ); 
        }
        else
        {
            for ( k = 0; k < nFrames; k++ )
                Abc_Print( 1, "%5d", pCountAll[k+1] ); 
            for ( k = nFrames; k < 7; k++ )
                Abc_Print( 1, "     " );
        }
        Abc_Print( 1, "%9.2f sec", 1.0*Time/CLOCKS_PER_SEC );
        Abc_Print( 1, "%5.1f Gb", sat_solver2_memory_proof( p->pSat ) / (1<<30) );
        Abc_Print( 1, "\n" );
    }
    fflush( stdout );

    if ( vCore )
    {
        ABC_FREE( pCountAll );
        ABC_FREE( pCountUni );
    }
    return fChanges;
}

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

  Synopsis    [Adds clauses to the solver.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vga_ManAddClausesOne( Vta_Man_t * p, int iObj, int iFrame )
{ 
    Vta_Obj_t * pThis0, * pThis1;
    Gia_Obj_t * pObj = Gia_ManObj( p->pGia, iObj );
    Vta_Obj_t * pThis = Vga_ManFindOrAdd( p, iObj, iFrame );
    int iThis0, iMainVar = Vta_ObjId(p, pThis);
    assert( pThis->iObj == iObj && pThis->iFrame == iFrame );
    if ( pThis->fAdded )
        return;
    pThis->fAdded = 1;
    Vec_IntPush( p->vAddedNew, iMainVar );
    if ( Gia_ObjIsAnd(pObj) )
    {
        pThis0 = Vga_ManFindOrAdd( p, Gia_ObjFaninId0p(p->pGia, pObj), iFrame );
        iThis0 = Vta_ObjId(p, pThis0);
        pThis1 = Vga_ManFindOrAdd( p, Gia_ObjFaninId1p(p->pGia, pObj), iFrame );
        sat_solver2_add_and( p->pSat, iMainVar, iThis0, Vta_ObjId(p, pThis1), 
            Gia_ObjFaninC0(pObj), Gia_ObjFaninC1(pObj), 0 );
        Vec_IntPush( p->vCla2Var, iMainVar );
        Vec_IntPush( p->vCla2Var, iMainVar );
        Vec_IntPush( p->vCla2Var, iMainVar );
    }
    else if ( Gia_ObjIsRo(p->pGia, pObj) )
    {
        if ( iFrame == 0 )
        {
            if ( p->pPars->fUseTermVars )
            {
                pThis0 = Vga_ManFindOrAdd( p, iObj, -1 );
                sat_solver2_add_constraint( p->pSat, iMainVar, Vta_ObjId(p, pThis0), 1, 0 );
                Vec_IntPush( p->vCla2Var, iMainVar );
                Vec_IntPush( p->vCla2Var, iMainVar );
            }
            else
            {
                sat_solver2_add_const( p->pSat, iMainVar, 1, 0 );
                Vec_IntPush( p->vCla2Var, iMainVar );
            }
        }
        else
        {
            pObj = Gia_ObjRoToRi( p->pGia, pObj );
            pThis0 = Vga_ManFindOrAdd( p, Gia_ObjFaninId0p(p->pGia, pObj), iFrame-1 );
            sat_solver2_add_buffer( p->pSat, iMainVar, Vta_ObjId(p, pThis0), Gia_ObjFaninC0(pObj), 0 );  
            Vec_IntPush( p->vCla2Var, iMainVar );
            Vec_IntPush( p->vCla2Var, iMainVar );
        }
    }
    else if ( Gia_ObjIsConst0(pObj) )
    {
        sat_solver2_add_const( p->pSat, iMainVar, 1, 0 );
        Vec_IntPush( p->vCla2Var, iMainVar );
    }
    else //if ( !Gia_ObjIsPi(p->pGia, pObj) )
        assert( 0 );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vga_ManLoadSlice( Vta_Man_t * p, Vec_Int_t * vOne, int Lift )
{
    int i, Entry;
    Vec_IntForEachEntry( vOne, Entry, i )
        Vga_ManAddClausesOne( p, Entry & p->nObjMask, (Entry >> p->nObjBits) + Lift );
    sat_solver2_simplify( p->pSat );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vga_ManPrintCore( Vta_Man_t * p, Vec_Int_t * vCore, int Lift )
{
    int i, Entry, iObj, iFrame;
    Vec_IntForEachEntry( vCore, Entry, i )
    {
        iObj   = (Entry &  p->nObjMask);
        iFrame = (Entry >> p->nObjBits);
        Abc_Print( 1, "%d*%d ", iObj, iFrame+Lift );
    }
    Abc_Print( 1, "\n" );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Vga_ManRollBack( Vta_Man_t * p, int nObjOld )
{
    Vta_Obj_t * pThis  = p->pObjs + nObjOld;
    Vta_Obj_t * pLimit = p->pObjs + p->nObjs;
    int i, Entry;
    for ( ; pThis < pLimit; pThis++ )
        Vga_ManDelete( p, pThis->iObj, pThis->iFrame );
    memset( p->pObjs + nObjOld, 0, sizeof(Vta_Obj_t) * (p->nObjs - nObjOld) );
    p->nObjs = nObjOld;
    Vec_IntForEachEntry( p->vAddedNew, Entry, i )
        if ( Entry < p->nObjs )
        {
            pThis = Vta_ManObj(p, Entry);
            assert( pThis->fAdded == 1 );
            pThis->fAdded = 0;
        }
}

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

  Synopsis    [Send abstracted model or send cancel.]

  Description [Counter-example will be sent automatically when &vta terminates.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_VtaSendAbsracted( Vta_Man_t * p, int fVerbose )
{
    extern int Gia_ManToBridgeAbsNetlist( FILE * pFile, Gia_Man_t * p );
    Gia_Man_t * pAbs;
    assert( Abc_FrameIsBridgeMode() );
//    if ( fVerbose )
//        Abc_Print( 1, "Sending abstracted model...\n" );
    // create obj classes
    Vec_IntFreeP( &p->pGia->vObjClasses );
    p->pGia->vObjClasses = Gia_VtaFramesToAbs( (Vec_Vec_t *)p->vCores );
    // create gate classes
    Vec_IntFreeP( &p->pGia->vGateClasses );
    p->pGia->vGateClasses = Gia_VtaConvertToGla( p->pGia, p->pGia->vObjClasses );
    Vec_IntFreeP( &p->pGia->vObjClasses );
    // create abstrated model
    pAbs = Gia_ManDupAbsGates( p->pGia, p->pGia->vGateClasses );
    Vec_IntFreeP( &p->pGia->vGateClasses );
    // send it out
    Gia_ManToBridgeAbsNetlist( stdout, pAbs );
    Gia_ManStop( pAbs );
}
void Gia_VtaSendCancel( Vta_Man_t * p, int fVerbose )
{
    extern int Gia_ManToBridgeBadAbs( FILE * pFile );
    assert( Abc_FrameIsBridgeMode() );
//    if ( fVerbose )
//        Abc_Print( 1, "Cancelling previously sent model...\n" );
    Gia_ManToBridgeBadAbs( stdout );
}

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

  Synopsis    [Send abstracted model or send cancel.]

  Description [Counter-example will be sent automatically when &vta terminates.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_VtaDumpAbsracted( Vta_Man_t * p, int fVerbose )
{
    char * pFileNameDef = "vabs.aig";
    char * pFileName = p->pPars->pFileVabs ? p->pPars->pFileVabs : pFileNameDef;
    Gia_Man_t * pAbs;
    if ( fVerbose )
        Abc_Print( 1, "Dumping abstracted model into file \"%s\"...\n", pFileName );
    // create obj classes
    Vec_IntFreeP( &p->pGia->vObjClasses );
    p->pGia->vObjClasses = Gia_VtaFramesToAbs( (Vec_Vec_t *)p->vCores );
    // create gate classes
    Vec_IntFreeP( &p->pGia->vGateClasses );
    p->pGia->vGateClasses = Gia_VtaConvertToGla( p->pGia, p->pGia->vObjClasses );
    Vec_IntFreeP( &p->pGia->vObjClasses );
    // create abstrated model
    pAbs = Gia_ManDupAbsGates( p->pGia, p->pGia->vGateClasses );
    Vec_IntFreeP( &p->pGia->vGateClasses );
    // send it out
    Gia_WriteAiger( pAbs, pFileName, 0, 0 );
    Gia_ManStop( pAbs );
}

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

  Synopsis    [Print memory report.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_VtaPrintMemory( Vta_Man_t * p )
{
    double memTot = 0;
    double memAig = Gia_ManObjNum(p->pGia) * sizeof(Gia_Obj_t);
    double memSat = sat_solver2_memory( p->pSat );
    double memPro = sat_solver2_memory_proof( p->pSat );
    double memMap = p->nObjsAlloc * sizeof(Vta_Obj_t) + p->nBins * sizeof(int);
    double memOth = sizeof(Vta_Man_t);
    memOth += Vec_IntCap(p->vOrder) * sizeof(int);
    memOth += Vec_VecMemoryInt( (Vec_Vec_t *)p->vFrames );
    memOth += Vec_BitCap(p->vSeenGla) * sizeof(int);
    memOth += Vec_IntCap(p->vCla2Var) * sizeof(int);
    memOth += Vec_VecMemoryInt( (Vec_Vec_t *)p->vCores );
    memOth += Vec_IntCap(p->vAddedNew) * sizeof(int);
    memTot = memAig + memSat + memPro + memMap + memOth;
    ABC_PRMP( "Memory: AIG  ", memAig, memTot );
    ABC_PRMP( "Memory: SAT  ", memSat, memTot );
    ABC_PRMP( "Memory: Proof", memPro, memTot );
    ABC_PRMP( "Memory: Map  ", memMap, memTot );
    ABC_PRMP( "Memory: Other", memOth, memTot );
    ABC_PRMP( "Memory: TOTAL", memTot, memTot );
}


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

  Synopsis    [Collect nodes/flops involved in different timeframes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_VtaPerformInt( Gia_Man_t * pAig, Gia_ParVta_t * pPars )
{
    Vta_Man_t * p;
    Vec_Int_t * vCore;
    Abc_Cex_t * pCex = NULL;
    int i, f, nConfls, Status, nObjOld, RetValue = -1, nCountNoChange = 0, fOneIsSent = 0;
    clock_t clk = clock(), clk2;
    // preconditions
    assert( Gia_ManPoNum(pAig) == 1 );
    assert( pPars->nFramesMax == 0 || pPars->nFramesStart <= pPars->nFramesMax );
    // compute intial abstraction
    if ( pAig->vObjClasses == NULL )
    {
        pAig->vObjClasses = Vec_IntAlloc( 5 );
        Vec_IntPush( pAig->vObjClasses, 1 );
        Vec_IntPush( pAig->vObjClasses, 3 );
        Vec_IntPush( pAig->vObjClasses, 4 );
        Vec_IntPush( pAig->vObjClasses, Gia_ObjFaninId0p(pAig, Gia_ManPo(pAig, 0)) );
    }
    // start the manager
    p = Vga_ManStart( pAig, pPars );
    // set runtime limit
    if ( p->pPars->nTimeOut )
        sat_solver2_set_runtime_limit( p->pSat, p->pPars->nTimeOut * CLOCKS_PER_SEC + clock() );
    // perform initial abstraction
    if ( p->pPars->fVerbose )
    {
        Abc_Print( 1, "Running variable-timeframe abstraction (VTA) with the following parameters:\n" );
        Abc_Print( 1, "FramePast = %d  FrameMax = %d  ConfMax = %d  LearnMax = %d  Timeout = %d  RatioMin = %d %%\n", 
            pPars->nFramesPast, pPars->nFramesMax, pPars->nConfLimit, pPars->nLearntMax, pPars->nTimeOut, pPars->nRatioMin );
        Abc_Print( 1, "Frame   %%   Abs   %%   Confl  Cex    SatVar   Core   F0   F1   F2  ...\n" );
    }
    assert( Vec_PtrSize(p->vFrames) > 0 );
    for ( f = i = 0; !p->pPars->nFramesMax || f < p->pPars->nFramesMax; f++ )
    {
        int nConflsBeg = sat_solver2_nconflicts(p->pSat);
        p->pPars->iFrame = f;
        // realloc storage for abstraction marks
        if ( f == p->nWords * 32 )
            p->nWords = Vec_IntDoubleWidth( p->vSeens, p->nWords );

        // create bookmark to be used for rollback
        nObjOld = p->nObjs;
        sat_solver2_bookmark( p->pSat );
        Vec_IntClear( p->vAddedNew );

        // load new timeframe
        Vga_ManAddClausesOne( p, 0, f );
        if ( f < Vec_PtrSize(p->vFrames) )
            Vga_ManLoadSlice( p, (Vec_Int_t *)Vec_PtrEntry(p->vFrames, f), 0 );
        else
        {
            for ( i = 1; i <= Abc_MinInt(p->pPars->nFramesPast, f); i++ )
                Vga_ManLoadSlice( p, (Vec_Int_t *)Vec_PtrEntry(p->vCores, f-i), i );
        }

        // iterate as long as there are counter-examples
        for ( i = 0; ; i++ )
        { 
            clk2 = clock();
            vCore = Vta_ManUnsatCore( Vga_ManGetOutLit(p, f), p->vCla2Var, p->pSat, pPars->nConfLimit, pPars->fVerbose, &Status, &nConfls );
            assert( (vCore != NULL) == (Status == 1) );
            if ( Status == -1 ) // resource limit is reached
            {
                Vga_ManRollBack( p, nObjOld );
                goto finish;
            }
            // check timeout
            if ( p->pSat->nRuntimeLimit && clock() > p->pSat->nRuntimeLimit )
            {
                Vga_ManRollBack( p, nObjOld );
                goto finish;
            }
            if ( vCore != NULL )
            {
                p->timeUnsat += clock() - clk2;
                break;
            }
            p->timeSat += clock() - clk2;
            assert( Status == 0 );
            p->nCexes++;
            // perform the refinement
            clk2 = clock();
            pCex = Vta_ManRefineAbstraction( p, f );
            p->timeCex += clock() - clk2;
            if ( pCex != NULL )
            {
                RetValue = 0;
                goto finish;
            }
            // print the result (do not count it towards change)
            Vta_ManAbsPrintFrame( p, NULL, f+1, sat_solver2_nconflicts(p->pSat)-nConflsBeg, i, clock() - clk, p->pPars->fVerbose );
        }
        assert( Status == 1 );
        // valid core is obtained
        Vta_ManUnsatCoreRemap( p, vCore );
        Vec_IntSort( vCore, 1 );
        // update the SAT solver
        sat_solver2_rollback( p->pSat );
        // update storage
        Vga_ManRollBack( p, nObjOld );
        Vec_IntShrink( p->vCla2Var, (int)p->pSat->stats.clauses+1 );
        // load this timeframe
        Vga_ManLoadSlice( p, vCore, 0 );
        Vec_IntFree( vCore );

        // run SAT solver
        clk2 = clock();
        vCore = Vta_ManUnsatCore( Vga_ManGetOutLit(p, f), p->vCla2Var, p->pSat, pPars->nConfLimit, p->pPars->fVerbose, &Status, &nConfls );
        p->timeUnsat += clock() - clk2;
        assert( (vCore != NULL) == (Status == 1) );
        if ( Status == -1 ) // resource limit is reached
            break;
        if ( Status == 0 )
        {
            Vta_ManSatVerify( p );
            // make sure, there was no initial abstraction (otherwise, it was invalid)
            assert( pAig->vObjClasses == NULL && f < p->pPars->nFramesStart );
            pCex = Vga_ManDeriveCex( p );
            RetValue = 0;
            break;
        }
        // add the core
        Vta_ManUnsatCoreRemap( p, vCore );
        // add in direct topological order
        Vec_IntSort( vCore, 1 );
        Vec_PtrPush( p->vCores, vCore );
        // print the result
        if ( Vta_ManAbsPrintFrame( p, vCore, f+1, sat_solver2_nconflicts(p->pSat)-nConflsBeg, i, clock() - clk, p->pPars->fVerbose ) )
        {
            // reset the counter of frames without change
            nCountNoChange = 1;
        }
        else if ( ++nCountNoChange == 2 ) // time to send
        {
            if ( Abc_FrameIsBridgeMode() )
            {
                // cancel old one if it was sent
                if ( fOneIsSent )
                    Gia_VtaSendCancel( p, pPars->fVerbose );
                // send new one 
                Gia_VtaSendAbsracted( p, pPars->fVerbose );
                fOneIsSent = 1;
            }
        }
        // dump the model
        if ( p->pPars->fDumpVabs && (f & 1) )
        {
            Abc_FrameSetCex( NULL );
            Abc_FrameSetNFrames( f+1 );
            Cmd_CommandExecute( Abc_FrameGetGlobalFrame(), "write_status vta.status" );
            Gia_VtaDumpAbsracted( p, pPars->fVerbose );
        }
        // check if the number of objects is below limit
        if ( p->nSeenGla >= Gia_ManCandNum(pAig) * (100-pPars->nRatioMin) / 100 )
        {
            Status = -1;
            break;
        }
    }
finish:
    // analize the results
    if ( pCex == NULL )
    {
        if ( Vec_PtrSize(p->vCores) == 0 )
            Abc_Print( 1, "Abstraction is not produced because first frame is not solved.  " );
        else
        {
            assert( Vec_PtrSize(p->vCores) > 0 );
            if ( pAig->vObjClasses != NULL )
                Abc_Print( 1, "Replacing the old abstraction by a new one.\n" );
            Vec_IntFreeP( &pAig->vObjClasses );
            pAig->vObjClasses = Gia_VtaFramesToAbs( (Vec_Vec_t *)p->vCores );
            if ( Status == -1 )
            {
                if ( p->pPars->nTimeOut && clock() >= p->pSat->nRuntimeLimit ) 
                    Abc_Print( 1, "SAT solver ran out of time at %d sec in frame %d.  ", p->pPars->nTimeOut, f );
                else if ( pPars->nConfLimit && sat_solver2_nconflicts(p->pSat) >= pPars->nConfLimit )
                    Abc_Print( 1, "SAT solver ran out of resources at %d conflicts in frame %d.  ", pPars->nConfLimit, f );
                else if ( p->nSeenGla >= Gia_ManCandNum(pAig) * (100-pPars->nRatioMin) / 100 )
                    Abc_Print( 1, "The ratio of abstracted objects is less than %d %% in frame %d.  ", pPars->nRatioMin, f );
                else
                    Abc_Print( 1, "Abstraction stopped for unknown reason in frame %d.  ", f );
            }
            else
            {
                p->pPars->iFrame++;
                Abc_Print( 1, "SAT solver completed %d frames and produced an abstraction.  ", f );
            }
        }
    }
    else
    {
        ABC_FREE( p->pGia->pCexSeq );
        p->pGia->pCexSeq = pCex;
        if ( !Gia_ManVerifyCex( p->pGia, pCex, 0 ) )
            Abc_Print( 1, "    Gia_VtaPerform(): CEX verification has failed!\n" );
        Abc_Print( 1, "Counter-example detected in frame %d.  ", f );
        p->pPars->iFrame = pCex->iFrame - 1;
    }
    Abc_PrintTime( 1, "Time", clock() - clk );

    if ( p->pPars->fVerbose )
    {
        p->timeOther = (clock() - clk) - p->timeUnsat - p->timeSat - p->timeCex;
        ABC_PRTP( "Runtime: Solver UNSAT", p->timeUnsat,  clock() - clk );
        ABC_PRTP( "Runtime: Solver SAT  ", p->timeSat,    clock() - clk );
        ABC_PRTP( "Runtime: Refinement  ", p->timeCex,    clock() - clk );
        ABC_PRTP( "Runtime: Other       ", p->timeOther,  clock() - clk );
        ABC_PRTP( "Runtime: TOTAL       ", clock() - clk, clock() - clk );
        Gia_VtaPrintMemory( p );
    }

    Vga_ManStop( p );
    fflush( stdout );
    return RetValue;
}

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

  Synopsis    [Collect nodes/flops involved in different timeframes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_VtaPerform( Gia_Man_t * pAig, Gia_ParVta_t * pPars )
{
    int RetValue = -1;
    if ( pAig->vObjClasses == NULL && pPars->fUseRollback )
    {
        int nFramesMaxOld = pPars->nFramesMax;
        pPars->nFramesMax = pPars->nFramesStart;
        RetValue = Gia_VtaPerformInt( pAig, pPars );
        pPars->nFramesMax = nFramesMaxOld;
    }
    if ( RetValue == 0 )
        return RetValue;
    return Gia_VtaPerformInt( pAig, pPars );
}

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


ABC_NAMESPACE_IMPL_END

