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

  FileName    [bmcBCore.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT-based bounded model checking.]

  Synopsis    [Performs recording of BMC core.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "bmc.h"
#include "sat/bsat/satSolver.h"
#include "sat/bsat/satStore.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Collect pivot variables.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Bmc_ManBCoreReadPivots( char * pName )
{
    int Num;
    Vec_Int_t * vPivots = Vec_IntAlloc( 100 );
    FILE * pFile = fopen( pName, "r" );
    while ( fscanf( pFile, "%d", &Num ) == 1 )
        Vec_IntPush( vPivots, Num );
    fclose( pFile );
    return vPivots;
}
Vec_Int_t * Bmc_ManBCoreCollectPivots( Gia_Man_t * p, char * pName, Vec_Int_t * vVarMap )
{
    Gia_Obj_t * pObj;
    int i, iVar, iFrame;
    Vec_Int_t * vPivots = Vec_IntAlloc( 100 );
    Vec_Int_t * vVars = Bmc_ManBCoreReadPivots( pName );
    Gia_ManForEachObj( p, pObj, i )
        pObj->fMark0 = 0;
    Vec_IntForEachEntry( vVars, iVar, i )
        if ( iVar > 0 && iVar < Gia_ManObjNum(p) )
            Gia_ManObj( p, iVar )->fMark0 = 1;
        else
            printf( "Cannot find object with Id %d in the given AIG.\n", iVar );
    Vec_IntForEachEntryDouble( vVarMap, iVar, iFrame, i )
        if ( Gia_ManObj( p, iVar )->fMark0 )
            Vec_IntPush( vPivots, Abc_Lit2Var(i) );
    Gia_ManForEachObj( p, pObj, i )
        pObj->fMark0 = 0;
    Vec_IntFree( vVars );
    return vPivots;
}

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

  Synopsis    [Collect (Id; Frame) pairs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Bmc_ManBCoreAssignVar( Gia_Man_t * p, Gia_Obj_t * pObj, int f, Vec_Int_t * vNodes )
{
    pObj->Value = Abc_Lit2Var(Vec_IntSize(vNodes));
    Vec_IntPush( vNodes, Gia_ObjId(p, pObj) );
    Vec_IntPush( vNodes, f );
//    printf( "Obj %3d  Frame %3d  --->  Var %3d    ", Gia_ObjId(p, pObj), f, pObj->Value );
//    Gia_ObjPrint( p, pObj );
}
void Bmc_ManBCoreCollect_rec( Gia_Man_t * p, int Id, int f, Vec_Int_t * vNodes, Vec_Int_t * vRootsNew )
{
    Gia_Obj_t * pObj;
    if ( Gia_ObjIsTravIdCurrentId(p, Id) )
        return;
    Gia_ObjSetTravIdCurrentId(p, Id);
    pObj = Gia_ManObj( p, Id );
    Bmc_ManBCoreAssignVar( p, pObj, f, vNodes );
    if ( Gia_ObjIsPi(p, pObj) )
        return;
    if ( Gia_ObjIsRo(p, pObj) )
    {
        Vec_IntPush( vRootsNew, Gia_ObjId(p, Gia_ObjRoToRi(p, pObj)) );
        return;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Bmc_ManBCoreCollect_rec( p, Gia_ObjFaninId0p(p, pObj), f, vNodes, vRootsNew );
    Bmc_ManBCoreCollect_rec( p, Gia_ObjFaninId1p(p, pObj), f, vNodes, vRootsNew );
}
Vec_Int_t * Bmc_ManBCoreCollect( Gia_Man_t * p, int iFrame, int iOut, sat_solver * pSat )
{
    Gia_Obj_t * pObj; 
    int f, i, iObj, nNodesOld;
    Vec_Int_t * vNodes  = Vec_IntAlloc( 100 );
    Vec_Int_t * vRoots  = Vec_IntAlloc( 100 );
    Vec_Int_t * vRoots2 = Vec_IntAlloc( 100 );
    assert( iFrame >= 0 && iOut >= 0 );
    // add first variables
    Vec_IntPush( vNodes, -1 );
    Vec_IntPush( vNodes, -1 );
    Bmc_ManBCoreAssignVar( p, Gia_ManPo(p, iOut), iFrame, vNodes );
    // start with root node
    Vec_IntPush( vRoots, Gia_ObjId(p, Gia_ManPo(p, iOut)) );
    // iterate through time frames
    for ( f = iFrame; f >= 0; f-- )
    {
        Gia_ManIncrementTravId( p );
        // add constant node
        Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) );
        Bmc_ManBCoreAssignVar( p, Gia_ManConst0(p), f, vNodes );
        sat_solver_add_const( pSat, Gia_ManConst0(p)->Value, 1 );
        // collect nodes in this timeframe
        Vec_IntClear( vRoots2 );
        nNodesOld = Vec_IntSize(vNodes);
        Gia_ManForEachObjVec( vRoots, p, pObj, i )
            Bmc_ManBCoreCollect_rec( p, Gia_ObjFaninId0p(p, pObj), f, vNodes, vRoots2 );
        if ( f == iFrame )
        {
            // add the final clause
            pObj = Gia_ManPo(p, iOut);
            assert( pObj->Value == 1 );
            assert( Gia_ObjFanin0(pObj)->Value == 3 );
//            sat_solver_add_const( pSat, Gia_ObjFanin0(pObj)->Value, Gia_ObjFaninC0(pObj) );
            sat_solver_add_constraint( pSat, Gia_ObjFanin0(pObj)->Value, pObj->Value, Gia_ObjFaninC0(pObj) );
        }
        else
        {
            // connect current RIs to previous ROs
            Gia_ManForEachObjVec( vRoots, p, pObj, i )
                sat_solver_add_buffer( pSat, pObj->Value, Gia_ObjFanin0(pObj)->Value, Gia_ObjFaninC0(pObj) );
        }
        Gia_ManForEachObjVec( vRoots2, p, pObj, i )
            pObj->Value = Gia_ObjRiToRo(p, pObj)->Value;
        // add nodes of this timeframe
        Vec_IntForEachEntryStart( vNodes, iObj, i, nNodesOld )
        {
            pObj = Gia_ManObj(p, iObj); i++;
            if ( Gia_ObjIsCi(pObj) )
                continue;
            assert( Gia_ObjIsAnd(pObj) );
            sat_solver_add_and( pSat, pObj->Value, Gia_ObjFanin0(pObj)->Value, Gia_ObjFanin1(pObj)->Value, Gia_ObjFaninC0(pObj), Gia_ObjFaninC1(pObj), 0 );
        }
        // collect constant node
        ABC_SWAP( Vec_Int_t *, vRoots, vRoots2 );
    }
    // add constraint variables for the init state
    Gia_ManForEachObjVec( vRoots, p, pObj, i )
    {
        sat_solver_add_constraint( pSat, pObj->Value, Abc_Lit2Var(Vec_IntSize(vNodes)), 1 );
        pObj = Gia_ObjRiToRo(p, pObj);
        Bmc_ManBCoreAssignVar( p, pObj, -1, vNodes );
    }
    Vec_IntFree( vRoots );
    Vec_IntFree( vRoots2 );
    return vNodes;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Bmc_ManBCorePerform( Gia_Man_t * p, Bmc_BCorePar_t * pPars )
{
    clock_t clk = clock();
    Intp_Man_t * pManProof;
    Vec_Int_t * vVarMap, * vCore;
    sat_solver * pSat;
    FILE * pFile;
    void * pSatCnf; 
    int RetValue;
    // create SAT solver
    pSat = sat_solver_new();
    sat_solver_store_alloc( pSat ); 
    sat_solver_setnvars( pSat, 1000 );
    sat_solver_set_runtime_limit( pSat, pPars->nTimeOut ? pPars->nTimeOut * CLOCKS_PER_SEC + Abc_Clock(): 0 );
    vVarMap = Bmc_ManBCoreCollect( p, pPars->iFrame, pPars->iOutput, pSat );
    sat_solver_store_mark_roots( pSat ); 
    // create pivot variables
    if ( pPars->pFilePivots )
    {
        Vec_Int_t * vPivots = Bmc_ManBCoreCollectPivots(p, pPars->pFilePivots, vVarMap);
        sat_solver_set_pivot_variables( pSat, Vec_IntArray(vPivots), Vec_IntSize(vPivots) );
        Vec_IntReleaseArray( vPivots );
        Vec_IntFree( vPivots );
    }
    // solve the problem
    RetValue = sat_solver_solve( pSat, NULL, NULL, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 );
    if ( RetValue == l_Undef )
    {
        Vec_IntFree( vVarMap );
        sat_solver_delete( pSat );
        printf( "Timeout of conflict limit is reached.\n" );
        return;
    }
    if ( RetValue == l_True )
    {
        Vec_IntFree( vVarMap );
        sat_solver_delete( pSat );
        printf( "The BMC problem is SAT.\n" );
        return;
    }
    if ( pPars->fVerbose )
    {
        printf( "SAT solver returned UNSAT after %7d conflicts.      ", (int)pSat->stats.conflicts );
        Abc_PrintTime( 1, "Time", clock() - clk );
    }
    assert( RetValue == l_False );
    pSatCnf = sat_solver_store_release( pSat ); 
//    Sto_ManDumpClauses( (Sto_Man_t *)pSatCnf, "cnf_store.txt" );
    // derive the UNSAT core
    clk = clock();
    pManProof = Intp_ManAlloc();
    vCore = (Vec_Int_t *)Intp_ManUnsatCore( pManProof, (Sto_Man_t *)pSatCnf, 1, pPars->fVerbose );
    Intp_ManFree( pManProof );
    if ( pPars->fVerbose )
    {
        printf( "UNSAT core contains %d (out of %d) learned clauses.   ", Vec_IntSize(vCore), sat_solver_nconflicts(pSat) );
        Abc_PrintTime( 1, "Time", clock() - clk );
    }
    // write the problem
    Vec_IntSort( vCore, 0 );
    pFile = pPars->pFileProof ? fopen( pPars->pFileProof, "wb" ) : stdout;
    Intp_ManUnsatCorePrintForBmc( pFile, (Sto_Man_t *)pSatCnf, vCore, vVarMap );
    if ( pFile != stdout )
        fclose( pFile );
    // cleanup
    Sto_ManFree( (Sto_Man_t *)pSatCnf );
    Vec_IntFree( vVarMap );
    Vec_IntFree( vCore );
    sat_solver_delete( pSat );
}

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


ABC_NAMESPACE_IMPL_END