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

  FileName    [bmcICheck.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT-based bounded model checking.]

  Synopsis    [Performs specialized check.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "bmc.h"
#include "sat/cnf/cnf.h"
#include "sat/bsat/satStore.h"
#include "aig/gia/giaAig.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Cnf_Dat_t * Cnf_DeriveGiaRemapped( Gia_Man_t * p )
{
    Cnf_Dat_t * pCnf;
    Aig_Man_t * pAig = Gia_ManToAigSimple( p );
    pAig->nRegs = 0;
    pCnf = Cnf_Derive( pAig, Aig_ManCoNum(pAig) );
    Aig_ManStop( pAig );
    return pCnf;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Cnf_DataLiftGia( Cnf_Dat_t * p, Gia_Man_t * pGia, int nVarsPlus )
{
    Gia_Obj_t * pObj;
    int v;
    Gia_ManForEachObj( pGia, pObj, v )
        if ( p->pVarNums[Gia_ObjId(pGia, pObj)] >= 0 )
            p->pVarNums[Gia_ObjId(pGia, pObj)] += nVarsPlus;
    for ( v = 0; v < p->nLiterals; v++ )
        p->pClauses[0][v] += 2*nVarsPlus;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
sat_solver * Bmc_DeriveSolver( Gia_Man_t * p, Gia_Man_t * pMiter, Cnf_Dat_t * pCnf, int nFramesMax, int nTimeOut, int fVerbose )
{
    sat_solver * pSat;
    Vec_Int_t * vLits;
    Gia_Obj_t * pObj, * pObj0, * pObj1;
    int i, k, iVar0, iVar1, iVarOut;
    int VarShift = 0;

    // start the SAT solver
    pSat = sat_solver_new();
    sat_solver_setnvars( pSat, Gia_ManRegNum(p) + Gia_ManCoNum(p) + pCnf->nVars * (nFramesMax + 1) );
    sat_solver_set_runtime_limit( pSat, nTimeOut ? nTimeOut * CLOCKS_PER_SEC + Abc_Clock(): 0 );

    // add one large OR clause
    vLits = Vec_IntAlloc( Gia_ManCoNum(p) );
    Gia_ManForEachCo( p, pObj, i )
        Vec_IntPush( vLits, Abc_Var2Lit(Gia_ManRegNum(p) + i, 0) );
    sat_solver_addclause( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits) );

    // load the last timeframe
    Cnf_DataLiftGia( pCnf, pMiter, Gia_ManRegNum(p) + Gia_ManCoNum(p) );
    VarShift += Gia_ManRegNum(p) + Gia_ManCoNum(p);

    // add XOR clauses
    Gia_ManForEachPo( p, pObj, i )
    {
        pObj0 = Gia_ManPo( pMiter, 2*i+0 );
        pObj1 = Gia_ManPo( pMiter, 2*i+1 );
        iVar0 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj0)];
        iVar1 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj1)];
        iVarOut = Gia_ManRegNum(p) + i;
        sat_solver_add_xor( pSat, iVar0, iVar1, iVarOut, 0 );
    }
    Gia_ManForEachRi( p, pObj, i )
    {
        pObj0 = Gia_ManRi( pMiter, i );
        pObj1 = Gia_ManRi( pMiter, i + Gia_ManRegNum(p) );
        iVar0 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj0)];
        iVar1 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj1)];
        iVarOut = Gia_ManRegNum(p) + Gia_ManPoNum(p) + i;
        sat_solver_add_xor_and( pSat, iVarOut, iVar0, iVar1, i );
    }
    // add timeframe clauses
    for ( i = 0; i < pCnf->nClauses; i++ )
        if ( !sat_solver_addclause( pSat, pCnf->pClauses[i], pCnf->pClauses[i+1] ) )
            assert( 0 );

    // add other timeframes
    for ( k = 0; k < nFramesMax; k++ )
    {
        // collect variables of the RO nodes
        Vec_IntClear( vLits );
        Gia_ManForEachRo( pMiter, pObj, i )
            Vec_IntPush( vLits, pCnf->pVarNums[Gia_ObjId(pMiter, pObj)] );
        // lift CNF again
        Cnf_DataLiftGia( pCnf, pMiter, pCnf->nVars );
        VarShift += pCnf->nVars;
        // stitch the clauses
        Gia_ManForEachRi( pMiter, pObj, i )
        {
            iVar0 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj)];
            iVar1 = Vec_IntEntry( vLits, i );
            if ( iVar1 == -1 )
                continue;
            sat_solver_add_buffer( pSat, iVar0, iVar1, 0 );
        }
        // add equality clauses for the COs
        Gia_ManForEachPo( p, pObj, i )
        {
            pObj0 = Gia_ManPo( pMiter, 2*i+0 );
            pObj1 = Gia_ManPo( pMiter, 2*i+1 );
            iVar0 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj0)];
            iVar1 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj1)];
            sat_solver_add_buffer( pSat, iVar0, iVar1, 0 );
        }
        Gia_ManForEachRi( p, pObj, i )
        {
            pObj0 = Gia_ManRi( pMiter, i );
            pObj1 = Gia_ManRi( pMiter, i + Gia_ManRegNum(p) );
            iVar0 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj0)];
            iVar1 = pCnf->pVarNums[Gia_ObjId(pMiter, pObj1)];
            sat_solver_add_buffer_enable( pSat, iVar0, iVar1, i, 0 );
        }
        // add timeframe clauses
        for ( i = 0; i < pCnf->nClauses; i++ )
            if ( !sat_solver_addclause( pSat, pCnf->pClauses[i], pCnf->pClauses[i+1] ) )
                assert( 0 );
    }
