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

  FileName    [saigSimMv.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Sequential AIG package.]

  Synopsis    [Multi-valued simulation.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "saig.h"

ABC_NAMESPACE_IMPL_START


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

#define SAIG_DIFF_VALUES  8
#define SAIG_UNDEF_VALUE  0x1ffffffe  //536870910

// old AIG
typedef struct Saig_MvObj_t_ Saig_MvObj_t;
struct Saig_MvObj_t_
{
    int              iFan0;
    int              iFan1;
    unsigned         Type   :  3;
    unsigned         Value  : 29;
};

// new AIG
typedef struct Saig_MvAnd_t_ Saig_MvAnd_t;
struct Saig_MvAnd_t_
{
    int              iFan0;
    int              iFan1;
    int              iNext;
};

// simulation manager
typedef struct Saig_MvMan_t_ Saig_MvMan_t;
struct Saig_MvMan_t_
{
    // user data
    Aig_Man_t *      pAig;         // original AIG    
    // parameters
    int              nStatesMax;   // maximum number of states
    int              nLevelsMax;   // maximum number of levels
    int              nValuesMax;   // maximum number of values
    int              nFlops;       // number of flops
    // compacted AIG
    Saig_MvObj_t *   pAigOld;      // AIG objects
    Vec_Ptr_t *      vFlops;       // collected flops
    Vec_Int_t *      vXFlops;      // flops that had at least one X-value
    Vec_Ptr_t *      vTired;       // collected flops
    unsigned *       pTStates;     // hash table for states
    int              nTStatesSize; // hash table size
    Aig_MmFixed_t *  pMemStates;   // memory for states
    Vec_Ptr_t *      vStates;      // reached states
    int *            pRegsUndef;   // count the number of undef values
    int **           pRegsValues;  // write the first different values
    int *            nRegsValues;  // count the number of different values
    int              nRUndefs;     // the number of undef registers
    int              nRValues[SAIG_DIFF_VALUES+1]; // the number of registers with given values
    // internal AIG
    Saig_MvAnd_t *   pAigNew;      // AIG nodes
    int              nObjsAlloc;   // the number of objects allocated 
    int              nObjs;        // the number of objects
    int              nPis;         // the number of primary inputs
    int *            pTNodes;      // hash table
    int              nTNodesSize;  // hash table size
    unsigned char *  pLevels;      // levels of AIG nodes
};

static inline int    Saig_MvObjFaninC0( Saig_MvObj_t * pObj )  { return pObj->iFan0 & 1;           }
static inline int    Saig_MvObjFaninC1( Saig_MvObj_t * pObj )  { return pObj->iFan1 & 1;           }
static inline int    Saig_MvObjFanin0( Saig_MvObj_t * pObj )   { return pObj->iFan0 >> 1;          }
static inline int    Saig_MvObjFanin1( Saig_MvObj_t * pObj )   { return pObj->iFan1 >> 1;          }

static inline int    Saig_MvConst0()                           { return 1;                         }
static inline int    Saig_MvConst1()                           { return 0;                         }
static inline int    Saig_MvConst( int c )                     { return !c;                        }
static inline int    Saig_MvUndef()                            { return SAIG_UNDEF_VALUE;          }

static inline int    Saig_MvIsConst0( int iNode )              { return iNode == 1;                }
static inline int    Saig_MvIsConst1( int iNode )              { return iNode == 0;                }
static inline int    Saig_MvIsConst( int iNode )               { return iNode  < 2;                }
static inline int    Saig_MvIsUndef( int iNode )               { return iNode == SAIG_UNDEF_VALUE; }

static inline int    Saig_MvRegular( int iNode )               { return (iNode & ~01);             }
static inline int    Saig_MvNot( int iNode )                   { return (iNode ^  01);             }
static inline int    Saig_MvNotCond( int iNode, int c )        { return (iNode ^ (c));             }
static inline int    Saig_MvIsComplement( int iNode )          { return (int)(iNode & 01);         }

static inline int    Saig_MvLit2Var( int iNode )               { return (iNode >> 1);              }
static inline int    Saig_MvVar2Lit( int iVar )                { return (iVar << 1);               }
static inline int    Saig_MvLev( Saig_MvMan_t * p, int iNode ) { return p->pLevels[iNode >> 1];    }

// iterator over compacted objects
#define Saig_MvManForEachObj( pAig, pEntry ) \
    for ( pEntry = pAig; pEntry->Type != AIG_OBJ_VOID; pEntry++ )

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

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

  Synopsis    [Creates reduced manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Saig_MvObj_t * Saig_ManCreateReducedAig( Aig_Man_t * p, Vec_Ptr_t ** pvFlops )
{
    Saig_MvObj_t * pAig, * pEntry;
    Aig_Obj_t * pObj;
    int i;
    *pvFlops = Vec_PtrAlloc( Aig_ManRegNum(p) );
    pAig = ABC_CALLOC( Saig_MvObj_t, Aig_ManObjNumMax(p)+1 );
    Aig_ManForEachObj( p, pObj, i )
    {
        pEntry = pAig + i;
        pEntry->Type = pObj->Type;
        if ( Aig_ObjIsPi(pObj) || i == 0 )
        {
            if ( Saig_ObjIsLo(p, pObj) )
            {
                pEntry->iFan0 = (Saig_ObjLoToLi(p, pObj)->Id << 1);
                pEntry->iFan1 = -1;
                Vec_PtrPush( *pvFlops, pEntry );
            }
            continue;
        }
        pEntry->iFan0 = (Aig_ObjFaninId0(pObj) << 1) | Aig_ObjFaninC0(pObj);
        if ( Aig_ObjIsPo(pObj) )
            continue;
        assert( Aig_ObjIsNode(pObj) );
        pEntry->iFan1 = (Aig_ObjFaninId1(pObj) << 1) | Aig_ObjFaninC1(pObj);
    }
    pEntry = pAig + Aig_ManObjNumMax(p);
    pEntry->Type = AIG_OBJ_VOID;
    return pAig;
}

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

  Synopsis    [Creates a new node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Saig_MvCreateObj( Saig_MvMan_t * p, int iFan0, int iFan1 )
{
    Saig_MvAnd_t * pNode;
    if ( p->nObjs == p->nObjsAlloc )
    {
        p->pAigNew = ABC_REALLOC( Saig_MvAnd_t, p->pAigNew, 2*p->nObjsAlloc );
        p->pLevels = ABC_REALLOC( unsigned char, p->pLevels, 2*p->nObjsAlloc );
        p->nObjsAlloc *= 2;
    }
    pNode = p->pAigNew + p->nObjs;
    pNode->iFan0 = iFan0;
    pNode->iFan1 = iFan1;
    pNode->iNext = 0;
    if ( iFan0 || iFan1 )
        p->pLevels[p->nObjs] = 1 + ABC_MAX( Saig_MvLev(p, iFan0), Saig_MvLev(p, iFan1) );
    else
        p->pLevels[p->nObjs] = 0, p->nPis++;
    return p->nObjs++;
}

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

  Synopsis    [Creates multi-valued simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Saig_MvMan_t * Saig_MvManStart( Aig_Man_t * pAig, int nFramesSatur )
{
    Saig_MvMan_t * p;
    int i;
    assert( Aig_ManRegNum(pAig) > 0 );
    p = (Saig_MvMan_t *)ABC_ALLOC( Saig_MvMan_t, 1 );
    memset( p, 0, sizeof(Saig_MvMan_t) );
    // set parameters
    p->pAig         = pAig;
    p->nStatesMax   = 2 * nFramesSatur + 100;
    p->nLevelsMax   = 4;
    p->nValuesMax   = SAIG_DIFF_VALUES;
    p->nFlops       = Aig_ManRegNum(pAig);
    // compacted AIG
    p->pAigOld      = Saig_ManCreateReducedAig( pAig, &p->vFlops );
    p->nTStatesSize = Aig_PrimeCudd( p->nStatesMax );
    p->pTStates     = ABC_CALLOC( unsigned, p->nTStatesSize );
    p->pMemStates   = Aig_MmFixedStart( sizeof(int) * (p->nFlops+1), p->nStatesMax );
    p->vStates      = Vec_PtrAlloc( p->nStatesMax );
    Vec_PtrPush( p->vStates, NULL );
    p->pRegsUndef   = ABC_CALLOC( int, p->nFlops );
    p->pRegsValues  = ABC_ALLOC( int *, p->nFlops );
    p->pRegsValues[0] = ABC_ALLOC( int, p->nValuesMax * p->nFlops );
    for ( i = 1; i < p->nFlops; i++ )
        p->pRegsValues[i] = p->pRegsValues[i-1] + p->nValuesMax;
    p->nRegsValues  = ABC_CALLOC( int, p->nFlops );
    p->vTired       = Vec_PtrAlloc( 100 );
    // internal AIG
    p->nObjsAlloc   = 1000000;
    p->pAigNew      = ABC_ALLOC( Saig_MvAnd_t, p->nObjsAlloc );
    p->nTNodesSize  = Aig_PrimeCudd( p->nObjsAlloc / 3 );
    p->pTNodes      = ABC_CALLOC( int, p->nTNodesSize );
    p->pLevels      = ABC_ALLOC( unsigned char, p->nObjsAlloc );
    Saig_MvCreateObj( p, 0, 0 );
    return p;
}

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

  Synopsis    [Destroys multi-valued simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Saig_MvManStop( Saig_MvMan_t * p )
{
    Aig_MmFixedStop( p->pMemStates, 0 );
    Vec_PtrFree( p->vStates );
    Vec_IntFreeP( &p->vXFlops );
    Vec_PtrFree( p->vFlops );
    Vec_PtrFree( p->vTired );
    ABC_FREE( p->pRegsValues[0] );
    ABC_FREE( p->pRegsValues );
    ABC_FREE( p->nRegsValues );
    ABC_FREE( p->pRegsUndef );
    ABC_FREE( p->pAigOld );
    ABC_FREE( p->pTStates );
    ABC_FREE( p->pAigNew );
    ABC_FREE( p->pTNodes );
    ABC_FREE( p->pLevels );
    ABC_FREE( p );
}

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

  Synopsis    [Hashing the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Saig_MvHash( int iFan0, int iFan1, int TableSize ) 
{
    unsigned Key = 0;
    assert( iFan0 < iFan1 );
    Key ^= Saig_MvLit2Var(iFan0) * 7937;
    Key ^= Saig_MvLit2Var(iFan1) * 2971;
    Key ^= Saig_MvIsComplement(iFan0) * 911;
    Key ^= Saig_MvIsComplement(iFan1) * 353;
    return (int)(Key % TableSize);
}

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

  Synopsis    [Returns the place where this node is stored (or should be stored).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int * Saig_MvTableFind( Saig_MvMan_t * p, int iFan0, int iFan1 )
{
    Saig_MvAnd_t * pEntry;
    int * pPlace = p->pTNodes + Saig_MvHash( iFan0, iFan1, p->nTNodesSize );
    for ( pEntry = (*pPlace)? p->pAigNew + *pPlace : NULL; pEntry; 
          pPlace = &pEntry->iNext, pEntry = (*pPlace)? p->pAigNew + *pPlace : NULL )
              if ( pEntry->iFan0 == iFan0 && pEntry->iFan1 == iFan1 )
                  break;
    return pPlace;
}

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

  Synopsis    [Performs an AND-operation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Saig_MvAnd( Saig_MvMan_t * p, int iFan0, int iFan1, int fFirst )
{
    if ( iFan0 == iFan1 )
        return iFan0;
    if ( iFan0 == Saig_MvNot(iFan1) )
        return Saig_MvConst0();
    if ( Saig_MvIsConst(iFan0) )
        return Saig_MvIsConst1(iFan0) ? iFan1 : Saig_MvConst0();
    if ( Saig_MvIsConst(iFan1) )
        return Saig_MvIsConst1(iFan1) ? iFan0 : Saig_MvConst0();
    if ( Saig_MvIsUndef(iFan0) || Saig_MvIsUndef(iFan1) )
        return Saig_MvUndef();
//    if ( Saig_MvLev(p, iFan0) >= p->nLevelsMax || Saig_MvLev(p, iFan1) >= p->nLevelsMax )
//        return Saig_MvUndef();

    // go undef after the first frame
    if ( !fFirst )
        return Saig_MvUndef();

    if ( iFan0 > iFan1 )
    {
        int Temp = iFan0;
        iFan0 = iFan1;
        iFan1 = Temp;
    }
    {
        int * pPlace;
        pPlace = Saig_MvTableFind( p, iFan0, iFan1 );
        if ( *pPlace == 0 )
        {
            if ( pPlace >= (int*)p->pAigNew && pPlace < (int*)(p->pAigNew + p->nObjsAlloc) )
            {
                int iPlace = pPlace - (int*)p->pAigNew;
                int iNode  = Saig_MvCreateObj( p, iFan0, iFan1 );
                ((int*)p->pAigNew)[iPlace] = iNode;
                return Saig_MvVar2Lit( iNode );
            }
            else
                *pPlace = Saig_MvCreateObj( p, iFan0, iFan1 );
        }
        return Saig_MvVar2Lit( *pPlace );
    }
}

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

  Synopsis    [Propagates one edge.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Saig_MvSimulateValue0( Saig_MvObj_t * pAig, Saig_MvObj_t * pObj )
{
    Saig_MvObj_t * pObj0 = pAig + Saig_MvObjFanin0(pObj);
    if ( Saig_MvIsUndef( pObj0->Value ) )
        return Saig_MvUndef();
    return Saig_MvNotCond( pObj0->Value, Saig_MvObjFaninC0(pObj) );
}
static inline int Saig_MvSimulateValue1( Saig_MvObj_t * pAig, Saig_MvObj_t * pObj )
{
    Saig_MvObj_t * pObj1 = pAig + Saig_MvObjFanin1(pObj);
    if ( Saig_MvIsUndef( pObj1->Value ) )
        return Saig_MvUndef();
    return Saig_MvNotCond( pObj1->Value, Saig_MvObjFaninC1(pObj) );
}

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

  Synopsis    [Prints MV state.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Saig_MvPrintState( int Iter, Saig_MvMan_t * p )
{
    Saig_MvObj_t * pEntry;
    int i;
    printf( "%3d : ", Iter );
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
    {
        if ( pEntry->Value == SAIG_UNDEF_VALUE )
            printf( "    *" );
        else
            printf( "%5d", pEntry->Value );
    }
    printf( "\n" );
}

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

  Synopsis    [Performs one iteration of simulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Saig_MvSimulateFrame( Saig_MvMan_t * p, int fFirst, int fVerbose )
{
    Saig_MvObj_t * pEntry;
    int i;
    Saig_MvManForEachObj( p->pAigOld, pEntry )
    {
        if ( pEntry->Type == AIG_OBJ_AND )
        {
            pEntry->Value = Saig_MvAnd( p, 
                Saig_MvSimulateValue0(p->pAigOld, pEntry),
                Saig_MvSimulateValue1(p->pAigOld, pEntry), fFirst );
        }
        else if ( pEntry->Type == AIG_OBJ_PO )
            pEntry->Value = Saig_MvSimulateValue0(p->pAigOld, pEntry);
        else if ( pEntry->Type == AIG_OBJ_PI )
        {
            if ( pEntry->iFan1 == 0 ) // true PI
            {
                if ( fFirst )
                    pEntry->Value = Saig_MvVar2Lit( Saig_MvCreateObj( p, 0, 0 ) );
                else
                    pEntry->Value = SAIG_UNDEF_VALUE;
            }
//            else if ( fFirst ) // register output
//                pEntry->Value = Saig_MvConst0();
//            else
//                pEntry->Value = Saig_MvSimulateValue0(p->pAigOld, pEntry);
        }
        else if ( pEntry->Type == AIG_OBJ_CONST1 )
            pEntry->Value = Saig_MvConst1();
        else if ( pEntry->Type != AIG_OBJ_NONE )
            assert( 0 );
    }
    // transfer to registers
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
        pEntry->Value = Saig_MvSimulateValue0( p->pAigOld, pEntry );
}


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

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

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Saig_MvSimHash( unsigned * pState, int nFlops, int TableSize )
{
    static int s_SPrimes[16] = { 
     1610612741,
     805306457,
     402653189,
     201326611,
     100663319,
     50331653,
     25165843,
     12582917,
     6291469,
     3145739,
     1572869,
     786433,
     393241,
     196613,
     98317,
     49157
    };
    unsigned uHash = 0;
    int i;
    for ( i = 0; i < nFlops; i++ )
        uHash ^= pState[i] * s_SPrimes[i & 0xF];
    return (int)(uHash % TableSize);
}

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

  Synopsis    [Returns the place where this state is stored (or should be stored).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned * Saig_MvSimTableFind( Saig_MvMan_t * p, unsigned * pState )
{
    unsigned * pEntry;
    unsigned * pPlace = p->pTStates + Saig_MvSimHash( pState+1, p->nFlops, p->nTStatesSize );
    for ( pEntry = (*pPlace)? (unsigned *)Vec_PtrEntry(p->vStates, *pPlace) : NULL; pEntry; 
          pPlace = pEntry, pEntry = (*pPlace)? (unsigned *)Vec_PtrEntry(p->vStates, *pPlace) : NULL )
              if ( memcmp( pEntry+1, pState+1, sizeof(int)*p->nFlops ) == 0 )
                  break;
    return pPlace;
}

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

  Synopsis    [Saves current state.]

  Description [Returns -1 if there is no fixed point.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Saig_MvSaveState( Saig_MvMan_t * p )
{
    Saig_MvObj_t * pEntry;
    unsigned * pState, * pPlace;
    int i;
    pState = (unsigned *)Aig_MmFixedEntryFetch( p->pMemStates );
    pState[0] = 0;
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
        pState[i+1] = pEntry->Value;
    pPlace = Saig_MvSimTableFind( p, pState );
    if ( *pPlace )
        return *pPlace;
    *pPlace = Vec_PtrSize( p->vStates );
    Vec_PtrPush( p->vStates, pState );
    return -1;
}

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

  Synopsis    [Performs multi-valued simulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Saig_MvManPostProcess( Saig_MvMan_t * p, int iState )
{
    Saig_MvObj_t * pEntry;
    unsigned * pState;
    int i, k, j, nTotal = 0, Counter = 0, iFlop;
    Vec_Int_t * vUniques = Vec_IntAlloc( 100 );
    Vec_Int_t * vCounter = Vec_IntAlloc( 100 );
    // count registers that never became undef
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
        if ( p->pRegsUndef[i] == 0 )
            nTotal++;
    printf( "The number of registers that never became undef = %d. (Total = %d.)\n", nTotal, p->nFlops );
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
    {
        if ( p->pRegsUndef[i] )
            continue;
        Vec_IntForEachEntry( vUniques, iFlop, k )
        {
            Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, j, 1 )
                if ( pState[iFlop+1] != pState[i+1] )
                    break;
            if ( j == Vec_PtrSize(p->vStates) )
            {
                Vec_IntAddToEntry( vCounter, k, 1 );
                break;
            }
        }
        if ( k == Vec_IntSize(vUniques) )
        {
            Vec_IntPush( vUniques, i );
            Vec_IntPush( vCounter, 1 );
        }
    }
    Vec_IntForEachEntry( vUniques, iFlop, i )
    {
        printf( "FLOP %5d : (%3d) ", iFlop, Vec_IntEntry(vCounter,i) );
/*
        for ( k = 0; k < p->nRegsValues[iFlop]; k++ )
            if ( p->pRegsValues[iFlop][k] == SAIG_UNDEF_VALUE )
                printf( "* " );
            else
                printf( "%d ", p->pRegsValues[iFlop][k] );
        printf( "\n" );
*/
        Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, k, 1 )
        {
            if ( k == iState+1 )
                printf( " # " );
            if ( pState[iFlop+1] == SAIG_UNDEF_VALUE )
                printf( "*" );
            else
                printf( "%d", pState[iFlop+1] );
        }
        printf( "\n" );
//        if ( ++Counter == 10 )
//            break;
    }

    Vec_IntFree( vUniques );
    Vec_IntFree( vCounter );
}

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

  Synopsis    [Performs multi-valued simulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Saig_MvManFindXFlops( Saig_MvMan_t * p )
{
    Vec_Int_t * vXFlops;
    unsigned * pState;
    int i, k;
    vXFlops = Vec_IntStart( p->nFlops );
    Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, i, 1 )
    {
        for ( k = 0; k < p->nFlops; k++ )
            if ( Saig_MvIsUndef(pState[k+1]) )
                Vec_IntWriteEntry( vXFlops, k, 1 );
    }
    return vXFlops;
}

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

  Synopsis    [Checks if the flop is an oscilator.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Saig_MvManCheckOscilator( Saig_MvMan_t * p, int iFlop )
{
    Vec_Int_t * vValues;
    unsigned * pState;
    int k, Per, Entry;
    // collect values of this flop
    vValues = Vec_IntAlloc( 100 );
    Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, k, 1 )
    {
        Vec_IntPush( vValues, pState[iFlop+1] );
//printf( "%d ", pState[iFlop+1] );
    }
//printf( "\n" );
    assert( Saig_MvIsConst0( Vec_IntEntry(vValues,0) ) );
    // look for constants
    for ( Per = 0; Per < Vec_IntSize(vValues)/2; Per++ )
    {
        // find the first non-const0
        Vec_IntForEachEntryStart( vValues, Entry, Per, Per )
            if ( !Saig_MvIsConst0(Entry) )
                break;
        if ( Per == Vec_IntSize(vValues) )
            break;
        // find the first const0
        Vec_IntForEachEntryStart( vValues, Entry, Per, Per )
            if ( Saig_MvIsConst0(Entry) )
                break;
        if ( Per == Vec_IntSize(vValues) )
            break;
        // try to determine period
        assert( Saig_MvIsConst0( Vec_IntEntry(vValues,Per) ) );
        for ( k = Per; k < Vec_IntSize(vValues); k++ )
            if ( Vec_IntEntry(vValues, k-Per) != Vec_IntEntry(vValues, k) )
                break;
        if ( k < Vec_IntSize(vValues) )
            continue;
        Vec_IntFree( vValues );
//printf( "Period = %d\n", Per );
        return Per;
    }
    Vec_IntFree( vValues );
    return 0;
}

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

  Synopsis    [Returns const0 and binary flops.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Saig_MvManFindConstBinaryFlops( Saig_MvMan_t * p, Vec_Int_t ** pvBinary )
{
    unsigned * pState;
    Vec_Int_t * vBinary, * vConst0;
    int i, k, fConst0;
    // detect constant flops
    vConst0 = Vec_IntAlloc( p->nFlops );
    vBinary = Vec_IntAlloc( p->nFlops );
    for ( k = 0; k < p->nFlops; k++ )
    {
        // check if this flop is constant 0 in all states
        fConst0 = 1;
        Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, i, 1 )
        {
            if ( !Saig_MvIsConst0(pState[k+1]) )
                fConst0 = 0;
            if ( Saig_MvIsUndef(pState[k+1]) )
                break;
        }
        if ( i < Vec_PtrSize(p->vStates) )
            continue;
        // the flop is binary-valued        
        if ( fConst0 ) 
            Vec_IntPush( vConst0, k );
        else
            Vec_IntPush( vBinary, k );
    }
    *pvBinary = vBinary;
    return vConst0;
}

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

  Synopsis    [Find oscilators.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Saig_MvManFindOscilators( Saig_MvMan_t * p, Vec_Int_t ** pvConst0 )
{
    Vec_Int_t * vBinary, * vOscils;
    int Entry, i;
    // detect constant flops
    *pvConst0 = Saig_MvManFindConstBinaryFlops( p, &vBinary );
    // check binary flops
    vOscils = Vec_IntAlloc( 100 );
    Vec_IntForEachEntry( vBinary, Entry, i )
        if ( Saig_MvManCheckOscilator( p, Entry ) )
            Vec_IntPush( vOscils, Entry );
    Vec_IntFree( vBinary );
    return vOscils;
}

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

  Synopsis    [Find constants and oscilators.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Saig_MvManCreateNextSkip( Saig_MvMan_t * p )
{
    Vec_Int_t * vConst0, * vOscils, * vXFlops;
    int i, Entry;
    vOscils = Saig_MvManFindOscilators( p, &vConst0 );
//printf( "Found %d constants and %d oscilators.\n", Vec_IntSize(vConst0), Vec_IntSize(vOscils) );
    vXFlops = Vec_IntAlloc( p->nFlops );
    Vec_IntFill( vXFlops, p->nFlops, 1 );
    Vec_IntForEachEntry( vConst0, Entry, i )
        Vec_IntWriteEntry( vXFlops, Entry, 0 );
    Vec_IntForEachEntry( vOscils, Entry, i )
        Vec_IntWriteEntry( vXFlops, Entry, 0 );
    Vec_IntFree( vOscils );
    Vec_IntFree( vConst0 );
    return vXFlops;
}

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

  Synopsis    [Finds equivalent flops.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Saig_MvManDeriveMap( Saig_MvMan_t * p, int fVerbose )
{
    Vec_Int_t * vConst0, * vBinValued;
    Vec_Ptr_t * vMap = NULL;
    Aig_Obj_t * pObj;
    unsigned * pState;
    int i, k, j, FlopK, FlopJ;
    int Counter1 = 0, Counter2 = 0;
    // prepare CI map
    vMap = Vec_PtrAlloc( Aig_ManPiNum(p->pAig) );
    Aig_ManForEachPi( p->pAig, pObj, i )
        Vec_PtrPush( vMap, pObj );
    // detect constant flops
    vConst0 = Saig_MvManFindConstBinaryFlops( p, &vBinValued );
    // set constants
    Vec_IntForEachEntry( vConst0, FlopK, k )
    {
        Vec_PtrWriteEntry( vMap, Saig_ManPiNum(p->pAig) + FlopK, Aig_ManConst0(p->pAig) );
        Counter1++;
    }
    Vec_IntFree( vConst0 ); 

    // detect equivalent (non-ternary flops)
    Vec_IntForEachEntry( vBinValued, FlopK, k )
    if ( FlopK >= 0 )
    Vec_IntForEachEntryStart( vBinValued, FlopJ, j, k+1 )
    if ( FlopJ >= 0 )
    {
        // check if they are equal
        Vec_PtrForEachEntryStart( unsigned *, p->vStates, pState, i, 1 )
            if ( pState[FlopK+1] != pState[FlopJ+1] )
                break;
        if ( i < Vec_PtrSize(p->vStates) )
            continue;
        // set the equivalence
        Vec_PtrWriteEntry( vMap, Saig_ManPiNum(p->pAig) + FlopJ, Saig_ManLo(p->pAig, FlopK) );
        Vec_IntWriteEntry( vBinValued, j, -1 );
        Counter2++;
    }
    if ( fVerbose )
    printf( "Detected %d const0 flops and %d pairs of equiv binary flops.\n", Counter1, Counter2 );
    Vec_IntFree( vBinValued );
    if ( Counter1 == 0 && Counter2 == 0 )
        Vec_PtrFreeP( &vMap );
    return vMap;
}

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

  Synopsis    [Performs multi-valued simulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Saig_MvManSimulate( Aig_Man_t * pAig, int nFramesSymb, int nFramesSatur, int fVerbose, int fVeryVerbose )
{
    Vec_Ptr_t * vMap;
    Saig_MvMan_t * p;
    Saig_MvObj_t * pEntry;
    int f, i, iState, clk = clock();
    assert( nFramesSymb >= 1 && nFramesSymb <= nFramesSatur );

    // start manager
    p = Saig_MvManStart( pAig, nFramesSatur );
if ( fVerbose )
ABC_PRT( "Constructing the problem", clock() - clk );

    // initialize registers
    Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
        pEntry->Value = Saig_MvConst0();
    Saig_MvSaveState( p );
    if ( fVeryVerbose )
        Saig_MvPrintState( 0, p );
    // simulate until convergence
    clk = clock();
    for ( f = 0; ; f++ )
    { 
        if ( f == nFramesSatur )
        {
            if ( fVerbose )
            printf( "Begining to saturate simulation after %d frames\n", f );
            // find all flops that have at least one X value in the past and set them to X forever
            p->vXFlops = Saig_MvManFindXFlops( p );            
        }
        if ( f == 2 * nFramesSatur )
        {
            if ( fVerbose )
            printf( "Agressively saturating simulation after %d frames\n", f );
            Vec_IntFree( p->vXFlops );
            p->vXFlops = Saig_MvManCreateNextSkip( p );
        }
        // retire some flops
        if ( p->vXFlops )
        {
            Vec_PtrForEachEntry( Saig_MvObj_t *, p->vFlops, pEntry, i )
                if ( Vec_IntEntry( p->vXFlops, i ) )
                    pEntry->Value = SAIG_UNDEF_VALUE;
        }
        // simulate timeframe
        Saig_MvSimulateFrame( p, (int)(f < nFramesSymb), fVerbose );
        // save and print state
        iState = Saig_MvSaveState( p );
        if ( fVeryVerbose )
            Saig_MvPrintState( f+1, p );
        if ( iState >= 0 )
        {
            if ( fVerbose )
            printf( "Converged after %d frames with lasso in state %d. Cycle = %d.\n", f+1, iState-1, f+2-iState );
//            printf( "Total number of PIs = %d. AND nodes = %d.\n", p->nPis, p->nObjs - p->nPis );
            break;
        }
    }
//    printf( "Coverged after %d frames.\n", f );
if ( fVerbose )
ABC_PRT( "Multi-valued simulation", clock() - clk );
    // implement equivalences
//    Saig_MvManPostProcess( p, iState-1 );
    vMap = Saig_MvManDeriveMap( p, fVerbose );
    Saig_MvManStop( p );
//    return Aig_ManDupSimple( pAig );
    return vMap;
}


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


ABC_NAMESPACE_IMPL_END

