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

  FileName    [saigSimSeq.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Sequential AIG package.]

  Synopsis    [Fast sequential AIG simulator.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "saig.h"
#include "proof/ssw/ssw.h"

ABC_NAMESPACE_IMPL_START


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

// combinational simulation manager
typedef struct Raig_Man_t_ Raig_Man_t;
struct Raig_Man_t_
{
    // parameters
    Aig_Man_t *     pAig;         // the AIG to be used for simulation
    int             nWords;       // the number of words to simulate
    // AIG representation
    int             nPis;         // the number of primary inputs
    int             nPos;         // the number of primary outputs
    int             nCis;         // the number of combinational inputs
    int             nCos;         // the number of combinational outputs
    int             nNodes;       // the number of internal nodes
    int             nObjs;        // nCis + nNodes + nCos + 2
    int *           pFans0;       // fanin0 for all objects
    int *           pFans1;       // fanin1 for all objects
    Vec_Int_t *     vCis2Ids;     // mapping of CIs into their PI ids
    Vec_Int_t *     vLos;         // register outputs
    Vec_Int_t *     vLis;         // register inputs
    // simulation info
    int *           pRefs;        // reference counter for each node
    unsigned *      pSims;        // simlulation information for each node
    // recycable memory
    unsigned *      pMems;        // allocated simulaton memory
    int             nWordsAlloc;  // the number of allocated entries
    int             nMems;        // the number of used entries  
    int             nMemsMax;     // the max number of used entries 
    int             MemFree;      // next free entry
};

static inline int  Raig_Var2Lit( int Var, int fCompl )  { return Var + Var + fCompl; }
static inline int  Raig_Lit2Var( int Lit )              { return Lit >> 1;           }
static inline int  Raig_LitIsCompl( int Lit )           { return Lit & 1;            }
static inline int  Raig_LitNot( int Lit )               { return Lit ^ 1;            }
static inline int  Raig_LitNotCond( int Lit, int c )    { return Lit ^ (int)(c > 0); }
static inline int  Raig_LitRegular( int Lit )           { return Lit & ~01;          }

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

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

  Synopsis    [Find the PO corresponding to the PO driver.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Raig_ManFindPo( Aig_Man_t * pAig, int iNode )
{
    Aig_Obj_t * pObj;
    int i;
    Saig_ManForEachPo( pAig, pObj, i )
        if ( pObj->iData == iNode )
            return i;
    return -1;
}

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

  Synopsis    [Creates fast simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Raig_ManCreate_rec( Raig_Man_t * p, Aig_Obj_t * pObj )
{
    int iFan0, iFan1;
    assert( !Aig_IsComplement(pObj) );
    if ( pObj->iData )
        return pObj->iData;
    assert( !Aig_ObjIsConst1(pObj) );
    if ( Aig_ObjIsNode(pObj) )
    {
        iFan0 = Raig_ManCreate_rec( p, Aig_ObjFanin0(pObj) );
        iFan0 = (iFan0 << 1) | Aig_ObjFaninC0(pObj);
        iFan1 = Raig_ManCreate_rec( p, Aig_ObjFanin1(pObj) );
        iFan1 = (iFan1 << 1) | Aig_ObjFaninC1(pObj);
    }
    else if ( Aig_ObjIsCo(pObj) )
    {
        iFan0 = Raig_ManCreate_rec( p, Aig_ObjFanin0(pObj) );
        iFan0 = (iFan0 << 1) | Aig_ObjFaninC0(pObj);
        iFan1 = 0;
    }
    else
    {
        iFan0 = iFan1 = 0;
        Vec_IntPush( p->vCis2Ids, Aig_ObjCioId(pObj) );
    }
    p->pFans0[p->nObjs] = iFan0;
    p->pFans1[p->nObjs] = iFan1;
    p->pRefs[p->nObjs] = Aig_ObjRefs(pObj);
    return pObj->iData = p->nObjs++;
}

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

  Synopsis    [Creates fast simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Raig_Man_t * Raig_ManCreate( Aig_Man_t * pAig )
{
    Raig_Man_t * p;
    Aig_Obj_t * pObj;
    int i, nObjs;
    Aig_ManCleanData( pAig );
    p = (Raig_Man_t *)ABC_ALLOC( Raig_Man_t, 1 );
    memset( p, 0, sizeof(Raig_Man_t) );
    p->pAig = pAig;
    p->nPis = Saig_ManPiNum(pAig);
    p->nPos = Saig_ManPoNum(pAig);
    p->nCis = Aig_ManCiNum(pAig);
    p->nCos = Aig_ManCoNum(pAig);
    p->nNodes = Aig_ManNodeNum(pAig);
    nObjs = p->nCis + p->nCos + p->nNodes + 2;
    p->pFans0 = ABC_ALLOC( int, nObjs );
    p->pFans1 = ABC_ALLOC( int, nObjs );
    p->pRefs = ABC_ALLOC( int, nObjs );
    p->pSims = ABC_CALLOC( unsigned, nObjs );
    p->vCis2Ids = Vec_IntAlloc( Aig_ManCiNum(pAig) );
    // add objects (0=unused; 1=const1)
    p->nObjs = 2;
    pObj = Aig_ManConst1( pAig );
    pObj->iData = 1;
    Aig_ManForEachCi( pAig, pObj, i )
        if ( Aig_ObjRefs(pObj) == 0 )
            Raig_ManCreate_rec( p, pObj );
    Aig_ManForEachCo( pAig, pObj, i )
        Raig_ManCreate_rec( p, pObj );
    assert( Vec_IntSize(p->vCis2Ids) == Aig_ManCiNum(pAig) );
    assert( p->nObjs == nObjs );
    // collect flop outputs
    p->vLos = Vec_IntAlloc( Aig_ManRegNum(pAig) );
    Saig_ManForEachLo( pAig, pObj, i )
        Vec_IntPush( p->vLos, pObj->iData );
    // collect flop inputs
    p->vLis = Vec_IntAlloc( Aig_ManRegNum(pAig) );
    Saig_ManForEachLi( pAig, pObj, i )
    {
        Vec_IntPush( p->vLis, pObj->iData );
        assert( p->pRefs[ pObj->iData ] == 0 );
        p->pRefs[ pObj->iData ]++;
    }
    return p;
}

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

  Synopsis    [Creates fast simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Raig_ManDelete( Raig_Man_t * p )
{
    Vec_IntFree( p->vCis2Ids );
    Vec_IntFree( p->vLos );
    Vec_IntFree( p->vLis );
    ABC_FREE( p->pFans0 );
    ABC_FREE( p->pFans1 );
    ABC_FREE( p->pRefs );
    ABC_FREE( p->pSims );
    ABC_FREE( p->pMems );
    ABC_FREE( p );
}

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

  Synopsis    [References simulation info.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
unsigned * Raig_ManSimRef( Raig_Man_t * p, int i )
{
    unsigned * pSim;
    assert( i > 1 );
    assert( p->pSims[i] == 0 );
    if ( p->MemFree == 0 )
    {
        unsigned * pPlace, Ent;
        if ( p->nWordsAlloc == 0 )
        {
            assert( p->pMems == NULL );
            p->nWordsAlloc = (1<<17); // -> 1Mb
            p->nMems = 1;
        }
        p->nWordsAlloc *= 2;
        p->pMems = ABC_REALLOC( unsigned, p->pMems, p->nWordsAlloc );
        memset( p->pMems, 0xff, sizeof(unsigned) * (p->nWords + 1) );
        pPlace = (unsigned *)&p->MemFree;
        for ( Ent = p->nMems * (p->nWords + 1); 
              Ent + p->nWords + 1 < (unsigned)p->nWordsAlloc; 
              Ent += p->nWords + 1 )
        {
            *pPlace = Ent;
            pPlace = p->pMems + Ent;
        }
        *pPlace = 0;
    }
    p->pSims[i] = p->MemFree;
    pSim = p->pMems + p->MemFree;
    p->MemFree = pSim[0];
    pSim[0] = p->pRefs[i];
    p->nMems++;
    if ( p->nMemsMax < p->nMems )
        p->nMemsMax = p->nMems;
    return pSim;
}

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

  Synopsis    [Dereference simulaton info.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
unsigned * Raig_ManSimDeref( Raig_Man_t * p, int i )
{
    unsigned * pSim;
    assert( i );
    if ( i == 1 ) // const 1
        return p->pMems;
    assert( p->pSims[i] > 0 );
    pSim = p->pMems + p->pSims[i];
    if ( --pSim[0] == 0 )
    {
        pSim[0] = p->MemFree;
        p->MemFree = p->pSims[i];
        p->pSims[i] = 0;
        p->nMems--;
    }
    return pSim;
}

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

  Synopsis    [Simulates one round.]

  Description [Returns the number of PO entry if failed; 0 otherwise.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Raig_ManSimulateRound( Raig_Man_t * p, int fMiter, int fFirst, int * piPat )
{ 
    unsigned * pRes0, * pRes1, * pRes;
    int i, w, nCis, nCos, iFan0, iFan1, iPioNum;
    // nove the values to the register outputs
    Vec_IntForEachEntry( p->vCis2Ids, iPioNum, i )
    {
        if ( iPioNum < p->nPis )
            continue;
        pRes = Raig_ManSimRef( p, Vec_IntEntry(p->vLos, iPioNum-p->nPis) );
        if ( fFirst )
            memset( pRes + 1, 0, sizeof(unsigned) * p->nWords );
        else
        {
            pRes0 = Raig_ManSimDeref( p, Vec_IntEntry(p->vLis, iPioNum-p->nPis) );
            for ( w = 1; w <= p->nWords; w++ )
                pRes[w] = pRes0[w];
        }
        // handle unused PIs
        if ( pRes[0] == 0 ) 
        {
            pRes[0] = 1;
            Raig_ManSimDeref( p, Vec_IntEntry(p->vLos, iPioNum-p->nPis) );
        }
    }
    // simulate the logic
    nCis = nCos = 0;
    for ( i = 2; i < p->nObjs; i++ )
    {
        if ( p->pFans0[i] == 0 ) // ci always has zero first fanin
        {
            iPioNum = Vec_IntEntry( p->vCis2Ids, nCis );
            if ( iPioNum < p->nPis )
            {
                pRes = Raig_ManSimRef( p, i );
                for ( w = 1; w <= p->nWords; w++ )
                    pRes[w] = Aig_ManRandom( 0 );
                // handle unused PIs
                if ( pRes[0] == 0 ) 
                {
                    pRes[0] = 1;
                    Raig_ManSimDeref( p, i );
                }
            }
            else
                assert( Vec_IntEntry(p->vLos, iPioNum-p->nPis) == i );
            nCis++;
            continue;
        }
        if ( p->pFans1[i] == 0 ) // co always has non-zero 1st fanin and zero 2nd fanin
        {
            pRes0 = Raig_ManSimDeref( p, Raig_Lit2Var(p->pFans0[i]) );
            if ( nCos < p->nPos && fMiter )
            {
                unsigned Const = Raig_LitIsCompl(p->pFans0[i])? ~0 : 0;
                for ( w = 1; w <= p->nWords; w++ )
                    if ( pRes0[w] != Const )
                    {
                        *piPat = 32*(w-1) + Aig_WordFindFirstBit( pRes0[w] ^ Const );
                        return i;
                    }
            }
            else
            {
                pRes = Raig_ManSimRef( p, i );
                assert( pRes[0] == 1 );
                if ( Raig_LitIsCompl(p->pFans0[i]) )
                    for ( w = 1; w <= p->nWords; w++ )
                        pRes[w] = ~pRes0[w];
                else
                    for ( w = 1; w <= p->nWords; w++ )
                        pRes[w] = pRes0[w];
            }
            nCos++;
            continue;
        }
        pRes  = Raig_ManSimRef( p, i );
        assert( pRes[0] > 0 );
        iFan0 = p->pFans0[i];
        iFan1 = p->pFans1[i];
        pRes0 = Raig_ManSimDeref( p, Raig_Lit2Var(p->pFans0[i]) );
        pRes1 = Raig_ManSimDeref( p, Raig_Lit2Var(p->pFans1[i]) );
        if ( Raig_LitIsCompl(iFan0) && Raig_LitIsCompl(iFan1) )
            for ( w = 1; w <= p->nWords; w++ )
                pRes[w] = ~(pRes0[w] | pRes1[w]);
        else if ( Raig_LitIsCompl(iFan0) && !Raig_LitIsCompl(iFan1) )
            for ( w = 1; w <= p->nWords; w++ )
                pRes[w] = ~pRes0[w] & pRes1[w];
        else if ( !Raig_LitIsCompl(iFan0) && Raig_LitIsCompl(iFan1) )
            for ( w = 1; w <= p->nWords; w++ )
                pRes[w] = pRes0[w] & ~pRes1[w];
        else if ( !Raig_LitIsCompl(iFan0) && !Raig_LitIsCompl(iFan1) )
            for ( w = 1; w <= p->nWords; w++ )
                pRes[w] = pRes0[w] & pRes1[w];
    }
    assert( nCis == p->nCis );
    assert( nCos == p->nCos );
    assert( p->nMems == 1 + Vec_IntSize(p->vLis) );
    return 0;
}

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

  Synopsis    [Returns the counter-example.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Cex_t * Raig_ManGenerateCounter( Aig_Man_t * pAig, int iFrame, int iOut, int nWords, int iPat, Vec_Int_t * vCis2Ids )
{
    Abc_Cex_t * p;
    unsigned * pData;
    int f, i, w, iPioId, Counter;
    p = Abc_CexAlloc( Aig_ManRegNum(pAig), Saig_ManPiNum(pAig), iFrame+1 );
    p->iFrame = iFrame;
    p->iPo = iOut;
    // fill in the binary data
    Aig_ManRandom( 1 );
    Counter = p->nRegs;
    pData = ABC_ALLOC( unsigned, nWords );
    for ( f = 0; f <= iFrame; f++, Counter += p->nPis )
    for ( i = 0; i < Aig_ManCiNum(pAig); i++ )
    {
        iPioId = Vec_IntEntry( vCis2Ids, i );
        if ( iPioId >= p->nPis )
            continue;
        for ( w = 0; w < nWords; w++ )
            pData[w] = Aig_ManRandom( 0 );
        if ( Abc_InfoHasBit( pData, iPat ) )
            Abc_InfoSetBit( p->pData, Counter + iPioId );
    }
    ABC_FREE( pData );
    return p;
}

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

  Synopsis    [Returns 1 if the bug is detected, 0 otherwise.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Raig_ManSimulate( Aig_Man_t * pAig, int nWords, int nIters, int TimeLimit, int fMiter, int fVerbose )
{
    Raig_Man_t * p;
    Sec_MtrStatus_t Status;
    int i, iPat, RetValue = 0;
    abctime clk, clkTotal = Abc_Clock();
    assert( Aig_ManRegNum(pAig) > 0 );
    Status = Sec_MiterStatus( pAig );
    if ( Status.nSat > 0 )
    {
        printf( "Miter is trivially satisfiable (output %d).\n", Status.iOut );
        return 1;
    }
    if ( Status.nUndec == 0 )
    {
        printf( "Miter is trivially unsatisfiable.\n" );
        return 0;
    }
    Aig_ManRandom( 1 );
    p = Raig_ManCreate( pAig );
    p->nWords = nWords;
    // iterate through objects
    for ( i = 0; i < nIters; i++ )
    {
        clk = Abc_Clock();
        RetValue = Raig_ManSimulateRound( p, fMiter, i==0, &iPat );
        if ( fVerbose )
        {
            printf( "Frame %4d out of %4d and timeout %3d sec. ", i+1, nIters, TimeLimit );
            printf("Time = %7.2f sec\r", (1.0*Abc_Clock()-clkTotal)/CLOCKS_PER_SEC);
        }
        if ( RetValue > 0 )
        {
            int iOut = Raig_ManFindPo(p->pAig, RetValue);
            assert( pAig->pSeqModel == NULL );
            pAig->pSeqModel = Raig_ManGenerateCounter( pAig, i, iOut, nWords, iPat, p->vCis2Ids );
            if ( fVerbose )
            printf( "Miter is satisfiable after simulation (output %d).\n", iOut );
            break;
        }
        if ( (Abc_Clock() - clk)/CLOCKS_PER_SEC >= TimeLimit )
        {
            printf( "No bug detected after %d frames with time limit %d seconds.\n", i+1, TimeLimit );
            break;
        }
    }
    if ( fVerbose )
    {
        printf( "Maxcut = %8d.  AigMem = %7.2f MB.  SimMem = %7.2f MB.  ", 
            p->nMemsMax, 
            1.0*(p->nObjs * 16)/(1<<20), 
            1.0*(p->nMemsMax * 4 * (nWords+1))/(1<<20) );
        ABC_PRT( "Total time", Abc_Clock() - clkTotal );
    }
    Raig_ManDelete( p );
    return RetValue > 0;
}

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


ABC_NAMESPACE_IMPL_END