//    sat_solver_compress( pSat );
    Cnf_DataLiftGia( pCnf, pMiter, -VarShift );
    Vec_IntFree( vLits );
    return pSat;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Bmc_PerformICheck( Gia_Man_t * p, int nFramesMax, int nTimeOut, int fEmpty, int fVerbose )
{
    int fUseOldCnf = 0;
    Gia_Man_t * pMiter, * pTemp;
    Cnf_Dat_t * pCnf;
    sat_solver * pSat;
    Vec_Int_t * vLits, * vUsed;
    int i, status, Lit;
    int nLitsUsed, nLits, * pLits;
    abctime clkStart = Abc_Clock();
    assert( nFramesMax > 0 );
    assert( Gia_ManRegNum(p) > 0 );

    if ( fVerbose )
    printf( "Solving M-inductiveness for design %s with %d AND nodes and %d flip-flops:\n",
        Gia_ManName(p), Gia_ManAndNum(p), Gia_ManRegNum(p) );

    // create miter
    pTemp = Gia_ManDup( p );
    pMiter = Gia_ManMiter( p, pTemp, 0, 1, 1, 0, 0 );
    Gia_ManStop( pTemp );
    assert( Gia_ManPoNum(pMiter)  == 2 * Gia_ManPoNum(p) );
    assert( Gia_ManRegNum(pMiter) == 2 * Gia_ManRegNum(p) );
    // derive CNF
    if ( fUseOldCnf )
        pCnf = Cnf_DeriveGiaRemapped( pMiter );
    else
    {
        pMiter = Jf_ManDeriveCnf( pTemp = pMiter, 0 );
        Gia_ManStop( pTemp );
        pCnf = (Cnf_Dat_t *)pMiter->pData; pMiter->pData = NULL;
    }

    // collect positive literals
    vLits = Vec_IntAlloc( Gia_ManCoNum(p) );
    for ( i = 0; i < Gia_ManRegNum(p); i++ )
        Vec_IntPush( vLits, Abc_Var2Lit(i, fEmpty) );

    // iteratively compute a minimal M-inductive set of next-state functions
    nLitsUsed = fEmpty ? 0 : Vec_IntSize(vLits);
    vUsed = Vec_IntAlloc( Vec_IntSize(vLits) );
    while ( 1 )
    {
        int fChanges = 0;
        // derive SAT solver        
        pSat = Bmc_DeriveSolver( p, pMiter, pCnf, nFramesMax, nTimeOut, fVerbose );
//        sat_solver_bookmark( pSat );
        status = sat_solver_solve( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 );
        if ( status == l_Undef )
        {
            printf( "Timeout reached after %d seconds.\n", nTimeOut );
            break;
        }
        if ( status == l_True )
        {
            printf( "The problem is satisfiable (the current set is not M-inductive).\n" );
            break;
        }
        assert( status == l_False );
        // call analize_final
        nLits = sat_solver_final( pSat, &pLits );
        // mark used literals
        Vec_IntFill( vUsed, Vec_IntSize(vLits), 0 );
        for ( i = 0; i < nLits; i++ )
            Vec_IntWriteEntry( vUsed, Abc_Lit2Var(pLits[i]), 1 );

        // check if there are any positive unused
        Vec_IntForEachEntry( vLits, Lit, i )
        {
            assert( i == Abc_Lit2Var(Lit) );
            if ( Abc_LitIsCompl(Lit) )
                continue;
            if ( Vec_IntEntry(vUsed, i) )
                continue;
            // positive literal became unused
            Vec_IntWriteEntry( vLits, i, Abc_LitNot(Lit) );
            nLitsUsed--;
            fChanges = 1;
        }
        // report the results
        if ( fVerbose )
        printf( "M =%4d :  AIG =%8d.  SAT vars =%8d.  SAT conf =%8d.  S =%6d. (%6.2f %%)  ",
            nFramesMax, (nFramesMax+1) * Gia_ManAndNum(pMiter), 
            Gia_ManRegNum(p) + Gia_ManCoNum(p) + sat_solver_nvars(pSat), 
            sat_solver_nconflicts(pSat), nLitsUsed, 100.0 * nLitsUsed / Gia_ManRegNum(p) );
        if ( fVerbose )
        Abc_PrintTime( 1, "Time", Abc_Clock() - clkStart );
        // count the number of negative literals
        sat_solver_delete( pSat );
        if ( !fChanges || fEmpty )
            break;
//        break;
//        sat_solver_rollback( pSat );
    }
    Cnf_DataFree( pCnf );
    Gia_ManStop( pMiter );
    Vec_IntFree( vLits );
    Vec_IntFree( vUsed );
}

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

  Synopsis    [Collect flops starting from the POs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Bmc_PerformFindFlopOrder_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vRegs )
{
    if ( Gia_ObjIsTravIdCurrent(p, pObj) )
        return;
    Gia_ObjSetTravIdCurrent(p, pObj);
    if ( Gia_ObjIsCi(pObj) )
    {
        if ( Gia_ObjIsRo(p, pObj) )
            Vec_IntPush( vRegs, Gia_ObjId(p, pObj) );
        return;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Bmc_PerformFindFlopOrder_rec( p, Gia_ObjFanin0(pObj), vRegs );
    Bmc_PerformFindFlopOrder_rec( p, Gia_ObjFanin1(pObj), vRegs );
}
void Bmc_PerformFindFlopOrder( Gia_Man_t * p, Vec_Int_t * vRegs )
{
    Gia_Obj_t * pObj;
    int i, iReg, k = 0;
    // start with POs
    Vec_IntClear( vRegs );
    Gia_ManForEachPo( p, pObj, i )
        Vec_IntPush( vRegs, Gia_ObjId(p, pObj) );
    // add flop outputs in the B
    Gia_ManIncrementTravId( p );
    Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) );
    Gia_ManForEachObjVec( vRegs, p, pObj, i )
    {
        assert( Gia_ObjIsPo(p, pObj) || Gia_ObjIsRo(p, pObj) );
        if ( Gia_ObjIsRo(p, pObj) )
            pObj = Gia_ObjRoToRi( p, pObj );
        Bmc_PerformFindFlopOrder_rec( p, Gia_ObjFanin0(pObj), vRegs );
    }
    // add dangling flops
    Gia_ManForEachRo( p, pObj, i )
        if ( !Gia_ObjIsTravIdCurrent(p, pObj) )
            Vec_IntPush( vRegs, Gia_ObjId(p, pObj) );
    // remove POs; keep flop outputs only; remap ObjId into CiId
    assert( Vec_IntSize(vRegs) == Gia_ManCoNum(p) );
    Gia_ManForEachObjVec( vRegs, p, pObj, i )
    {
        if ( i < Gia_ManPoNum(p) )
            continue;
        iReg = Gia_ObjCioId(pObj) - Gia_ManPiNum(p);
        assert( iReg >= 0 && iReg < Gia_ManRegNum(p) );
        Vec_IntWriteEntry( vRegs, k++, iReg );
    }
    Vec_IntShrink( vRegs, k );
    assert( Vec_IntSize(vRegs) == Gia_ManRegNum(p) );
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Bmc_PerformISearchOne( Gia_Man_t * p, int nFramesMax, int nTimeOut, int fReverse, int fBackTopo, int fVerbose, Vec_Int_t * vLits )
{
    int fUseOldCnf = 0;
    Gia_Man_t * pMiter, * pTemp;
    Cnf_Dat_t * pCnf;
    sat_solver * pSat;
    Vec_Int_t * vRegs = NULL;
//    Vec_Int_t * vLits;
    int i, Iter, status;
    int nLitsUsed, RetValue = 0;
    abctime clkStart = Abc_Clock();
    assert( nFramesMax > 0 );
    assert( Gia_ManRegNum(p) > 0 );

    // create miter
    pTemp = Gia_ManDup( p );
    pMiter = Gia_ManMiter( p, pTemp, 0, 1, 1, 0, 0 );
    Gia_ManStop( pTemp );
    assert( Gia_ManPoNum(pMiter)  == 2 * Gia_ManPoNum(p) );
    assert( Gia_ManRegNum(pMiter) == 2 * Gia_ManRegNum(p) );
    // derive CNF
    if ( fUseOldCnf )
        pCnf = Cnf_DeriveGiaRemapped( pMiter );
    else
    {
        //pMiter = Jf_ManDeriveCnf( pTemp = pMiter, 0 );
        //Gia_ManStop( pTemp );
        //pCnf = (Cnf_Dat_t *)pMiter->pData; pMiter->pData = NULL;
        pCnf = (Cnf_Dat_t *)Mf_ManGenerateCnf( pMiter, 8, 0, 0, 0, 0 );
    }
/*
    // collect positive literals
    vLits = Vec_IntAlloc( Gia_ManCoNum(p) );
    for ( i = 0; i < Gia_ManRegNum(p); i++ )
        Vec_IntPush( vLits, Abc_Var2Lit(i, 0) );
*/
    // derive SAT solver        
    pSat = Bmc_DeriveSolver( p, pMiter, pCnf, nFramesMax, nTimeOut, fVerbose );
    status = sat_solver_solve( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 );
    if ( status == l_True )
    {
        printf( "I = %4d :  ", nFramesMax );
        printf( "Problem is satisfiable.\n" );
        sat_solver_delete( pSat );
        Cnf_DataFree( pCnf );
        Gia_ManStop( pMiter );
        return 1;
    }
    if ( status == l_Undef )
    {
        printf( "ICheck: Timeout reached after %d seconds.                                                                          \n", nTimeOut );
        RetValue = 1;
        goto cleanup;
    }
    assert( status == l_False );

    // count the number of positive literals
    nLitsUsed = 0;
    for ( i = 0; i < Gia_ManRegNum(p); i++ )
        if ( !Abc_LitIsCompl(Vec_IntEntry(vLits, i)) )
            nLitsUsed++;

    // try removing variables
    vRegs = Vec_IntStartNatural( Gia_ManRegNum(p) );
    if ( fBackTopo )
        Bmc_PerformFindFlopOrder( p, vRegs );
    if ( fReverse )
        Vec_IntReverseOrder( vRegs );
//    for ( Iter = 0; Iter < Gia_ManRegNum(p); Iter++ )
    Vec_IntForEachEntry( vRegs, i, Iter )
    {
//        i = fReverse ? Gia_ManRegNum(p) - 1 - Iter : Iter;
        if ( Abc_LitIsCompl(Vec_IntEntry(vLits, i)) )
            continue;
        Vec_IntWriteEntry( vLits, i, Abc_LitNot(Vec_IntEntry(vLits, i)) );
        status = sat_solver_solve( pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 );
        if ( status == l_Undef )
        {
            printf( "ICheck: Timeout reached after %d seconds.                                                                          \n", nTimeOut );
            RetValue = 1;
            goto cleanup;
        }
        if ( status == l_True )
            Vec_IntWriteEntry( vLits, i, Abc_LitNot(Vec_IntEntry(vLits, i)) );
        else if ( status == l_False )
            nLitsUsed--;
        else assert( 0 );
        // report the results
        //printf( "Round %d:  ", o );
        if ( fVerbose )
        {
            printf( "I = %4d :  AIG =%8d.  SAT vars =%8d.  SAT conf =%8d.  S =%6d. (%6.2f %%)  ",
                i, (nFramesMax+1) * Gia_ManAndNum(pMiter), 
                Gia_ManRegNum(p) + Gia_ManCoNum(p) + sat_solver_nvars(pSat), 
                sat_solver_nconflicts(pSat), nLitsUsed, 100.0 * nLitsUsed / Gia_ManRegNum(p) );
            ABC_PRTr( "Time", Abc_Clock() - clkStart );
            fflush( stdout );
        }
    }
    // report the results
    //printf( "Round %d:  ", o );
    if ( fVerbose )
    {
        printf( "M = %4d :  AIG =%8d.  SAT vars =%8d.  SAT conf =%8d.  S =%6d. (%6.2f %%)  ",
            nFramesMax, (nFramesMax+1) * Gia_ManAndNum(pMiter), 
            Gia_ManRegNum(p) + Gia_ManCoNum(p) + sat_solver_nvars(pSat), 
            sat_solver_nconflicts(pSat), nLitsUsed, 100.0 * nLitsUsed / Gia_ManRegNum(p) );
        Abc_PrintTime( 1, "Time", Abc_Clock() - clkStart );
        fflush( stdout );
    }
cleanup:
    // cleanup
    sat_solver_delete( pSat );
    Cnf_DataFree( pCnf );
    Gia_ManStop( pMiter );
    Vec_IntFree( vRegs );
//    Vec_IntFree( vLits );
    return RetValue;
}
Vec_Int_t * Bmc_PerformISearch( Gia_Man_t * p, int nFramesMax, int nTimeOut, int fReverse, int fBackTopo, int fDump, int fVerbose )
{
    Vec_Int_t * vLits, * vFlops;
    int i, f;
    if ( fVerbose )
    printf( "Solving M-inductiveness for design %s with %d AND nodes and %d flip-flops with %s %s flop order:\n",
        Gia_ManName(p), Gia_ManAndNum(p), Gia_ManRegNum(p), fReverse ? "reverse":"direct", fBackTopo ? "backward":"natural" );
    fflush( stdout );

    // collect positive literals
    vLits = Vec_IntAlloc( Gia_ManCoNum(p) );
    for ( i = 0; i < Gia_ManRegNum(p); i++ )
        Vec_IntPush( vLits, Abc_Var2Lit(i, 0) );

    for ( f = 1; f <= nFramesMax; f++ )
        if ( Bmc_PerformISearchOne( p, f, nTimeOut, fReverse, fBackTopo, fVerbose, vLits ) )
        {
            Vec_IntFree( vLits );
            return NULL;
        }

    // dump the numbers of the flops
    if ( fDump )
    {
        int nLitsUsed = 0;
        for ( i = 0; i < Gia_ManRegNum(p); i++ )
            if ( !Abc_LitIsCompl(Vec_IntEntry(vLits, i)) )
                nLitsUsed++;
        printf( "The set contains %d (out of %d) next-state functions with 0-based numbers:\n", nLitsUsed, Gia_ManRegNum(p) );
        for ( i = 0; i < Gia_ManRegNum(p); i++ )
            if ( !Abc_LitIsCompl(Vec_IntEntry(vLits, i)) )
                printf( "%d ", i );
        printf( "\n" );
    }       
    // save flop indexes
    vFlops = Vec_IntAlloc( Gia_ManRegNum(p) );
    for ( i = 0; i < Gia_ManRegNum(p); i++ )
        if ( !Abc_LitIsCompl(Vec_IntEntry(vLits, i)) )
            Vec_IntPush( vFlops, 1 );
        else
            Vec_IntPush( vFlops, 0 );
    Vec_IntFree( vLits );
    return vFlops;
}

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


ABC_NAMESPACE_IMPL_END