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

  FileName    [giaEra.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Explicit reachability analysis.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "misc/mem/mem.h"

ABC_NAMESPACE_IMPL_START


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

// explicit state representation
typedef struct Gia_ObjEra_t_ Gia_ObjEra_t;
struct Gia_ObjEra_t_
{
    int            Num;          // ID of this state
    int            Cond;         // input condition
    int            iPrev;        // previous state
    int            iNext;        // next state in the hash table
    unsigned       pData[0];     // state bits
};

// explicit state reachability
typedef struct Gia_ManEra_t_ Gia_ManEra_t;
struct Gia_ManEra_t_
{
    Gia_Man_t *    pAig;         // user's AIG manager
    int            nWordsSim;       // 2^(PInum)
    int            nWordsDat;   // Abc_BitWordNum
    unsigned *     pDataSim;     // simulation data
    Mem_Fixed_t *  pMemory;      // memory manager
    Vec_Ptr_t *    vStates;      // reached states
    Gia_ObjEra_t * pStateNew;    // temporary state
    int            iCurState;    // the current state
    Vec_Int_t *    vBugTrace;    // the sequence of transitions
    Vec_Int_t *    vStgDump;     // STG written into a file
    // hash table for states
    int            nBins;         
    unsigned *     pBins;
};

static inline unsigned *     Gia_ManEraData( Gia_ManEra_t * p, int i )    { return p->pDataSim + i * p->nWordsSim;  }
static inline Gia_ObjEra_t * Gia_ManEraState( Gia_ManEra_t * p, int i )   { return (Gia_ObjEra_t *)Vec_PtrEntry(p->vStates, i);  }

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

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

  Synopsis    [Creates reachability manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_ManEra_t * Gia_ManEraCreate( Gia_Man_t * pAig )
{
    Vec_Ptr_t * vTruths;
    Gia_ManEra_t * p;
    unsigned * pTruth, * pSimInfo;
    int i;
    p = ABC_CALLOC( Gia_ManEra_t, 1 );
    p->pAig      = pAig;
    p->nWordsSim = Abc_TruthWordNum( Gia_ManPiNum(pAig) );
    p->nWordsDat = Abc_BitWordNum( Gia_ManRegNum(pAig) );
    p->pDataSim  = ABC_ALLOC( unsigned, p->nWordsSim*Gia_ManObjNum(pAig) );
    p->pMemory   = Mem_FixedStart( sizeof(Gia_ObjEra_t) + sizeof(unsigned) * p->nWordsDat );
    p->vStates   = Vec_PtrAlloc( 100000 );
    p->nBins     = Abc_PrimeCudd( 100000 );
    p->pBins     = ABC_CALLOC( unsigned, p->nBins );
    Vec_PtrPush( p->vStates, NULL );
    // assign primary input values
    vTruths = Vec_PtrAllocTruthTables( Gia_ManPiNum(pAig) );
    Vec_PtrForEachEntry( unsigned *, vTruths, pTruth, i )
    {
        pSimInfo = Gia_ManEraData( p, Gia_ObjId(pAig, Gia_ManPi(pAig, i)) );
        memcpy( pSimInfo, pTruth, sizeof(unsigned) * p->nWordsSim );
    }
    Vec_PtrFree( vTruths );
    // assign constant zero node
    pSimInfo = Gia_ManEraData( p, 0 );
    memset( pSimInfo, 0, sizeof(unsigned) * p->nWordsSim );
    p->vStgDump = Vec_IntAlloc( 1000 );
    return p;
}

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

  Synopsis    [Deletes reachability manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEraFree( Gia_ManEra_t * p )
{
    Mem_FixedStop( p->pMemory, 0 );
    Vec_IntFree( p->vStgDump );
    Vec_PtrFree( p->vStates );
    if ( p->vBugTrace ) Vec_IntFree( p->vBugTrace );
    ABC_FREE( p->pDataSim );
    ABC_FREE( p->pBins );
    ABC_FREE( p );
}

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

  Synopsis    [Creates new state.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_ObjEra_t * Gia_ManEraCreateState( Gia_ManEra_t * p )
{
    Gia_ObjEra_t * pNew;
    pNew = (Gia_ObjEra_t *)Mem_FixedEntryFetch( p->pMemory );
    pNew->Num = Vec_PtrSize( p->vStates );
    pNew->iPrev = 0;
    Vec_PtrPush( p->vStates, pNew );
    return pNew;
}


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

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

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEraStateHash( unsigned * pState, int nWordsSim, 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 < nWordsSim; i++ )
        uHash ^= pState[i] * s_FPrimes[i & 0x7F];
    return uHash % nTableSize;
}

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

  Synopsis    [Returns the place of this state in the table or NULL if it exists.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned * Gia_ManEraHashFind( Gia_ManEra_t * p, Gia_ObjEra_t * pState, int * pStateNum )
{
    Gia_ObjEra_t * pThis;
    unsigned * pPlace = p->pBins + Gia_ManEraStateHash( pState->pData, p->nWordsDat, p->nBins );
    for ( pThis = (*pPlace)? Gia_ManEraState(p, *pPlace) : NULL; pThis; 
          pPlace = (unsigned *)&pThis->iNext, pThis = (*pPlace)? Gia_ManEraState(p, *pPlace) : NULL )
              if ( !memcmp( pState->pData, pThis->pData, sizeof(unsigned) * p->nWordsDat ) )
              {
                  if ( pStateNum )
                      *pStateNum = pThis->Num;
                  return NULL;
              }
    if ( pStateNum )
      *pStateNum = -1;
    return pPlace;
}

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

  Synopsis    [Resizes the hash table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEraHashResize( Gia_ManEra_t * p )
{
    Gia_ObjEra_t * pThis;
    unsigned * pBinsOld, * piPlace;
    int nBinsOld, iNext, Counter, i;
    assert( p->pBins != NULL );
    // replace the table
    pBinsOld = p->pBins;
    nBinsOld = p->nBins;
    p->nBins = Abc_PrimeCudd( 3 * p->nBins ); 
    p->pBins = ABC_CALLOC( unsigned, p->nBins );
    // rehash the entries from the old table
    Counter = 0;
    for ( i = 0; i < nBinsOld; i++ )
    for ( pThis = (pBinsOld[i]? Gia_ManEraState(p, pBinsOld[i]) : NULL),
          iNext = (pThis? pThis->iNext : 0);  
          pThis;  pThis = (iNext? Gia_ManEraState(p, iNext) : NULL),   
          iNext = (pThis? pThis->iNext : 0)  )
    {
        assert( pThis->Num );
        pThis->iNext = 0;
        piPlace = Gia_ManEraHashFind( p, pThis, NULL );
        assert( *piPlace == 0 ); // should not be there
        *piPlace = pThis->Num;
        Counter++;
    }
    assert( Counter == Vec_PtrSize( p->vStates ) - 1 );
    ABC_FREE( pBinsOld );
}

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

  Synopsis    [Initialize register output to the given state.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManInsertState( Gia_ManEra_t * p, Gia_ObjEra_t * pState )
{
    Gia_Obj_t * pObj;
    unsigned * pSimInfo;
    int i;
    Gia_ManForEachRo( p->pAig, pObj, i )
    {
        pSimInfo = Gia_ManEraData( p, Gia_ObjId(p->pAig, pObj) );
        if ( Abc_InfoHasBit(pState->pData, i) )
            memset( pSimInfo, 0xff, sizeof(unsigned) * p->nWordsSim );
        else
            memset( pSimInfo, 0, sizeof(unsigned) * p->nWordsSim );
    }
}

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

  Synopsis    [Returns -1 if outputs are not asserted.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Gia_ManOutputAsserted( Gia_ManEra_t * p, Gia_Obj_t * pObj )
{
    unsigned * pInfo  = Gia_ManEraData( p, Gia_ObjId(p->pAig, pObj) );
    int w;
    for ( w = 0; w < p->nWordsSim; w++ )
        if ( pInfo[w] )
            return 32*w + Gia_WordFindFirstBit( pInfo[w] );
    return -1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Gia_ManSimulateCo( Gia_ManEra_t * p, Gia_Obj_t * pObj )
{
    int Id = Gia_ObjId(p->pAig, pObj);
    unsigned * pInfo  = Gia_ManEraData( p, Id );
    unsigned * pInfo0 = Gia_ManEraData( p, Gia_ObjFaninId0(pObj, Id) );
    int w;
    if ( Gia_ObjFaninC0(pObj) )
        for ( w = p->nWordsSim-1; w >= 0; w-- )
            pInfo[w] = ~pInfo0[w];
    else 
        for ( w = p->nWordsSim-1; w >= 0; w-- )
            pInfo[w] = pInfo0[w];
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Gia_ManSimulateNode( Gia_ManEra_t * p, Gia_Obj_t * pObj )
{
    int Id = Gia_ObjId(p->pAig, pObj);
    unsigned * pInfo  = Gia_ManEraData( p, Id );
    unsigned * pInfo0 = Gia_ManEraData( p, Gia_ObjFaninId0(pObj, Id) );
    unsigned * pInfo1 = Gia_ManEraData( p, Gia_ObjFaninId1(pObj, Id) );
    int w;
    if ( Gia_ObjFaninC0(pObj) )
    {
        if (  Gia_ObjFaninC1(pObj) )
            for ( w = p->nWordsSim-1; w >= 0; w-- )
                pInfo[w] = ~(pInfo0[w] | pInfo1[w]);
        else 
            for ( w = p->nWordsSim-1; w >= 0; w-- )
                pInfo[w] = ~pInfo0[w] & pInfo1[w];
    }
    else 
    {
        if (  Gia_ObjFaninC1(pObj) )
            for ( w = p->nWordsSim-1; w >= 0; w-- )
                pInfo[w] = pInfo0[w] & ~pInfo1[w];
        else 
            for ( w = p->nWordsSim-1; w >= 0; w-- )
                pInfo[w] = pInfo0[w] & pInfo1[w];
    }
}

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

  Synopsis    [Performs one iteration of reachability analysis.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManPerformOneIter( Gia_ManEra_t * p )
{
    Gia_Obj_t * pObj;
    int i;
    Gia_ManForEachObj1( p->pAig, pObj, i )
    {
        if ( Gia_ObjIsAnd(pObj) )
            Gia_ManSimulateNode( p, pObj );
        else if ( Gia_ObjIsCo(pObj) )
            Gia_ManSimulateCo( p, pObj );
    }
}

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

  Synopsis    [Performs one iteration of reachability analysis.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Gia_ManCollectBugTrace( Gia_ManEra_t * p, Gia_ObjEra_t * pState, int iCond )
{
    Vec_Int_t * vTrace;
    vTrace = Vec_IntAlloc( 10 );
    Vec_IntPush( vTrace, iCond );
    for ( ; pState; pState = pState->iPrev ? Gia_ManEraState(p, pState->iPrev) : NULL )
        Vec_IntPush( vTrace, pState->Cond );
    Vec_IntReverseOrder( vTrace );
    return vTrace;
}

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

  Synopsis    [Counts the depth of state transitions leading ot this state.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCountDepth( Gia_ManEra_t * p )
{
    Gia_ObjEra_t * pState;
    int Counter = 0;
    pState = (Gia_ObjEra_t *)Vec_PtrEntryLast( p->vStates );
    if ( pState->iPrev == 0 && Vec_PtrSize(p->vStates) > 3 )
        pState = (Gia_ObjEra_t *)Vec_PtrEntry( p->vStates, Vec_PtrSize(p->vStates) - 2 );
    for ( ; pState; pState = pState->iPrev ? Gia_ManEraState(p, pState->iPrev) : NULL )
        Counter++;
    return Counter;
}

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

  Synopsis    [Analized reached states.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManAnalyzeResult( Gia_ManEra_t * p, Gia_ObjEra_t * pState, int fMiter, int fStgDump )
{
    Gia_Obj_t * pObj;
    unsigned * pSimInfo, * piPlace, uOutput = 0;
    int i, k, iCond, nMints, iNextState;
    // check if the miter is asserted
    if ( fMiter )
    {
        Gia_ManForEachPo( p->pAig, pObj, i )
        {
            iCond = Gia_ManOutputAsserted( p, pObj );
            if ( iCond >= 0 )
            {
                p->vBugTrace = Gia_ManCollectBugTrace( p, pState, iCond );
                return 1;
            }
        }
    }
    // collect reached states 
    nMints = (1 << Gia_ManPiNum(p->pAig));
    for ( k = 0; k < nMints; k++ )
    {
        if ( p->pStateNew == NULL )
            p->pStateNew = Gia_ManEraCreateState( p );
        p->pStateNew->pData[p->nWordsDat-1] = 0;
        Gia_ManForEachRi( p->pAig, pObj, i )
        {
            pSimInfo = Gia_ManEraData( p, Gia_ObjId(p->pAig, pObj) );
            if ( Abc_InfoHasBit(p->pStateNew->pData, i) != Abc_InfoHasBit(pSimInfo, k) )
                Abc_InfoXorBit( p->pStateNew->pData, i );
        }
        if ( fStgDump )
        {
            uOutput = 0;
            Gia_ManForEachPo( p->pAig, pObj, i )
            {
                pSimInfo = Gia_ManEraData( p, Gia_ObjId(p->pAig, pObj) );
                if ( Abc_InfoHasBit(pSimInfo, k) && i < 32 )
                    Abc_InfoXorBit( &uOutput, i );
            }
        }
        piPlace = Gia_ManEraHashFind( p, p->pStateNew, &iNextState );
        if ( fStgDump ) Vec_IntPush( p->vStgDump, k );
        if ( fStgDump ) Vec_IntPush( p->vStgDump, pState->Num );
        if ( piPlace == NULL )
        {
            if ( fStgDump ) Vec_IntPush( p->vStgDump, iNextState );
            if ( fStgDump ) Vec_IntPush( p->vStgDump, uOutput );
            continue;
        }
        if ( fStgDump ) Vec_IntPush( p->vStgDump, p->pStateNew->Num );
        if ( fStgDump ) Vec_IntPush( p->vStgDump, uOutput );
//printf( "Inserting %d ", Vec_PtrSize(p->vStates) );
//Extra_PrintBinary( stdout, p->pStateNew->pData, Gia_ManRegNum(p->pAig) );  printf( "\n" );
        assert( *piPlace == 0 );
        *piPlace = p->pStateNew->Num;
        p->pStateNew->Cond = k;
        p->pStateNew->iPrev = pState->Num;
        p->pStateNew->iNext = 0;
        p->pStateNew = NULL;
        // expand hash table if needed
        if ( Vec_PtrSize(p->vStates) > 2 * p->nBins )
            Gia_ManEraHashResize( p );
    }
    return 0;
}

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

  Synopsis    [Resizes the hash table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCollectReachable( Gia_Man_t * pAig, int nStatesMax, int fMiter, int fDumpFile, int fVerbose )
{ 
    Gia_ManEra_t * p;
    Gia_ObjEra_t * pState;
    int Hash;
    abctime clk = Abc_Clock();
    int RetValue = 1;
    assert( Gia_ManPiNum(pAig) <= 12 );
    assert( Gia_ManRegNum(pAig) > 0 );
    p = Gia_ManEraCreate( pAig );
    // create init state
    pState = Gia_ManEraCreateState( p );
    pState->Cond  = 0; 
    pState->iPrev = 0;
    pState->iNext = 0; 
    memset( pState->pData, 0, sizeof(unsigned) * p->nWordsDat );
    Hash = Gia_ManEraStateHash(pState->pData, p->nWordsDat, p->nBins);
    p->pBins[ Hash ] = pState->Num;
    // process reachable states
    while ( p->iCurState < Vec_PtrSize( p->vStates ) - 1 )
    {
        if ( Vec_PtrSize(p->vStates) >= nStatesMax )
        {
            printf( "Reached the limit on states traversed (%d).  ", nStatesMax );
            RetValue = -1;
            break;
        }
        pState = Gia_ManEraState( p, ++p->iCurState );
        if ( p->iCurState > 1 && pState->iPrev == 0 )
            continue;
//printf( "Extracting %d  ", p->iCurState );
//Extra_PrintBinary( stdout, p->pStateNew->pData, Gia_ManRegNum(p->pAig) );  printf( "\n" );
        Gia_ManInsertState( p, pState );
        Gia_ManPerformOneIter( p );
        if ( Gia_ManAnalyzeResult( p, pState, fMiter, fDumpFile ) && fMiter )
        {
            RetValue = 0;
            printf( "Miter failed in state %d after %d transitions.  ", 
                p->iCurState, Vec_IntSize(p->vBugTrace)-1 );
            break;
        }
        if ( fVerbose && p->iCurState % 5000 == 0 )
        {
            printf( "States =%10d. Reached =%10d. R = %5.3f. Depth =%6d. Mem =%9.2f MB.  ", 
                p->iCurState, Vec_PtrSize(p->vStates), 1.0*p->iCurState/Vec_PtrSize(p->vStates), Gia_ManCountDepth(p), 
                (1.0/(1<<20))*(1.0*Vec_PtrSize(p->vStates)*(sizeof(Gia_ObjEra_t) + sizeof(unsigned) * p->nWordsDat) + 
                   1.0*p->nBins*sizeof(unsigned) + 1.0*p->vStates->nCap * sizeof(void*)) );
            ABC_PRT( "Time", Abc_Clock() - clk );
        }
    }
    printf( "Reachability analysis traversed %d states with depth %d.  ", p->iCurState-1, Gia_ManCountDepth(p) );
    ABC_PRT( "Time", Abc_Clock() - clk );
    if ( fDumpFile )
    {
        char * pFileName = "test.stg";
        FILE * pFile = fopen( pFileName, "wb" );
        if ( pFile == NULL )
            printf( "Cannot open file \"%s\" for writing.\n", pFileName );
        else
        {
            Gia_ManStgPrint( pFile, p->vStgDump, Gia_ManPiNum(pAig), Gia_ManPoNum(pAig), p->iCurState-1 );
            fclose( pFile );
            printf( "Extracted STG was written into file \"%s\".\n", pFileName );
        }
    }
    Gia_ManEraFree( p );
    return RetValue;
}

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


ABC_NAMESPACE_IMPL_END