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

  FileName    [fsimTsim.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Fast sequential AIG simulator.]

  Synopsis    [Varius utilities.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "fsimInt.h"

ABC_NAMESPACE_IMPL_START


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

#define FSIM_ZER 1
#define FSIM_ONE 2
#define FSIM_UND 3

static inline int Aig_XsimNotCond( int Value, int fCompl )   
{ 
    if ( Value == FSIM_UND )
        return FSIM_UND;
    if ( Value == FSIM_ZER + fCompl )
        return FSIM_ZER;
    return FSIM_ONE;
}
static inline int Aig_XsimAndCond( int Value0, int fCompl0, int Value1, int fCompl1 )   
{ 
    if ( Value0 == FSIM_UND || Value1 == FSIM_UND )
        return FSIM_UND;
    if ( Value0 == FSIM_ZER + fCompl0 || Value1 == FSIM_ZER + fCompl1 )
        return FSIM_ZER;
    return FSIM_ONE;
}

static inline int Fsim_ManTerSimInfoGet( unsigned * pInfo, int i )
{
    return 3 & (pInfo[i >> 4] >> ((i & 15) << 1));
}
static inline void Fsim_ManTerSimInfoSet( unsigned * pInfo, int i, int Value )
{
    assert( Value >= FSIM_ZER && Value <= FSIM_UND );
    Value ^= Fsim_ManTerSimInfoGet( pInfo, i );
    pInfo[i >> 4] ^= (Value << ((i & 15) << 1));
}

static inline unsigned * Fsim_ManTerStateNext( unsigned * pState, int nWords )                      { return *((unsigned **)(pState + nWords));  }
static inline void       Fsim_ManTerStateSetNext( unsigned * pState, int nWords, unsigned * pNext ) { *((unsigned **)(pState + nWords)) = pNext; }

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimulateCi( Fsim_Man_t * p, int iNode, int iCi )
{
    Fsim_ManTerSimInfoSet( p->pDataSim, iNode, Fsim_ManTerSimInfoGet(p->pDataSimCis, iCi) );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimulateCo( Fsim_Man_t * p, int iCo, int iFan0 )
{
    int Value = Fsim_ManTerSimInfoGet( p->pDataSim, Fsim_Lit2Var(iFan0) );
    Fsim_ManTerSimInfoSet( p->pDataSimCos, iCo, Aig_XsimNotCond( Value, Fsim_LitIsCompl(iFan0) ) );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimulateNode( Fsim_Man_t * p, int iNode, int iFan0, int iFan1 )
{
    int Value0 = Fsim_ManTerSimInfoGet( p->pDataSim, Fsim_Lit2Var(iFan0) );
    int Value1 = Fsim_ManTerSimInfoGet( p->pDataSim, Fsim_Lit2Var(iFan1) );
    Fsim_ManTerSimInfoSet( p->pDataSim, iNode, Aig_XsimAndCond( Value0, Fsim_LitIsCompl(iFan0), Value1, Fsim_LitIsCompl(iFan1) ) );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimInfoInit( Fsim_Man_t * p )
{
    int iPioNum, i;
    Vec_IntForEachEntry( p->vCis2Ids, iPioNum, i )
    {
        if ( iPioNum < p->nPis )
            Fsim_ManTerSimInfoSet( p->pDataSimCis, i, FSIM_UND );
        else
            Fsim_ManTerSimInfoSet( p->pDataSimCis, i, FSIM_ZER );
    }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimInfoTransfer( Fsim_Man_t * p )
{
    int iPioNum, i;
    Vec_IntForEachEntry( p->vCis2Ids, iPioNum, i )
    {
        if ( iPioNum < p->nPis )
            Fsim_ManTerSimInfoSet( p->pDataSimCis, i, FSIM_UND );
        else
            Fsim_ManTerSimInfoSet( p->pDataSimCis, i, Fsim_ManTerSimInfoGet( p->pDataSimCos, p->nPos+iPioNum-p->nPis ) );
    }
}


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

  Synopsis    [Computes hash value of the node using its simulation info.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fsim_ManTerStateHash( unsigned * pState, int nWords, int nTableSize )
{
    static int s_FPrimes[128] = { 
        1009, 1049, 1093, 1151, 1201, 1249, 1297, 1361, 1427, 1459, 
        1499, 1559, 1607, 1657, 1709, 1759, 1823, 1877, 1933, 1997, 
        2039, 2089, 2141, 2213, 2269, 2311, 2371, 2411, 2467, 2543, 
        2609, 2663, 2699, 2741, 2797, 2851, 2909, 2969, 3037, 3089, 
        3169, 3221, 3299, 3331, 3389, 3461, 3517, 3557, 3613, 3671, 
        3719, 3779, 3847, 3907, 3943, 4013, 4073, 4129, 4201, 4243, 
        4289, 4363, 4441, 4493, 4549, 4621, 4663, 4729, 4793, 4871, 
        4933, 4973, 5021, 5087, 5153, 5227, 5281, 5351, 5417, 5471, 
        5519, 5573, 5651, 5693, 5749, 5821, 5861, 5923, 6011, 6073, 
        6131, 6199, 6257, 6301, 6353, 6397, 6481, 6563, 6619, 6689, 
        6737, 6803, 6863, 6917, 6977, 7027, 7109, 7187, 7237, 7309, 
        7393, 7477, 7523, 7561, 7607, 7681, 7727, 7817, 7877, 7933, 
        8011, 8039, 8059, 8081, 8093, 8111, 8123, 8147
    };
    unsigned uHash;
    int i;
    uHash = 0;
    for ( i = 0; i < nWords; i++ )
        uHash ^= pState[i] * s_FPrimes[i & 0x7F];
    return uHash % nTableSize;
}

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

  Synopsis    [Inserts value into the table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fsim_ManTerStateLookup( unsigned * pState, int nWords, unsigned ** pBins, int nBins )
{
    unsigned * pEntry;
    int Hash;
    Hash = Fsim_ManTerStateHash( pState, nWords, nBins );
    for ( pEntry = pBins[Hash]; pEntry; pEntry = Fsim_ManTerStateNext(pEntry, nWords) )
        if ( !memcmp( pEntry, pState, sizeof(unsigned) * nWords ) )
            return 1;
    return 0;
}

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

  Synopsis    [Inserts value into the table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Fsim_ManTerStateInsert( unsigned * pState, int nWords, unsigned ** pBins, int nBins )
{
    int Hash = Fsim_ManTerStateHash( pState, nWords, nBins );
    assert( !Fsim_ManTerStateLookup( pState, nWords, pBins, nBins ) );
    Fsim_ManTerStateSetNext( pState, nWords, pBins[Hash] );
    pBins[Hash] = pState;    
}

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

  Synopsis    [Inserts value into the table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
unsigned * Fsim_ManTerStateCreate( unsigned * pInfo, int nPis, int nCis, int nWords )
{
    unsigned * pRes;
    int i;
    pRes = (unsigned *)ABC_CALLOC( char, sizeof(unsigned) * nWords + sizeof(unsigned *) );
    for ( i = nPis; i < nCis; i++ )
        Fsim_ManTerSimInfoSet( pRes, i-nPis, Fsim_ManTerSimInfoGet(pInfo, i) );
    return pRes;    
}

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

  Synopsis    [Inserts value into the table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Fsim_ManTerStatePrint( unsigned * pState, int nRegs )
{
    int i, Value, nZeros = 0, nOnes = 0, nDcs = 0;
    for ( i = 0; i < nRegs; i++ )
    {
        Value = (Aig_InfoHasBit( pState, 2 * i + 1 ) << 1) | Aig_InfoHasBit( pState, 2 * i );
        if ( Value == 1 )
            printf( "0" ), nZeros++;
        else if ( Value == 2 )
            printf( "1" ), nOnes++;
        else if ( Value == 3 )
            printf( "x" ), nDcs++;
        else
            assert( 0 );
    }
    printf( " (0=%5d, 1=%5d, x=%5d)\n", nZeros, nOnes, nDcs );
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Fsim_ManTerSimulateRound( Fsim_Man_t * p )
{
    int * pCur, * pEnd;
    int iCis = 0, iCos = 0;
    if ( Aig_ObjRefs(Aig_ManConst1(p->pAig)) )
        Fsim_ManTerSimInfoSet( p->pDataSimCis, 1, FSIM_ONE );
    pCur = p->pDataAig2 + 6;
    pEnd = p->pDataAig2 + 3 * p->nObjs;
    while ( pCur < pEnd )
    {
        if ( pCur[1] == 0 )
            Fsim_ManTerSimulateCi( p, pCur[0], iCis++ );
        else if ( pCur[2] == 0 )
            Fsim_ManTerSimulateCo( p, iCos++, pCur[1] );
        else
            Fsim_ManTerSimulateNode( p, pCur[0], pCur[1], pCur[2] );
        pCur += 3;
    }
    assert( iCis == p->nCis );
    assert( iCos == p->nCos );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Fsim_ManTerSimulate( Aig_Man_t * pAig, int fVerbose )
{
    Fsim_Man_t * p;
    Vec_Ptr_t * vStates;
    unsigned ** pBins, * pState;
    int i, nWords, nBins;
    clock_t clk, clkTotal = clock();
    assert( Aig_ManRegNum(pAig) > 0 );
    // create manager
    clk = clock();
    p = Fsim_ManCreate( pAig );
    if ( fVerbose )
    {
        printf( "Obj = %8d (%8d). Cut = %6d. Front = %6d.  FrtMem = %7.2f MB. ", 
            p->nObjs, p->nCis + p->nNodes, p->nCrossCutMax, p->nFront, 
            4.0*Aig_BitWordNum(2 * p->nFront)/(1<<20) );
        ABC_PRT( "Time", clock() - clk );
    }
    // create simulation frontier
    clk = clock();
    Fsim_ManFront( p, 0 );
    if ( fVerbose )
    {
        printf( "Max ID = %8d. Log max ID = %2d.  AigMem = %7.2f MB (%5.2f byte/obj).  ", 
            p->iNumber, Aig_Base2Log(p->iNumber), 
            1.0*(p->pDataCur-p->pDataAig)/(1<<20), 
            1.0*(p->pDataCur-p->pDataAig)/p->nObjs ); 
        ABC_PRT( "Time", clock() - clk );
    }
    // allocate storage for terminary states
    nWords  = Abc_BitWordNum( 2*Aig_ManRegNum(pAig) );
    vStates = Vec_PtrAlloc( 1000 );
    nBins   = Abc_PrimeCudd( 500 );
    pBins   = ABC_ALLOC( unsigned *, nBins );
    memset( pBins, 0, sizeof(unsigned *) * nBins );
    // perform simulation
    assert( p->pDataSim == NULL );
    p->pDataSim = ABC_ALLOC( unsigned, Aig_BitWordNum(2 * p->nFront) * sizeof(unsigned) );
    p->pDataSimCis = ABC_ALLOC( unsigned, Aig_BitWordNum(2 * p->nCis) * sizeof(unsigned) );
    p->pDataSimCos = ABC_ALLOC( unsigned, Aig_BitWordNum(2 * p->nCos) * sizeof(unsigned) );
    Fsim_ManTerSimInfoInit( p );
    // hash the first state
    pState = Fsim_ManTerStateCreate( p->pDataSimCis, p->nPis, p->nCis, nWords );
    Vec_PtrPush( vStates, pState );
    Fsim_ManTerStateInsert( pState, nWords, pBins, nBins );
    // perform simuluation till convergence
    for ( i = 0; ; i++ )
    {
        Fsim_ManTerSimulateRound( p );
        Fsim_ManTerSimInfoTransfer( p );
        // hash the first state
        pState = Fsim_ManTerStateCreate( p->pDataSimCis, p->nPis, p->nCis, nWords );
        Vec_PtrPush( vStates, pState );
        if ( Fsim_ManTerStateLookup(pState, nWords, pBins, nBins) )
            break;
        Fsim_ManTerStateInsert( pState, nWords, pBins, nBins );
    }
    if ( fVerbose )
    {
        printf( "Maxcut = %8d.  AigMem = %7.2f MB.  SimMem = %7.2f MB.  ", 
            p->nCrossCutMax, 
            p->pDataAig2? 12.0*p->nObjs/(1<<20) : 1.0*(p->pDataCur-p->pDataAig)/(1<<20), 
            4.0*(Aig_BitWordNum(2 * p->nFront)+Aig_BitWordNum(2 * p->nCis)+Aig_BitWordNum(2 * p->nCos))/(1<<20) );
        ABC_PRT( "Sim time", clock() - clkTotal );
    }
    ABC_FREE( pBins );
    Fsim_ManDelete( p );
    return vStates;

}

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


ABC_NAMESPACE_IMPL_END

