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

  FileName    [giaEquiv.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Manipulation of equivalence classes.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "proof/cec/cec.h"
#include "sat/bmc/bmc.h"

ABC_NAMESPACE_IMPL_START

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

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

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

  Synopsis    [Manipulating original IDs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManOrigIdsInit( Gia_Man_t * p )
{
    Vec_IntFreeP( &p->vIdsOrig );
    p->vIdsOrig = Vec_IntStartNatural( Gia_ManObjNum(p) );
}
void Gia_ManOrigIdsStart( Gia_Man_t * p )
{
    Vec_IntFreeP( &p->vIdsOrig );
    p->vIdsOrig = Vec_IntStartFull( Gia_ManObjNum(p) );
}
void Gia_ManOrigIdsRemap( Gia_Man_t * p, Gia_Man_t * pNew )
{
    Gia_Obj_t * pObj; int i;
    if ( p->vIdsOrig == NULL )
        return;
    Gia_ManOrigIdsStart( pNew );
    Vec_IntWriteEntry( pNew->vIdsOrig, 0, 0 );
    Gia_ManForEachObj1( p, pObj, i )
        if ( ~pObj->Value && Abc_Lit2Var(pObj->Value) && Vec_IntEntry(p->vIdsOrig, i) != -1 && Vec_IntEntry(pNew->vIdsOrig, Abc_Lit2Var(pObj->Value)) == -1 )
            Vec_IntWriteEntry( pNew->vIdsOrig, Abc_Lit2Var(pObj->Value), Vec_IntEntry(p->vIdsOrig, i) );
    Gia_ManForEachObj( pNew, pObj, i )
        assert( Vec_IntEntry(pNew->vIdsOrig, i) >= 0 );
}
// input is a set of equivalent node pairs in any order
// output is the mapping of each node into the equiv node with the smallest ID
void Gia_ManOrigIdsRemapPairsInsert( Vec_Int_t * vMap, int One, int Two )
{
    int Smo = One < Two ? One : Two;
    int Big = One < Two ? Two : One;
    assert( Smo != Big );
    if ( Vec_IntEntry(vMap, Big) == -1 )
        Vec_IntWriteEntry( vMap, Big, Smo );
    else
        Gia_ManOrigIdsRemapPairsInsert( vMap, Smo, Vec_IntEntry(vMap, Big) );
}
int Gia_ManOrigIdsRemapPairsExtract( Vec_Int_t * vMap, int One )
{
    if ( Vec_IntEntry(vMap, One) == -1 )
        return One;
    return Gia_ManOrigIdsRemapPairsExtract( vMap, Vec_IntEntry(vMap, One) );
}
Vec_Int_t * Gia_ManOrigIdsRemapPairs( Vec_Int_t * vEquivPairs, int nObjs )
{
    Vec_Int_t * vMapResult;
    Vec_Int_t * vMap2Smaller;
    int i, One, Two;
    // map bigger into smaller one
    vMap2Smaller = Vec_IntStartFull( nObjs );
    Vec_IntForEachEntryDouble( vEquivPairs, One, Two, i )
        Gia_ManOrigIdsRemapPairsInsert( vMap2Smaller, One, Two );
    // collect results in the topo order
    vMapResult = Vec_IntStartFull( nObjs );
    Vec_IntForEachEntry( vMap2Smaller, One, i )
        if ( One >= 0 )
            Vec_IntWriteEntry( vMapResult, i, Gia_ManOrigIdsRemapPairsExtract(vMap2Smaller, One) );
    Vec_IntFree( vMap2Smaller );
    return vMapResult;
}
// remap the AIG using the equivalent pairs proved
// returns the reduced AIG and the equivalence classes of the original AIG
Gia_Man_t * Gia_ManOrigIdsReduce( Gia_Man_t * p, Vec_Int_t * vPairs )
{
    Gia_Man_t * pNew = NULL;
    Gia_Obj_t * pObj, * pRepr; int i;
    Vec_Int_t * vMap = Gia_ManOrigIdsRemapPairs( vPairs, Gia_ManObjNum(p) );
    Gia_ManSetPhase( p );
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    pNew->pName = Abc_UtilStrsav( p->pName );
    Gia_ManFillValue(p);
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi(pNew);
    Gia_ManHashAlloc( pNew );
    Gia_ManForEachAnd( p, pObj, i )
    {
        if ( Vec_IntEntry(vMap, i) == -1 )
            pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
        else
        {
            pRepr = Gia_ManObj( p, Vec_IntEntry(vMap, i) );
            pObj->Value = Abc_LitNotCond( pRepr->Value, pRepr->fPhase ^ pObj->fPhase );
        }
    }
    Gia_ManHashStop( pNew );
    Gia_ManForEachCo( p, pObj, i )
        pObj->Value = Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Vec_IntFree( vMap );
    // compute equivalences
    assert( !p->pReprs && !p->pNexts );
    p->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(p) );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        Gia_ObjSetRepr( p, i, GIA_VOID );
    Gia_ManFillValue(pNew);
    Gia_ManForEachAnd( p, pObj, i )
    {
        int iRepr = Abc_Lit2Var(pObj->Value);
        if ( iRepr == 0 )
        {
            Gia_ObjSetRepr( p, i, 0 );
            continue;
        }
        pRepr = Gia_ManObj( pNew, iRepr );
        if ( !~pRepr->Value ) // first time
        {
            pRepr->Value = i;
            continue;
        }
        // add equivalence
        Gia_ObjSetRepr( p, i, pRepr->Value );
    }
    p->pNexts = Gia_ManDeriveNexts( p );
    return pNew;
}
Gia_Man_t * Gia_ManOrigIdsReduceTest( Gia_Man_t * p, Vec_Int_t * vPairs )
{
    Gia_Man_t * pTemp, * pNew = Gia_ManOrigIdsReduce( p, vPairs );
    Gia_ManPrintStats( p, NULL );
    Gia_ManPrintStats( pNew, NULL );
    //Gia_ManStop( pNew );
    // cleanup the resulting one
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}

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

  Synopsis    [Compute equivalence classes of nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManComputeGiaEquivs( Gia_Man_t * pGia, int nConfs, int fVerbose )
{
    Gia_Man_t * pTemp;
    Cec_ParFra_t ParsFra, * pPars = &ParsFra;
    Cec_ManFraSetDefaultParams( pPars );
    pPars->fUseOrigIds = 1;
    pPars->fSatSweeping = 1;
    pPars->nBTLimit = nConfs;
    pPars->fVerbose = fVerbose;
    pTemp = Cec_ManSatSweeping( pGia, pPars, 0 );
    Gia_ManStop( pTemp );
    return Gia_ManOrigIdsReduce( pGia, pGia->vIdsEquiv );
}

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

  Synopsis    [Returns 1 if AIG is not in the required topo order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCheckTopoOrder_rec( Gia_Man_t * p, Gia_Obj_t * pObj )
{
    Gia_Obj_t * pRepr;
    if ( pObj->Value == 0 )
        return 1;
    pObj->Value = 0;
    assert( Gia_ObjIsAnd(pObj) );
    if ( !Gia_ManCheckTopoOrder_rec( p, Gia_ObjFanin0(pObj) ) )
        return 0;
    if ( !Gia_ManCheckTopoOrder_rec( p, Gia_ObjFanin1(pObj) ) )
        return 0;
    pRepr = p->pReprs ? Gia_ObjReprObj( p, Gia_ObjId(p,pObj) ) : NULL;
    return pRepr == NULL || pRepr->Value == 0;
}

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

  Synopsis    [Returns 0 if AIG is not in the required topo order.]

  Description [AIG should be in such an order that the representative
  is always traversed before the node.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCheckTopoOrder( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, RetValue = 1;
    Gia_ManFillValue( p );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = 0;
    Gia_ManForEachCo( p, pObj, i )
        RetValue &= Gia_ManCheckTopoOrder_rec( p, Gia_ObjFanin0(pObj) );
    return RetValue;
}

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

  Synopsis    [Given representatives, derives pointers to the next objects.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int * Gia_ManDeriveNexts( Gia_Man_t * p )
{
    unsigned * pNexts, * pTails;
    int i;
    assert( p->pReprs != NULL );
    assert( p->pNexts == NULL );
    pNexts = ABC_CALLOC( unsigned, Gia_ManObjNum(p) );
    pTails = ABC_ALLOC( unsigned, Gia_ManObjNum(p) );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        pTails[i] = i;
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
    {
        if ( !p->pReprs[i].iRepr || p->pReprs[i].iRepr == GIA_VOID )
            continue;
        pNexts[ pTails[p->pReprs[i].iRepr] ] = i;
        pTails[p->pReprs[i].iRepr] = i;
    }
    ABC_FREE( pTails );
    return (int *)pNexts;
}

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

  Synopsis    [Given points to the next objects, derives representatives.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManDeriveReprs( Gia_Man_t * p )
{
    int i, iObj;
    assert( p->pReprs == NULL );
    assert( p->pNexts != NULL );
    p->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(p) );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        Gia_ObjSetRepr( p, i, GIA_VOID );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
    {
        if ( p->pNexts[i] == 0 )
            continue;
        if ( p->pReprs[i].iRepr != GIA_VOID )
            continue;
        // next is set, repr is not set
        for ( iObj = p->pNexts[i]; iObj; iObj = p->pNexts[iObj] )
            p->pReprs[iObj].iRepr = i;
    }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivCountLitsAll( Gia_Man_t * p )
{
    int i, nLits = 0;
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        nLits += (Gia_ObjRepr(p, i) != GIA_VOID);
    return nLits;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivCountClasses( Gia_Man_t * p )
{
    int i, Counter = 0;
    if ( p->pReprs == NULL )
        return 0;
    for ( i = 1; i < Gia_ManObjNum(p); i++ )
        Counter += Gia_ObjIsHead(p, i);
    return Counter;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivCheckLits( Gia_Man_t * p, int nLits )
{
    int nLitsReal = Gia_ManEquivCountLitsAll( p );
    if ( nLitsReal != nLits )
        Abc_Print( 1, "Detected a mismatch in counting equivalence classes (%d).\n", nLitsReal - nLits );
    return 1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManPrintStatsClasses( Gia_Man_t * p )
{
    int i, Counter = 0, Counter0 = 0, CounterX = 0, Proved = 0, nLits;
    for ( i = 1; i < Gia_ManObjNum(p); i++ )
    {
        if ( Gia_ObjIsHead(p, i) )
            Counter++;
        else if ( Gia_ObjIsConst(p, i) )
            Counter0++;
        else if ( Gia_ObjIsNone(p, i) )
            CounterX++;
        if ( Gia_ObjProved(p, i) )
            Proved++;
    }
    CounterX -= Gia_ManCoNum(p);
    nLits = Gia_ManCiNum(p) + Gia_ManAndNum(p) - Counter - CounterX;

//    Abc_Print( 1, "i/o/ff =%5d/%5d/%5d  ", Gia_ManPiNum(p), Gia_ManPoNum(p), Gia_ManRegNum(p) );
//    Abc_Print( 1, "and =%5d  ", Gia_ManAndNum(p) );
//    Abc_Print( 1, "lev =%3d  ", Gia_ManLevelNum(p) );
    Abc_Print( 1, "cst =%3d  cls =%6d  lit =%8d\n", Counter0, Counter, nLits );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivCountLits( Gia_Man_t * p )
{
    int i, Counter = 0, Counter0 = 0, CounterX = 0;
    if ( p->pReprs == NULL || p->pNexts == NULL )
        return 0;
    for ( i = 1; i < Gia_ManObjNum(p); i++ )
    {
        if ( Gia_ObjIsHead(p, i) )
            Counter++;
        else if ( Gia_ObjIsConst(p, i) )
            Counter0++;
        else if ( Gia_ObjIsNone(p, i) )
            CounterX++;
    }
    CounterX -= Gia_ManCoNum(p);
    return Gia_ManCiNum(p) + Gia_ManAndNum(p) - Counter - CounterX;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivCountOne( Gia_Man_t * p, int i )
{
    int Ent, nLits = 1;
    Gia_ClassForEachObj1( p, i, Ent )
    {
        assert( Gia_ObjRepr(p, Ent) == i );
        nLits++;
    }
    return nLits;
}
void Gia_ManEquivPrintOne( Gia_Man_t * p, int i, int Counter )
{
    int Ent;
    Abc_Print( 1, "Class %4d :  Num = %2d  {", Counter, Gia_ManEquivCountOne(p, i) );
    Gia_ClassForEachObj( p, i, Ent )
    {
        Abc_Print( 1," %d", Ent );
        if ( p->pReprs[Ent].fColorA || p->pReprs[Ent].fColorB )
            Abc_Print( 1," <%d%d>", p->pReprs[Ent].fColorA, p->pReprs[Ent].fColorB );
    }
    Abc_Print( 1, " }\n" );
}
void Gia_ManEquivPrintClasses( Gia_Man_t * p, int fVerbose, float Mem )
{
    int i, Counter = 0, Counter0 = 0, CounterX = 0, Proved = 0, nLits;
    for ( i = 1; i < Gia_ManObjNum(p); i++ )
    {
        if ( Gia_ObjIsHead(p, i) )
            Counter++;
        else if ( Gia_ObjIsConst(p, i) )
            Counter0++;
        else if ( Gia_ObjIsNone(p, i) )
            CounterX++;
        if ( Gia_ObjProved(p, i) )
            Proved++;
    }
    CounterX -= Gia_ManCoNum(p);
    nLits = Gia_ManCiNum(p) + Gia_ManAndNum(p) - Counter - CounterX;
    Abc_Print( 1, "cst =%8d  cls =%7d  lit =%8d  unused =%8d  proof =%6d  mem =%5.2f MB\n",
        Counter0, Counter, nLits, CounterX, Proved, (Mem == 0.0) ? 8.0*Gia_ManObjNum(p)/(1<<20) : Mem );
    assert( Gia_ManEquivCheckLits( p, nLits ) );
    if ( fVerbose )
    {
//        int Ent;
        Abc_Print( 1, "Const0 (%d) = ", Counter0 );
        Gia_ManForEachConst( p, i )
            Abc_Print( 1, "%d ", i );
        Abc_Print( 1, "\n" );
        Counter = 0;
        Gia_ManForEachClass( p, i )
            Gia_ManEquivPrintOne( p, i, ++Counter );
/*
        Gia_ManLevelNum( p );
        Gia_ManForEachClass( p, i )
            if ( i % 100 == 0 )
            {
//                Abc_Print( 1, "%d ", Gia_ManEquivCountOne(p, i) );
                Gia_ClassForEachObj( p, i, Ent )
                {
                    Abc_Print( 1, "%d ", Gia_ObjLevel( p, Gia_ManObj(p, Ent) ) );
                }
                Abc_Print( 1, "\n" );
            }
*/
    }
}

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

  Synopsis    [Returns representative node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Gia_Obj_t * Gia_ManEquivRepr( Gia_Man_t * p, Gia_Obj_t * pObj, int fUseAll, int fDualOut )
{
    if ( fUseAll )
    {
        if ( Gia_ObjRepr(p, Gia_ObjId(p,pObj)) == GIA_VOID )
            return NULL;
    }
    else
    {
        if ( !Gia_ObjProved(p, Gia_ObjId(p,pObj)) )
            return NULL;
    }
//    if ( fDualOut && !Gia_ObjDiffColors( p, Gia_ObjId(p, pObj), Gia_ObjRepr(p, Gia_ObjId(p,pObj)) ) )
    if ( fDualOut && !Gia_ObjDiffColors2( p, Gia_ObjId(p, pObj), Gia_ObjRepr(p, Gia_ObjId(p,pObj)) ) )
        return NULL;
    return Gia_ManObj( p, Gia_ObjRepr(p, Gia_ObjId(p,pObj)) );
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivReduce_rec( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj, int fUseAll, int fDualOut )
{
    Gia_Obj_t * pRepr;
    if ( (pRepr = Gia_ManEquivRepr(p, pObj, fUseAll, fDualOut)) )
    {
        Gia_ManEquivReduce_rec( pNew, p, pRepr, fUseAll, fDualOut );
        pObj->Value = Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
        return;
    }
    if ( ~pObj->Value )
        return;
    assert( Gia_ObjIsAnd(pObj) );
    Gia_ManEquivReduce_rec( pNew, p, Gia_ObjFanin0(pObj), fUseAll, fDualOut );
    Gia_ManEquivReduce_rec( pNew, p, Gia_ObjFanin1(pObj), fUseAll, fDualOut );
    pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
}

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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManEquivReduce( Gia_Man_t * p, int fUseAll, int fDualOut, int fSkipPhase, int fVerbose )
{
    Gia_Man_t * pNew;
    Gia_Obj_t * pObj;
    int i;
    if ( !p->pReprs && p->pSibls )
    {
        p->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(p) );
        for ( i = 0; i < Gia_ManObjNum(p); i++ )
            Gia_ObjSetRepr( p, i, GIA_VOID );
        for ( i = 0; i < Gia_ManObjNum(p); i++ )
            if ( p->pSibls[i] > 0 )
                Gia_ObjSetRepr( p, i, p->pSibls[i] );
        printf( "Created equivalence classes.\n" );
        ABC_FREE( p->pNexts );
        p->pNexts = Gia_ManDeriveNexts( p );
    }
    if ( !p->pReprs )
    {
        Abc_Print( 1, "Gia_ManEquivReduce(): Equivalence classes are not available.\n" );
        return NULL;
    }
    if ( fDualOut && (Gia_ManPoNum(p) & 1) )
    {
        Abc_Print( 1, "Gia_ManEquivReduce(): Dual-output miter should have even number of POs.\n" );
        return NULL;
    }
    // check if there are any equivalences defined
    Gia_ManForEachObj( p, pObj, i )
        if ( Gia_ObjReprObj(p, i) != NULL )
            break;
    if ( i == Gia_ManObjNum(p) )
    {
//        Abc_Print( 1, "Gia_ManEquivReduce(): There are no equivalences to reduce.\n" );
//        return NULL;
        return Gia_ManDup( p );
    }
/*
    if ( !Gia_ManCheckTopoOrder( p ) )
    {
        Abc_Print( 1, "Gia_ManEquivReduce(): AIG is not in a correct topological order.\n" );
        return NULL;
    }
*/
    if ( !fSkipPhase )
        Gia_ManSetPhase( p );
    if ( fDualOut )
        Gia_ManEquivSetColors( p, fVerbose );
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    Gia_ManFillValue( p );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi(pNew);
    Gia_ManHashAlloc( pNew );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ManEquivReduce_rec( pNew, p, Gia_ObjFanin0(pObj), fUseAll, fDualOut );
    Gia_ManForEachCo( p, pObj, i )
        pObj->Value = Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Gia_ManHashStop( pNew );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p) );
    return pNew;
}

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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivFixOutputPairs( Gia_Man_t * p )
{
    Gia_Obj_t * pObj0, * pObj1;
    int i;
    assert( (Gia_ManPoNum(p) & 1) == 0 );
    Gia_ManForEachPo( p, pObj0, i )
    {
        pObj1 = Gia_ManPo( p, ++i );
        if ( Gia_ObjChild0(pObj0) != Gia_ObjChild0(pObj1) )
            continue;
        pObj0->iDiff0  = Gia_ObjId(p, pObj0);
        pObj0->fCompl0 = 0;
        pObj1->iDiff0  = Gia_ObjId(p, pObj1);
        pObj1->fCompl0 = 0;
    }
}

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

  Synopsis    [Removes pointers to the unmarked nodes..]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivUpdatePointers( Gia_Man_t * p, Gia_Man_t * pNew )
{
    Gia_Obj_t * pObj, * pObjNew;
    int i;
    Gia_ManForEachObj( p, pObj, i )
    {
        if ( !~pObj->Value )
            continue;
        pObjNew = Gia_ManObj( pNew, Abc_Lit2Var(pObj->Value) );
        if ( pObjNew->fMark0 )
            pObj->Value = ~0;
    }
}

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

  Synopsis    [Removes pointers to the unmarked nodes..]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivDeriveReprs( Gia_Man_t * p, Gia_Man_t * pNew, Gia_Man_t * pFinal )
{
    Vec_Int_t * vClass;
    Gia_Obj_t * pObj, * pObjNew;
    int i, k, iNode, iRepr, iPrev;
    // start representatives
    pFinal->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(pFinal) );
    for ( i = 0; i < Gia_ManObjNum(pFinal); i++ )
        Gia_ObjSetRepr( pFinal, i, GIA_VOID );
    // iterate over constant candidates
    Gia_ManForEachConst( p, i )
    {
        pObj = Gia_ManObj( p, i );
        if ( !~pObj->Value )
            continue;
        pObjNew = Gia_ManObj( pNew, Abc_Lit2Var(pObj->Value) );
        if ( Abc_Lit2Var(pObjNew->Value) == 0 )
            continue;
        Gia_ObjSetRepr( pFinal, Abc_Lit2Var(pObjNew->Value), 0 );
    }
    // iterate over class candidates
    vClass = Vec_IntAlloc( 100 );
    Gia_ManForEachClass( p, i )
    {
        Vec_IntClear( vClass );
        Gia_ClassForEachObj( p, i, k )
        {
            pObj = Gia_ManObj( p, k );
            if ( !~pObj->Value )
                continue;
            pObjNew = Gia_ManObj( pNew, Abc_Lit2Var(pObj->Value) );
            Vec_IntPushUnique( vClass, Abc_Lit2Var(pObjNew->Value) );
        }
        if ( Vec_IntSize( vClass ) < 2 )
            continue;
        Vec_IntSort( vClass, 0 );
        iRepr = iPrev = Vec_IntEntry( vClass, 0 );
        Vec_IntForEachEntryStart( vClass, iNode, k, 1 )
        {
            Gia_ObjSetRepr( pFinal, iNode, iRepr );
            assert( iPrev < iNode );
            iPrev = iNode;
        }
    }
    Vec_IntFree( vClass );
    pFinal->pNexts = Gia_ManDeriveNexts( pFinal );
}

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

  Synopsis    [Removes pointers to the unmarked nodes..]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManEquivRemapDfs( Gia_Man_t * p )
{
    Gia_Man_t * pNew;
    Vec_Int_t * vClass;
    int i, k, iNode, iRepr, iPrev;
    pNew = Gia_ManDupDfs( p );
    // start representatives
    pNew->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(pNew) );
    for ( i = 0; i < Gia_ManObjNum(pNew); i++ )
        Gia_ObjSetRepr( pNew, i, GIA_VOID );
    // iterate over constant candidates
    Gia_ManForEachConst( p, i )
        Gia_ObjSetRepr( pNew, Abc_Lit2Var(Gia_ManObj(p, i)->Value), 0 );
    // iterate over class candidates
    vClass = Vec_IntAlloc( 100 );
    Gia_ManForEachClass( p, i )
    {
        Vec_IntClear( vClass );
        Gia_ClassForEachObj( p, i, k )
            Vec_IntPushUnique( vClass, Abc_Lit2Var(Gia_ManObj(p, k)->Value) );
        assert( Vec_IntSize( vClass ) > 1 );
        Vec_IntSort( vClass, 0 );
        iRepr = iPrev = Vec_IntEntry( vClass, 0 );
        Vec_IntForEachEntryStart( vClass, iNode, k, 1 )
        {
            Gia_ObjSetRepr( pNew, iNode, iRepr );
            assert( iPrev < iNode );
            iPrev = iNode;
        }
    }
    Vec_IntFree( vClass );
    pNew->pNexts = Gia_ManDeriveNexts( pNew );
    return pNew;
}

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

  Synopsis    [Reduces AIG while remapping equivalence classes.]

  Description [Drops the pairs of outputs if they are proved equivalent.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManEquivReduceAndRemap( Gia_Man_t * p, int fSeq, int fMiterPairs )
{
    Gia_Man_t * pNew, * pFinal;
    pNew = Gia_ManEquivReduce( p, 0, 0, 0, 0 );
    if ( pNew == NULL )
        return NULL;
    Gia_ManOrigIdsRemap( p, pNew );
    if ( fMiterPairs )
        Gia_ManEquivFixOutputPairs( pNew );
    if ( fSeq )
        Gia_ManSeqMarkUsed( pNew );
    else
        Gia_ManCombMarkUsed( pNew );
    Gia_ManEquivUpdatePointers( p, pNew );
    pFinal = Gia_ManDupMarked( pNew );
    Gia_ManOrigIdsRemap( pNew, pFinal );
    Gia_ManEquivDeriveReprs( p, pNew, pFinal );
    Gia_ManStop( pNew );
    pFinal = Gia_ManEquivRemapDfs( pNew = pFinal );
    Gia_ManOrigIdsRemap( pNew, pFinal );
    Gia_ManStop( pNew );
    return pFinal;
}

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

  Synopsis    [Marks CIs/COs/ANDs unreachable from POs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivSetColor_rec( Gia_Man_t * p, Gia_Obj_t * pObj, int fOdds )
{
    if ( Gia_ObjVisitColor( p, Gia_ObjId(p,pObj), fOdds ) )
        return 0;
    if ( Gia_ObjIsRo(p, pObj) )
        return 1 + Gia_ManEquivSetColor_rec( p, Gia_ObjFanin0(Gia_ObjRoToRi(p, pObj)), fOdds );
    assert( Gia_ObjIsAnd(pObj) );
    return 1 + Gia_ManEquivSetColor_rec( p, Gia_ObjFanin0(pObj), fOdds )
             + Gia_ManEquivSetColor_rec( p, Gia_ObjFanin1(pObj), fOdds );
}

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

  Synopsis    [Marks CIs/COs/ANDs unreachable from POs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManEquivSetColors( Gia_Man_t * p, int fVerbose )
{
    Gia_Obj_t * pObj;
    int i, nNodes[2], nDiffs[2];
    assert( (Gia_ManPoNum(p) & 1) == 0 );
    Gia_ObjSetColors( p, 0 );
    Gia_ManForEachPi( p, pObj, i )
        Gia_ObjSetColors( p, Gia_ObjId(p,pObj) );
    nNodes[0] = nNodes[1] = Gia_ManPiNum(p);
    Gia_ManForEachPo( p, pObj, i )
        nNodes[i&1] += Gia_ManEquivSetColor_rec( p, Gia_ObjFanin0(pObj), i&1 );
//    Gia_ManForEachObj( p, pObj, i )
//        if ( Gia_ObjIsCi(pObj) || Gia_ObjIsAnd(pObj) )
//            assert( Gia_ObjColors(p, i) );
    nDiffs[0] = Gia_ManCandNum(p) - nNodes[0];
    nDiffs[1] = Gia_ManCandNum(p) - nNodes[1];
    if ( fVerbose )
    {
        Abc_Print( 1, "CI+AND = %7d  A = %7d  B = %7d  Ad = %7d  Bd = %7d  AB = %7d.\n",
            Gia_ManCandNum(p), nNodes[0], nNodes[1], nDiffs[0], nDiffs[1],
            Gia_ManCandNum(p) - nDiffs[0] - nDiffs[1] );
    }
    return (nDiffs[0] + nDiffs[1]) / 2;
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Gia_ManSpecBuild( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vXorLits, int fDualOut, int fSpeculate, Vec_Int_t * vTrace, Vec_Int_t * vGuide, Vec_Int_t * vMap )
{
    Gia_Obj_t * pRepr;
    unsigned iLitNew;
    pRepr = Gia_ObjReprObj( p, Gia_ObjId(p,pObj) );
    if ( pRepr == NULL )
        return;
//    if ( fDualOut && !Gia_ObjDiffColors( p, Gia_ObjId(p, pObj), Gia_ObjId(p, pRepr) ) )
    if ( fDualOut && !Gia_ObjDiffColors2( p, Gia_ObjId(p, pObj), Gia_ObjId(p, pRepr) ) )
        return;
    iLitNew = Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
    if ( pObj->Value != iLitNew && !Gia_ObjProved(p, Gia_ObjId(p,pObj)) )
    {
        if ( vTrace )
            Vec_IntPush( vTrace, 1 );
        if ( vGuide == NULL || Vec_IntEntry( vGuide, Vec_IntSize(vTrace)-1 ) )
        {
            if ( vMap )  
                Vec_IntPush( vMap, Gia_ObjId(p, pObj) );
            Vec_IntPush( vXorLits, Gia_ManHashXor(pNew, pObj->Value, iLitNew) );
        }
    }
    else
    {
        if ( vTrace )
            Vec_IntPush( vTrace, 0 );
    }
    if ( fSpeculate )
        pObj->Value = iLitNew;
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSpecReduce_rec( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vXorLits, int fDualOut, int fSpeculate, Vec_Int_t * vTrace, Vec_Int_t * vGuide, Vec_Int_t * vMap )
{
    if ( ~pObj->Value )
        return;
    assert( Gia_ObjIsAnd(pObj) );
    Gia_ManSpecReduce_rec( pNew, p, Gia_ObjFanin0(pObj), vXorLits, fDualOut, fSpeculate, vTrace, vGuide, vMap );
    Gia_ManSpecReduce_rec( pNew, p, Gia_ObjFanin1(pObj), vXorLits, fDualOut, fSpeculate, vTrace, vGuide, vMap );
    pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
    Gia_ManSpecBuild( pNew, p, pObj, vXorLits, fDualOut, fSpeculate, vTrace, vGuide, vMap );
}

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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManSpecReduceTrace( Gia_Man_t * p, Vec_Int_t * vTrace, Vec_Int_t * vMap )
{
    Vec_Int_t * vXorLits;
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj;
    int i, iLitNew;
    if ( !p->pReprs )
    {
        Abc_Print( 1, "Gia_ManSpecReduce(): Equivalence classes are not available.\n" );
        return NULL;
    }
    Vec_IntClear( vTrace );
    vXorLits = Vec_IntAlloc( 1000 );
    Gia_ManSetPhase( p );
    Gia_ManFillValue( p );
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    Gia_ManHashAlloc( pNew );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi(pNew);
    Gia_ManForEachRo( p, pObj, i )
        Gia_ManSpecBuild( pNew, p, pObj, vXorLits, 0, 1, vTrace, NULL, vMap );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ManSpecReduce_rec( pNew, p, Gia_ObjFanin0(pObj), vXorLits, 0, 1, vTrace, NULL, vMap );
    Gia_ManForEachPo( p, pObj, i )
        Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Vec_IntForEachEntry( vXorLits, iLitNew, i )
        Gia_ManAppendCo( pNew, iLitNew );
    if ( Vec_IntSize(vXorLits) == 0 )
    {
        Abc_Print( 1, "Speculatively reduced model has no primary outputs.\n" );
        Gia_ManAppendCo( pNew, 0 );
    }
    Gia_ManForEachRi( p, pObj, i )
        Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Gia_ManHashStop( pNew );
    Vec_IntFree( vXorLits );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p) );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}

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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManSpecReduce( Gia_Man_t * p, int fDualOut, int fSynthesis, int fSpeculate, int fSkipSome, int fVerbose )
{
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj;
    Vec_Int_t * vXorLits;
    int i, iLitNew;
    Vec_Int_t * vTrace = NULL, * vGuide = NULL;
    if ( !p->pReprs )
    {
        Abc_Print( 1, "Gia_ManSpecReduce(): Equivalence classes are not available.\n" );
        return NULL;
    }
    if ( fDualOut && (Gia_ManPoNum(p) & 1) )
    {
        Abc_Print( 1, "Gia_ManSpecReduce(): Dual-output miter should have even number of POs.\n" );
        return NULL;
    }
    if ( fSkipSome )
    {
        vGuide = Vec_IntAlloc( 100 );
        pTemp  = Gia_ManSpecReduceTrace( p, vGuide, NULL );
        Gia_ManStop( pTemp );
        assert( Vec_IntSize(vGuide) == Gia_ManEquivCountLitsAll(p) );
        vTrace = Vec_IntAlloc( 100 );
    }
    vXorLits = Vec_IntAlloc( 1000 );
    Gia_ManSetPhase( p );
    Gia_ManFillValue( p );
    if ( fDualOut )
        Gia_ManEquivSetColors( p, fVerbose );
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    Gia_ManHashAlloc( pNew );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi(pNew);
    Gia_ManForEachRo( p, pObj, i )
        Gia_ManSpecBuild( pNew, p, pObj, vXorLits, fDualOut, fSpeculate, vTrace, vGuide, NULL );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ManSpecReduce_rec( pNew, p, Gia_ObjFanin0(pObj), vXorLits, fDualOut, fSpeculate, vTrace, vGuide, NULL );
    if ( !fSynthesis )
    {
        Gia_ManForEachPo( p, pObj, i )
            Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    }
    Vec_IntForEachEntry( vXorLits, iLitNew, i )
        Gia_ManAppendCo( pNew, iLitNew );
    if ( Vec_IntSize(vXorLits) == 0 )
    {
        Abc_Print( 1, "Speculatively reduced model has no primary outputs.\n" );
        Gia_ManAppendCo( pNew, 0 );
    }
    Gia_ManForEachRi( p, pObj, i )
        Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Gia_ManHashStop( pNew );
    Vec_IntFree( vXorLits );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p) );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );

    // update using trace
    if ( fSkipSome )
    {
        // count the number of non-zero entries
        int iLit, nAddPos = 0;
        Vec_IntForEachEntry( vGuide, iLit, i )
            if ( iLit )
                nAddPos++;
        if ( nAddPos )
            assert( Gia_ManPoNum(pNew) == Gia_ManPoNum(p) + nAddPos );
    }
    Vec_IntFreeP( &vTrace );
    Vec_IntFreeP( &vGuide );
    return pNew;
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSpecBuildInit( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vXorLits, int f, int fDualOut )
{
    Gia_Obj_t * pRepr;
    int iLitNew;
    pRepr = Gia_ObjReprObj( p, Gia_ObjId(p,pObj) );
    if ( pRepr == NULL )
        return;
//    if ( fDualOut && !Gia_ObjDiffColors( p, Gia_ObjId(p, pObj), Gia_ObjId(p, pRepr) ) )
    if ( fDualOut && !Gia_ObjDiffColors2( p, Gia_ObjId(p, pObj), Gia_ObjId(p, pRepr) ) )
        return;
    iLitNew = Abc_LitNotCond( Gia_ObjCopyF(p, f, pRepr), Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
    if ( Gia_ObjCopyF(p, f, pObj) != iLitNew && !Gia_ObjProved(p, Gia_ObjId(p,pObj)) )
        Vec_IntPush( vXorLits, Gia_ManHashXor(pNew, Gia_ObjCopyF(p, f, pObj), iLitNew) );
    Gia_ObjSetCopyF( p, f, pObj, iLitNew );
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSpecReduceInit_rec( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vXorLits, int f, int fDualOut )
{
    if ( ~Gia_ObjCopyF(p, f, pObj) )
        return;
    assert( Gia_ObjIsAnd(pObj) );
    Gia_ManSpecReduceInit_rec( pNew, p, Gia_ObjFanin0(pObj), vXorLits, f, fDualOut );
    Gia_ManSpecReduceInit_rec( pNew, p, Gia_ObjFanin1(pObj), vXorLits, f, fDualOut );
    Gia_ObjSetCopyF( p, f, pObj, Gia_ManHashAnd(pNew, Gia_ObjFanin0CopyF(p, f, pObj), Gia_ObjFanin1CopyF(p, f, pObj)) );
    Gia_ManSpecBuildInit( pNew, p, pObj, vXorLits, f, fDualOut );
}

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

  Synopsis    [Creates initialized SRM with the given number of frames.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManSpecReduceInit( Gia_Man_t * p, Abc_Cex_t * pInit, int nFrames, int fDualOut )
{
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj, * pObjRi, * pObjRo;
    Vec_Int_t * vXorLits;
    int f, i, iLitNew;
    if ( !p->pReprs )
    {
        Abc_Print( 1, "Gia_ManSpecReduceInit(): Equivalence classes are not available.\n" );
        return NULL;
    }
    if ( Gia_ManRegNum(p) == 0 )
    {
        Abc_Print( 1, "Gia_ManSpecReduceInit(): Circuit is not sequential.\n" );
        return NULL;
    }
    if ( Gia_ManRegNum(p) != pInit->nRegs )
    {
        Abc_Print( 1, "Gia_ManSpecReduceInit(): Mismatch in the number of registers.\n" );
        return NULL;
    }
    if ( fDualOut && (Gia_ManPoNum(p) & 1) )
    {
        Abc_Print( 1, "Gia_ManSpecReduceInit(): Dual-output miter should have even number of POs.\n" );
        return NULL;
    }

/*
    if ( !Gia_ManCheckTopoOrder( p ) )
    {
        Abc_Print( 1, "Gia_ManSpecReduceInit(): AIG is not in a correct topological order.\n" );
        return NULL;
    }
*/
    assert( pInit->nRegs == Gia_ManRegNum(p) && pInit->nPis == 0 );
    Vec_IntFill( &p->vCopies, nFrames * Gia_ManObjNum(p), -1 );
    vXorLits = Vec_IntAlloc( 1000 );
    Gia_ManSetPhase( p );
    if ( fDualOut )
        Gia_ManEquivSetColors( p, 0 );
    pNew = Gia_ManStart( nFrames * Gia_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    Gia_ManHashAlloc( pNew );
    Gia_ManForEachRo( p, pObj, i )
        Gia_ObjSetCopyF( p, 0, pObj, Abc_InfoHasBit(pInit->pData, i) );
    for ( f = 0; f < nFrames; f++ )
    {
        Gia_ObjSetCopyF( p, f, Gia_ManConst0(p), 0 );
        Gia_ManForEachPi( p, pObj, i )
            Gia_ObjSetCopyF( p, f, pObj, Gia_ManAppendCi(pNew) );
        Gia_ManForEachRo( p, pObj, i )
            Gia_ManSpecBuildInit( pNew, p, pObj, vXorLits, f, fDualOut );
        Gia_ManForEachCo( p, pObj, i )
        {
            Gia_ManSpecReduceInit_rec( pNew, p, Gia_ObjFanin0(pObj), vXorLits, f, fDualOut );
            Gia_ObjSetCopyF( p, f, pObj, Gia_ObjFanin0CopyF(p, f, pObj) );
        }
        if ( f == nFrames - 1 )
            break;
        Gia_ManForEachRiRo( p, pObjRi, pObjRo, i )
            Gia_ObjSetCopyF( p, f+1, pObjRo, Gia_ObjCopyF(p, f, pObjRi) );
    }
    Vec_IntForEachEntry( vXorLits, iLitNew, i )
        Gia_ManAppendCo( pNew, iLitNew );
    if ( Vec_IntSize(vXorLits) == 0 )
    {
//        Abc_Print( 1, "Speculatively reduced model has no primary outputs.\n" );
        Gia_ManAppendCo( pNew, 0 );
    }
    Vec_IntErase( &p->vCopies );
    Vec_IntFree( vXorLits );
    Gia_ManHashStop( pNew );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}

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

  Synopsis    [Creates initialized SRM with the given number of frames.]

  Description [Uses as many frames as needed to create the number of 
  output not less than the number of equivalence literals.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManSpecReduceInitFrames( Gia_Man_t * p, Abc_Cex_t * pInit, int nFramesMax, int * pnFrames, int fDualOut, int nMinOutputs )
{
    Gia_Man_t * pFrames;
    int f, nLits;
    nLits = Gia_ManEquivCountLits( p );
    for ( f = 1; ; f++ )
    {
        pFrames = Gia_ManSpecReduceInit( p, pInit, f, fDualOut );
        if ( (nMinOutputs == 0 && Gia_ManPoNum(pFrames) >= nLits/2+1) ||
             (nMinOutputs != 0 && Gia_ManPoNum(pFrames) >= nMinOutputs) )
            break;
        if ( f == nFramesMax )
            break;
        if ( Gia_ManAndNum(pFrames) > 500000 )
        {
            Gia_ManStop( pFrames );
            return NULL;
        }
        Gia_ManStop( pFrames );
        pFrames = NULL;
    }
    if ( f == nFramesMax )
        Abc_Print( 1, "Stopped unrolling after %d frames.\n", nFramesMax );
    if ( pnFrames )
        *pnFrames = f;
    return pFrames;
}

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

  Synopsis    [Transforms equiv classes by removing the AB nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivTransform( Gia_Man_t * p, int fVerbose )
{
    extern void Cec_ManSimClassCreate( Gia_Man_t * p, Vec_Int_t * vClass );
    Vec_Int_t * vClass, * vClassNew;
    int iRepr, iNode, Ent, k;
    int nRemovedLits = 0, nRemovedClas = 0;
    int nTotalLits = 0, nTotalClas = 0;
    Gia_Obj_t * pObj;
    int i;
    assert( p->pReprs && p->pNexts );
    vClass = Vec_IntAlloc( 100 );
    vClassNew = Vec_IntAlloc( 100 );
    Gia_ManForEachObj( p, pObj, i )
        if ( Gia_ObjIsCi(pObj) || Gia_ObjIsAnd(pObj) )
            assert( Gia_ObjColors(p, i) );
    Gia_ManForEachClassReverse( p, iRepr )
    {
        nTotalClas++;
        Vec_IntClear( vClass );
        Vec_IntClear( vClassNew );
        Gia_ClassForEachObj( p, iRepr, iNode )
        {
            nTotalLits++;
            Vec_IntPush( vClass, iNode );
            assert( Gia_ObjColors(p, iNode) );
            if ( Gia_ObjColors(p, iNode) != 3 )
                Vec_IntPush( vClassNew, iNode );
            else
                nRemovedLits++;
        }
        Vec_IntForEachEntry( vClass, Ent, k )
        {
            p->pReprs[Ent].fFailed = p->pReprs[Ent].fProved = 0;
            p->pReprs[Ent].iRepr = GIA_VOID;
            p->pNexts[Ent] = 0;
        }
        if ( Vec_IntSize(vClassNew) < 2 )
        {
            nRemovedClas++;
            continue;
        }
        Cec_ManSimClassCreate( p, vClassNew );
    }
    Vec_IntFree( vClass );
    Vec_IntFree( vClassNew );
    if ( fVerbose )
    Abc_Print( 1, "Removed classes = %6d (out of %6d). Removed literals = %6d (out of %6d).\n",
        nRemovedClas, nTotalClas, nRemovedLits, nTotalLits );
}

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

  Synopsis    [Marks proved equivalences.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivMark( Gia_Man_t * p, char * pFileName, int fSkipSome, int fVerbose )
{
    Gia_Man_t * pMiter, * pTemp;
    Gia_Obj_t * pObj;
    int i, iLit, nAddPos, nLits = 0;
    int nLitsAll, Counter = 0;
    nLitsAll = Gia_ManEquivCountLitsAll( p );
    if ( nLitsAll == 0 )
    {
        Abc_Print( 1, "Gia_ManEquivMark(): Current AIG does not have equivalences.\n" );
        return;
    }
    // read AIGER file
    pMiter = Gia_AigerRead( pFileName, 0, 0, 0 );
    if ( pMiter == NULL )
    {
        Abc_Print( 1, "Gia_ManEquivMark(): Input file %s could not be read.\n", pFileName );
        return;
    }
    if ( fSkipSome )
    {
        Vec_Int_t * vTrace = Vec_IntAlloc( 100 );
        pTemp = Gia_ManSpecReduceTrace( p, vTrace, NULL );
        Gia_ManStop( pTemp );
        assert( Vec_IntSize(vTrace) == nLitsAll );
        // count the number of non-zero entries
        nAddPos = 0;
        Vec_IntForEachEntry( vTrace, iLit, i )
            if ( iLit )
                nAddPos++;
        // check the number
        if ( Gia_ManPoNum(pMiter) != Gia_ManPoNum(p) + nAddPos )
        {
            Abc_Print( 1, "Gia_ManEquivMark(): The number of POs is not correct: MiterPONum(%d) != AIGPONum(%d) + AIGFilteredEquivNum(%d).\n",
                Gia_ManPoNum(pMiter), Gia_ManPoNum(p), nAddPos );
            Gia_ManStop( pMiter );
            Vec_IntFreeP( &vTrace );
            return;
        }
        // mark corresponding POs as solved
        nLits = iLit = Counter = 0;
        for ( i = 0; i < Gia_ManObjNum(p); i++ )
        {
            if ( Gia_ObjRepr(p, i) == GIA_VOID )
                continue;
            if ( Vec_IntEntry( vTrace, nLits++ ) == 0 )
                continue;
            pObj = Gia_ManPo( pMiter, Gia_ManPoNum(p) + iLit++ );
            if ( Gia_ObjFaninLit0p(pMiter, pObj) == 0 ) // const 0 - proven
            {
                Gia_ObjSetProved(p, i);
                Counter++;
            }
        }
        assert( nLits == nLitsAll );
        assert( iLit == nAddPos );
        Vec_IntFreeP( &vTrace );
    }
    else
    {
        if ( Gia_ManPoNum(pMiter) != Gia_ManPoNum(p) + nLitsAll )
        {
            Abc_Print( 1, "Gia_ManEquivMark(): The number of POs is not correct: MiterPONum(%d) != AIGPONum(%d) + AIGEquivNum(%d).\n",
                Gia_ManPoNum(pMiter), Gia_ManPoNum(p), nLitsAll );
            Gia_ManStop( pMiter );
            return;
        }
        // mark corresponding POs as solved
        nLits = 0;
        for ( i = 0; i < Gia_ManObjNum(p); i++ )
        {
            if ( Gia_ObjRepr(p, i) == GIA_VOID )
                continue;
            pObj = Gia_ManPo( pMiter, Gia_ManPoNum(p) + nLits++ );
            if ( Gia_ObjFaninLit0p(pMiter, pObj) == 0 ) // const 0 - proven
            {
                Gia_ObjSetProved(p, i);
                Counter++;
            }
        }
        assert( nLits == nLitsAll );
    }
    if ( fVerbose )
        Abc_Print( 1, "Set %d equivalences as proved.\n", Counter );
    Gia_ManStop( pMiter );
}

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

  Synopsis    [Transforms equiv classes by filtering those that correspond to disproved outputs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivFilter( Gia_Man_t * p, Vec_Int_t * vPoIds, int fVerbose )
{
    Gia_Man_t * pSrm;
    Vec_Int_t * vTrace, * vMap;
    int i, iObjId, Entry, Prev = -1;
    // check if there are equivalences
    if ( p->pReprs == NULL || p->pNexts == NULL )
    {
        Abc_Print( 1, "Gia_ManEquivFilter(): Equivalence classes are not defined.\n" );
        return;
    }
    // check if PO indexes are available
    if ( vPoIds == NULL )
    {
        Abc_Print( 1, "Gia_ManEquivFilter(): Array of disproved POs is not available.\n" );
        return;
    }
    if ( Vec_IntSize(vPoIds) == 0 )
        return;
    // create SRM with mapping into POs
    vMap = Vec_IntAlloc( 1000 );
    vTrace = Vec_IntAlloc( 1000 );
    pSrm = Gia_ManSpecReduceTrace( p, vTrace, vMap );
    Vec_IntFree( vTrace );
    // the resulting array (vMap) maps PO indexes of the SRM into object IDs
    assert( Gia_ManPoNum(pSrm) == Gia_ManPoNum(p) + Vec_IntSize(vMap) );
    Gia_ManStop( pSrm );
    if ( fVerbose )
        printf( "Design POs = %d. SRM POs = %d. Spec POs = %d. Disproved POs = %d.\n", 
            Gia_ManPoNum(p), Gia_ManPoNum(p) + Vec_IntSize(vMap), Vec_IntSize(vMap), Vec_IntSize(vPoIds) );
    // check if disproved POs satisfy the range
    Vec_IntSort( vPoIds, 0 );
    Vec_IntForEachEntry( vPoIds, Entry, i )
    {
        if ( Entry < 0 || Entry >= Gia_ManPoNum(p) + Vec_IntSize(vMap) )
        {
            Abc_Print( 1, "Gia_ManEquivFilter(): Array of disproved POs contains PO index (%d),\n", Entry );
            Abc_Print( 1, "which does not fit into the range of available PO indexes of the SRM: [%d; %d].\n", 0, Gia_ManPoNum(p) + Vec_IntSize(vMap)-1 );
            Vec_IntFree( vMap );
            return;
        }
        if ( Entry < Gia_ManPoNum(p) )
            Abc_Print( 0, "Gia_ManEquivFilter(): One of the original POs (%d) have failed.\n", Entry );
        if ( Prev == Entry )
        {
            Abc_Print( 1, "Gia_ManEquivFilter(): Array of disproved POs contains at least one duplicate entry (%d),\n", Entry );
            Vec_IntFree( vMap );
            return;
        }
        Prev = Entry;
    }
    // perform the reduction of the equivalence classes
    Vec_IntForEachEntry( vPoIds, Entry, i )
    {
        if ( Entry < Gia_ManPoNum(p) )
            continue;
        iObjId = Vec_IntEntry( vMap, Entry - Gia_ManPoNum(p) );
        Gia_ObjUnsetRepr( p, iObjId );
//        Gia_ObjSetNext( p, iObjId, 0 );
    }
    Vec_IntFree( vMap );
    ABC_FREE( p->pNexts );
    p->pNexts = Gia_ManDeriveNexts( p );
}

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

  Synopsis    [Transforms equiv classes by filtering those that correspond to disproved outputs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivFilterTest( Gia_Man_t * p )
{
    Vec_Int_t * vPoIds;
    int i;
    vPoIds = Vec_IntAlloc( 1000 );
    for ( i = 0; i < 10; i++ )
    {
        Vec_IntPush( vPoIds, Gia_ManPoNum(p) + 2 * i + 2 );
        printf( "%d ", Gia_ManPoNum(p) + 2*i + 2 );
    }
    printf( "\n" );
    Gia_ManEquivFilter( p, vPoIds, 1 );
    Vec_IntFree( vPoIds );
}

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

  Synopsis    [Transforms equiv classes by setting a good representative.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivImprove( Gia_Man_t * p )
{
    Vec_Int_t * vClass;
    int i, k, iNode, iRepr;
    int iReprBest, iLevelBest, iLevelCur, iMffcBest, iMffcCur;
    assert( p->pReprs != NULL && p->pNexts != NULL );
    Gia_ManLevelNum( p );
    Gia_ManCreateRefs( p );
    // iterate over class candidates
    vClass = Vec_IntAlloc( 100 );
    Gia_ManForEachClass( p, i )
    {
        Vec_IntClear( vClass );
        iReprBest = -1;
        iLevelBest = iMffcBest = ABC_INFINITY;
        Gia_ClassForEachObj( p, i, k )
        {
            iLevelCur = Gia_ObjLevel( p,Gia_ManObj(p, k) );
            iMffcCur  = Gia_NodeMffcSize( p, Gia_ManObj(p, k) );
            if ( iLevelBest > iLevelCur || (iLevelBest == iLevelCur && iMffcBest > iMffcCur) )
            {
                iReprBest  = k;
                iLevelBest = iLevelCur;
                iMffcBest  = iMffcCur;
            }
            Vec_IntPush( vClass, k );
        }
        assert( Vec_IntSize( vClass ) > 1 );
        assert( iReprBest > 0 );
        if ( i == iReprBest )
            continue;
/*
        Abc_Print( 1, "Repr/Best = %6d/%6d. Lev = %3d/%3d. Mffc = %3d/%3d.\n", 
            i, iReprBest, Gia_ObjLevel( p,Gia_ManObj(p, i) ), Gia_ObjLevel( p,Gia_ManObj(p, iReprBest) ),
            Gia_NodeMffcSize( p, Gia_ManObj(p, i) ), Gia_NodeMffcSize( p, Gia_ManObj(p, iReprBest) ) );
*/
        iRepr = iReprBest;
        Gia_ObjSetRepr( p, iRepr, GIA_VOID );
        Gia_ObjSetProved( p, i );
        Gia_ObjUnsetProved( p, iRepr );
        Vec_IntForEachEntry( vClass, iNode, k )
            if ( iNode != iRepr )
                Gia_ObjSetRepr( p, iNode, iRepr );
    }
    Vec_IntFree( vClass );
    ABC_FREE( p->pNexts );
//    p->pNexts = Gia_ManDeriveNexts( p );
}


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

  Synopsis    [Returns 1 if pOld is in the TFI of pNode.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ObjCheckTfi_rec( Gia_Man_t * p, Gia_Obj_t * pOld, Gia_Obj_t * pNode, Vec_Ptr_t * vVisited )
{
    // check the trivial cases
    if ( pNode == NULL )
        return 0;
    if ( Gia_ObjIsCi(pNode) )
        return 0;
//    if ( pNode->Id < pOld->Id ) // cannot use because of choices of pNode
//        return 0;
    if ( pNode == pOld )
        return 1;
    // skip the visited node
    if ( pNode->fMark0 )
        return 0;
    pNode->fMark0 = 1;
    Vec_PtrPush( vVisited, pNode );
    // check the children
    if ( Gia_ObjCheckTfi_rec( p, pOld, Gia_ObjFanin0(pNode), vVisited ) )
        return 1;
    if ( Gia_ObjCheckTfi_rec( p, pOld, Gia_ObjFanin1(pNode), vVisited ) )
        return 1;
    // check equivalent nodes
    return Gia_ObjCheckTfi_rec( p, pOld, Gia_ObjNextObj(p, Gia_ObjId(p, pNode)), vVisited );
}

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

  Synopsis    [Returns 1 if pOld is in the TFI of pNode.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ObjCheckTfi( Gia_Man_t * p, Gia_Obj_t * pOld, Gia_Obj_t * pNode )
{
    Vec_Ptr_t * vVisited;
    Gia_Obj_t * pObj;
    int RetValue, i;
    assert( !Gia_IsComplement(pOld) );
    assert( !Gia_IsComplement(pNode) );
    vVisited = Vec_PtrAlloc( 100 );
    RetValue = Gia_ObjCheckTfi_rec( p, pOld, pNode, vVisited );
    Vec_PtrForEachEntry( Gia_Obj_t *, vVisited, pObj, i )
        pObj->fMark0 = 0;
    Vec_PtrFree( vVisited );
    return RetValue;
}


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

  Synopsis    [Adds the next entry while making choices.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManAddNextEntry_rec( Gia_Man_t * p, Gia_Obj_t * pOld, Gia_Obj_t * pNode )
{
    if ( Gia_ObjNext(p, Gia_ObjId(p, pOld)) == 0 )
    {
        Gia_ObjSetNext( p, Gia_ObjId(p, pOld), Gia_ObjId(p, pNode) );
        return;
    }
    Gia_ManAddNextEntry_rec( p, Gia_ObjNextObj(p, Gia_ObjId(p, pOld)), pNode );
}

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

  Synopsis    [Duplicates the AIG in the DFS order.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManEquivToChoices_rec( Gia_Man_t * pNew, Gia_Man_t * p, Gia_Obj_t * pObj )
{
    Gia_Obj_t * pRepr, * pReprNew, * pObjNew;
    if ( ~pObj->Value )
        return;
    if ( (pRepr = Gia_ObjReprObj(p, Gia_ObjId(p, pObj))) )
    {
        if ( Gia_ObjIsConst0(pRepr) )
        {
            pObj->Value = Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
            return;
        }
        Gia_ManEquivToChoices_rec( pNew, p, pRepr );
        assert( Gia_ObjIsAnd(pObj) );
        Gia_ManEquivToChoices_rec( pNew, p, Gia_ObjFanin0(pObj) );
        Gia_ManEquivToChoices_rec( pNew, p, Gia_ObjFanin1(pObj) );
        pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
        if ( Abc_LitRegular(pObj->Value) == Abc_LitRegular(pRepr->Value) )
        {
            assert( (int)pObj->Value == Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) ) );
            return;
        }
        if ( pRepr->Value > pObj->Value ) // should never happen with high resource limit
            return;
        assert( pRepr->Value < pObj->Value );
        pReprNew = Gia_ManObj( pNew, Abc_Lit2Var(pRepr->Value) );
        pObjNew  = Gia_ManObj( pNew, Abc_Lit2Var(pObj->Value) );
        if ( Gia_ObjReprObj( pNew, Gia_ObjId(pNew, pObjNew) ) )
        {
//            assert( Gia_ObjReprObj( pNew, Gia_ObjId(pNew, pObjNew) ) == pReprNew );
            if ( Gia_ObjReprObj( pNew, Gia_ObjId(pNew, pObjNew) ) != pReprNew )
                return;
            pObj->Value = Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
            return;
        }
        if ( !Gia_ObjCheckTfi( pNew, pReprNew, pObjNew ) )
        {
            assert( Gia_ObjNext(pNew, Gia_ObjId(pNew, pObjNew)) == 0 );
            Gia_ObjSetRepr( pNew, Gia_ObjId(pNew, pObjNew), Gia_ObjId(pNew, pReprNew) );
            Gia_ManAddNextEntry_rec( pNew, pReprNew, pObjNew );
        }
        pObj->Value = Abc_LitNotCond( pRepr->Value, Gia_ObjPhaseReal(pRepr) ^ Gia_ObjPhaseReal(pObj) );
        return;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Gia_ManEquivToChoices_rec( pNew, p, Gia_ObjFanin0(pObj) );
    Gia_ManEquivToChoices_rec( pNew, p, Gia_ObjFanin1(pObj) );
    pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
}

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

  Synopsis    [Removes choices, which contain fanouts.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManRemoveBadChoices( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, iObj, iPrev, Counter = 0;
    // mark nodes with fanout
    Gia_ManForEachObj( p, pObj, i )
    {
        pObj->fMark0 = 0;
        if ( Gia_ObjIsAnd(pObj) )
        {
            Gia_ObjFanin0(pObj)->fMark0 = 1;
            Gia_ObjFanin1(pObj)->fMark0 = 1;
        }
        else if ( Gia_ObjIsCo(pObj) )
            Gia_ObjFanin0(pObj)->fMark0 = 1;
    }
    // go through the classes and remove 
    Gia_ManForEachClass( p, i )
    {
        for ( iPrev = i, iObj = Gia_ObjNext(p, i); iObj; iObj = Gia_ObjNext(p, iPrev) )
        {
            if ( !Gia_ManObj(p, iObj)->fMark0 )
            {
                iPrev = iObj;
                continue;
            }
            Gia_ObjSetRepr( p, iObj, GIA_VOID );
            Gia_ObjSetNext( p, iPrev, Gia_ObjNext(p, iObj) );
            Gia_ObjSetNext( p, iObj, 0 );
            Counter++;
        }
    }
    // remove the marks
    Gia_ManCleanMark0( p );
//    Abc_Print( 1, "Removed %d bad choices.\n", Counter );
}

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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManEquivToChoices( Gia_Man_t * p, int nSnapshots )
{
    Vec_Int_t * vNodes;
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj, * pRepr;
    int i;
//Gia_ManEquivPrintClasses( p, 0, 0 );
    assert( (Gia_ManCoNum(p) % nSnapshots) == 0 );
    Gia_ManSetPhase( p );
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(p) );
    pNew->pNexts = ABC_CALLOC( int, Gia_ManObjNum(p) );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        Gia_ObjSetRepr( pNew, i, GIA_VOID );
    Gia_ManFillValue( p );
    Gia_ManConst0(p)->Value = 0;
    Gia_ManForEachCi( p, pObj, i )
        pObj->Value = Gia_ManAppendCi(pNew);
    Gia_ManForEachRo( p, pObj, i )
        if ( (pRepr = Gia_ObjReprObj(p, Gia_ObjId(p, pObj))) )
        {
            assert( Gia_ObjIsConst0(pRepr) || Gia_ObjIsRo(p, pRepr) );
            pObj->Value = pRepr->Value;
        }
    Gia_ManHashAlloc( pNew );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ManEquivToChoices_rec( pNew, p, Gia_ObjFanin0(pObj) );
    vNodes = Gia_ManGetDangling( p );
    Gia_ManForEachObjVec( vNodes, p, pObj, i )
        Gia_ManEquivToChoices_rec( pNew, p, pObj );
    Vec_IntFree( vNodes );
    Gia_ManForEachCo( p, pObj, i )
        if ( i % nSnapshots == 0 )
            Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
    Gia_ManHashStop( pNew );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p) );
    Gia_ManRemoveBadChoices( pNew );
//Gia_ManEquivPrintClasses( pNew, 0, 0 );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
//Gia_ManEquivPrintClasses( pNew, 0, 0 );
    return pNew;
}

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

  Synopsis    [Counts the number of choice nodes]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCountChoiceNodes( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, Counter = 0;
    if ( p->pReprs == NULL || p->pNexts == NULL )
        return 0;
    Gia_ManForEachObj( p, pObj, i )
        Counter += Gia_ObjIsHead( p, i );
    return Counter;
}

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

  Synopsis    [Counts the number of choices]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCountChoices( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, Counter = 0;
    if ( p->pReprs == NULL || p->pNexts == NULL )
        return 0;
    Gia_ManForEachObj( p, pObj, i )
        Counter += (int)(Gia_ObjNext( p, i ) > 0);
    return Counter;
}

ABC_NAMESPACE_IMPL_END

#include "aig/aig/aig.h"
#include "aig/saig/saig.h"
#include "proof/cec/cec.h"
#include "giaAig.h"

ABC_NAMESPACE_IMPL_START


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

  Synopsis    [Returns 1 if AIG has dangling nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManHasNoEquivs( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i;
    if ( p->pReprs == NULL )
        return 1;
    Gia_ManForEachObj( p, pObj, i )
        if ( Gia_ObjReprObj(p, i) != NULL )
            break;
    return i == Gia_ManObjNum(p);
}

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

  Synopsis    [Implements iteration during speculation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_CommandSpecI( Gia_Man_t * pGia, int nFramesInit, int nBTLimitInit, int fStart, int fCheckMiter, int fVerbose )
{
//    extern int Cec_ManCheckNonTrivialCands( Gia_Man_t * pAig );
    Aig_Man_t * pTemp;
    Gia_Man_t * pSrm, * pReduce, * pAux;
    int nIter, nStart = 0;
    if ( pGia->pReprs == NULL || pGia->pNexts == NULL )
    {
        Abc_Print( 1, "Gia_CommandSpecI(): Equivalence classes are not defined.\n" );
        return 0;
    }
    // (spech)*  where spech = &srm; restore save3; bmc2 -F 100 -C 25000; &resim
    Gia_ManCleanMark0( pGia );
    Gia_ManPrintStats( pGia, NULL );
    for ( nIter = 0; ; nIter++ )
    {
        if ( Gia_ManHasNoEquivs(pGia) )
        {
            Abc_Print( 1, "Gia_CommandSpecI: No equivalences left.\n" );
            break;
        }
        Abc_Print( 1, "ITER %3d : ", nIter );
//      if ( fVerbose )
//            Abc_Print( 1, "Starting BMC from frame %d.\n", nStart );
//      if ( fVerbose )
//            Gia_ManPrintStats( pGia, 0 );
            Gia_ManPrintStatsClasses( pGia );
        // perform speculative reduction
//        if ( Gia_ManPoNum(pSrm) <= Gia_ManPoNum(pGia) )
        if ( !Cec_ManCheckNonTrivialCands(pGia) )
        {
            Abc_Print( 1, "Gia_CommandSpecI: There are only trivial equiv candidates left (PO drivers). Quitting.\n" );
            break;
        }
        pSrm = Gia_ManSpecReduce( pGia, 0, 0, 1, 0, 0 );
        // bmc2 -F 100 -C 25000
        {
            Abc_Cex_t * pCex;
            int nFrames     = nFramesInit; // different from default
            int nNodeDelta  = 2000;
            int nBTLimit    = nBTLimitInit; // different from default
            int nBTLimitAll = 2000000;
            pTemp = Gia_ManToAig( pSrm, 0 );
//            Aig_ManPrintStats( pTemp );
            Gia_ManStop( pSrm );
            Saig_BmcPerform( pTemp, nStart, nFrames, nNodeDelta, 0, nBTLimit, nBTLimitAll, fVerbose, 0, NULL, 0, 0 );
            pCex = pTemp->pSeqModel; pTemp->pSeqModel = NULL;
            Aig_ManStop( pTemp );
            if ( pCex == NULL )
            {
                Abc_Print( 1, "Gia_CommandSpecI(): Internal BMC could not find a counter-example.\n" );
                break;
            }
            if ( fStart )
                nStart = pCex->iFrame;
            // perform simulation
            {
                Cec_ParSim_t Pars, * pPars = &Pars;
                Cec_ManSimSetDefaultParams( pPars );
                pPars->fCheckMiter = fCheckMiter;
                if ( Cec_ManSeqResimulateCounter( pGia, pPars, pCex ) )
                {
                    ABC_FREE( pCex );
                    break;
                }
                ABC_FREE( pCex );
            }
        }
        // write equivalence classes
        Gia_AigerWrite( pGia, "gore.aig", 0, 0, 0 );
        // reduce the model
        pReduce = Gia_ManSpecReduce( pGia, 0, 0, 1, 0, 0 );
        if ( pReduce )
        {
            pReduce = Gia_ManSeqStructSweep( pAux = pReduce, 1, 1, 0 );
            Gia_ManStop( pAux );
            Gia_AigerWrite( pReduce, "gsrm.aig", 0, 0, 0 );
//            Abc_Print( 1, "Speculatively reduced model was written into file \"%s\".\n", "gsrm.aig" );
//          Gia_ManPrintStatsShort( pReduce );
            Gia_ManStop( pReduce );
        }
    }
    return 1;
}




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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManFilterEquivsForSpeculation( Gia_Man_t * pGia, char * pName1, char * pName2, int fLatchA, int fLatchB )
{
    Gia_Man_t * pGia1, * pGia2, * pMiter;
    Gia_Obj_t * pObj1, * pObj2, * pObjM, * pObj;
    int i, iObj, iNext, Counter = 0;
    if ( pGia->pReprs == NULL || pGia->pNexts == NULL )
    {
        Abc_Print( 1, "Equivalences are not defined.\n" );
        return 0;
    }
    pGia1 = Gia_AigerRead( pName1, 0, 0, 0 );
    if ( pGia1 == NULL )
    {
        Abc_Print( 1, "Cannot read first file %s.\n", pName1 );
        return 0;
    }
    pGia2 = Gia_AigerRead( pName2, 0, 0, 0 );
    if ( pGia2 == NULL )
    {
        Gia_ManStop( pGia2 );
        Abc_Print( 1, "Cannot read second file %s.\n", pName2 );
        return 0;
    }
    pMiter = Gia_ManMiter( pGia1, pGia2, 0, 0, 1, 0, 0 );
    if ( pMiter == NULL )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Abc_Print( 1, "Cannot create sequential miter.\n" );
        return 0;
    }
    // make sure the miter is isomorphic
    if ( Gia_ManObjNum(pGia) != Gia_ManObjNum(pMiter) )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Gia_ManStop( pMiter );
        Abc_Print( 1, "The number of objects in different.\n" );
        return 0;
    }
    if ( memcmp( pGia->pObjs, pMiter->pObjs, sizeof(Gia_Obj_t) *  Gia_ManObjNum(pGia) ) )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Gia_ManStop( pMiter );
        Abc_Print( 1, "The AIG structure of the miter does not match.\n" );
        return 0;
    }
    // transfer copies
    Gia_ManCleanMark0( pGia );
    Gia_ManForEachObj( pGia1, pObj1, i )
    {
        if ( pObj1->Value == ~0 )
            continue;
        pObjM = Gia_ManObj( pMiter, Abc_Lit2Var(pObj1->Value) );
        pObj = Gia_ManObj( pGia, Gia_ObjId(pMiter, pObjM) );
        pObj->fMark0 = 1;
    }
    Gia_ManCleanMark1( pGia );
    Gia_ManForEachObj( pGia2, pObj2, i )
    {
        if ( pObj2->Value == ~0 )
            continue;
        pObjM = Gia_ManObj( pMiter, Abc_Lit2Var(pObj2->Value) );
        pObj = Gia_ManObj( pGia, Gia_ObjId(pMiter, pObjM) );
        pObj->fMark1 = 1;
    }

    // filter equivalences
    Gia_ManForEachConst( pGia, i )
    {
        Gia_ObjUnsetRepr( pGia, i );
        assert( pGia->pNexts[i] == 0 );
    }
    Gia_ManForEachClass( pGia, i )
    {
        // find the first colorA and colorB
        int ClassA = -1, ClassB = -1;
        Gia_ClassForEachObj( pGia, i, iObj )
        {
            pObj = Gia_ManObj( pGia, iObj );
            if ( ClassA == -1 && pObj->fMark0 && !pObj->fMark1 )
            {
                if ( fLatchA && !Gia_ObjIsRo(pGia, pObj) )
                    continue;
                ClassA = iObj;
            }
            if ( ClassB == -1 && pObj->fMark1 && !pObj->fMark0 )
            {
                if ( fLatchB && !Gia_ObjIsRo(pGia, pObj) )
                    continue;
                ClassB = iObj;
            }
        }
        // undo equivalence classes
        for ( iObj = i, iNext = Gia_ObjNext(pGia, iObj); iObj;
              iObj = iNext, iNext = Gia_ObjNext(pGia, iObj) )
        {
            Gia_ObjUnsetRepr( pGia, iObj );
            Gia_ObjSetNext( pGia, iObj, 0 );
        }
        assert( !Gia_ObjIsHead(pGia, i) );
        if ( ClassA > 0 && ClassB > 0 )
        {
            if ( ClassA > ClassB )
            {
                ClassA ^= ClassB;
                ClassB ^= ClassA;
                ClassA ^= ClassB;
            }
            assert( ClassA < ClassB );
            Gia_ObjSetNext( pGia, ClassA, ClassB );
            Gia_ObjSetRepr( pGia, ClassB, ClassA );
            Counter++;
            assert( Gia_ObjIsHead(pGia, ClassA) );
        }
    }
    Abc_Print( 1, "The number of two-node classes after filtering = %d.\n", Counter );
//Gia_ManEquivPrintClasses( pGia, 1, 0 );

    Gia_ManCleanMark0( pGia );
    Gia_ManCleanMark1( pGia );
    return 1;
}


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

  Synopsis    [Reduces AIG using equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManFilterEquivsUsingParts( Gia_Man_t * pGia, char * pName1, char * pName2 )
{
    Vec_Int_t * vNodes;
    Gia_Man_t * pGia1, * pGia2, * pMiter;
    Gia_Obj_t * pObj1, * pObj2, * pObjM, * pObj = NULL;
    int i, k, iObj, iNext, iPrev, iRepr;
    int iLitsOld, iLitsNew;
    if ( pGia->pReprs == NULL || pGia->pNexts == NULL )
    {
        Abc_Print( 1, "Equivalences are not defined.\n" );
        return 0;
    }
    pGia1 = Gia_AigerRead( pName1, 0, 0, 0 );
    if ( pGia1 == NULL )
    {
        Abc_Print( 1, "Cannot read first file %s.\n", pName1 );
        return 0;
    }
    pGia2 = Gia_AigerRead( pName2, 0, 0, 0 );
    if ( pGia2 == NULL )
    {
        Gia_ManStop( pGia2 );
        Abc_Print( 1, "Cannot read second file %s.\n", pName2 );
        return 0;
    }
    pMiter = Gia_ManMiter( pGia1, pGia2, 0, 0, 1, 0, 0 );
    if ( pMiter == NULL )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Abc_Print( 1, "Cannot create sequential miter.\n" );
        return 0;
    }
    // make sure the miter is isomorphic
    if ( Gia_ManObjNum(pGia) != Gia_ManObjNum(pMiter) )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Gia_ManStop( pMiter );
        Abc_Print( 1, "The number of objects in different.\n" );
        return 0;
    }
    if ( memcmp( pGia->pObjs, pMiter->pObjs, sizeof(Gia_Obj_t) *  Gia_ManObjNum(pGia) ) )
    {
        Gia_ManStop( pGia1 );
        Gia_ManStop( pGia2 );
        Gia_ManStop( pMiter );
        Abc_Print( 1, "The AIG structure of the miter does not match.\n" );
        return 0;
    }
    // transfer copies
    Gia_ManCleanMark0( pGia );
    Gia_ManForEachObj( pGia1, pObj1, i )
    {
        if ( pObj1->Value == ~0 )
            continue;
        pObjM = Gia_ManObj( pMiter, Abc_Lit2Var(pObj1->Value) );
        pObj = Gia_ManObj( pGia, Gia_ObjId(pMiter, pObjM) );
        pObj->fMark0 = 1;
    }
    Gia_ManCleanMark1( pGia );
    Gia_ManForEachObj( pGia2, pObj2, i )
    {
        if ( pObj2->Value == ~0 )
            continue;
        pObjM = Gia_ManObj( pMiter, Abc_Lit2Var(pObj2->Value) );
        pObj = Gia_ManObj( pGia, Gia_ObjId(pMiter, pObjM) );
        pObj->fMark1 = 1;
    }

    // filter equivalences
    iLitsOld = iLitsNew = 0;
    Gia_ManForEachConst( pGia, i )
    {
        iLitsOld++;
        pObj = Gia_ManObj( pGia, i );
        assert( pGia->pNexts[i] == 0 );
        assert( pObj->fMark0 || pObj->fMark1 );
        if ( pObj->fMark0 && pObj->fMark1 ) // belongs to both A and B
            Gia_ObjUnsetRepr( pGia, i );
        else
            iLitsNew++;
    }
    // filter equivalences
    vNodes = Vec_IntAlloc( 100 );
    Gia_ManForEachClass( pGia, i )
    {
        int fSeenA = 0, fSeenB = 0;
        assert( pObj->fMark0 || pObj->fMark1 );
        Vec_IntClear( vNodes );
        Gia_ClassForEachObj( pGia, i, iObj )
        {
            pObj = Gia_ManObj( pGia, iObj );
            if ( pObj->fMark0 && !pObj->fMark1 )
            {
                fSeenA = 1;
                Vec_IntPush( vNodes, iObj );
            }
            if ( !pObj->fMark0 && pObj->fMark1 )
            {
                fSeenB = 1;
                Vec_IntPush( vNodes, iObj );
            }
            iLitsOld++;
        }
        iLitsOld--;
        // undo equivalence classes
        for ( iObj = i, iNext = Gia_ObjNext(pGia, iObj); iObj;
              iObj = iNext, iNext = Gia_ObjNext(pGia, iObj) )
        {
            Gia_ObjUnsetRepr( pGia, iObj );
            Gia_ObjSetNext( pGia, iObj, 0 );
        }
        assert( !Gia_ObjIsHead(pGia, i) );
        if ( fSeenA && fSeenB && Vec_IntSize(vNodes) > 1 )
        {
            // create new class
            iPrev = iRepr = Vec_IntEntry( vNodes, 0 );
            Vec_IntForEachEntryStart( vNodes, iObj, k, 1 )
            {
                Gia_ObjSetRepr( pGia, iObj, iRepr );
                Gia_ObjSetNext( pGia, iPrev, iObj );
                iPrev = iObj;
                iLitsNew++;
            }
            assert( Gia_ObjNext(pGia, iPrev) == 0 );
        }
    }
    Vec_IntFree( vNodes );
    Abc_Print( 1, "The number of literals: Before = %d. After = %d.\n", iLitsOld, iLitsNew );
//Gia_ManEquivPrintClasses( pGia, 1, 0 );

    Gia_ManCleanMark0( pGia );
    Gia_ManCleanMark1( pGia );
    return 1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManFilterEquivsUsingLatches( Gia_Man_t * pGia, int fFlopsOnly, int fFlopsWith, int fUseRiDrivers )
{
    Gia_Obj_t * pObjR;
    Vec_Int_t * vNodes, * vFfIds;
    int i, k, iObj, iNext, iPrev, iRepr;
    int iLitsOld = 0, iLitsNew = 0;
    assert( fFlopsOnly ^ fFlopsWith );
    vNodes = Vec_IntAlloc( 100 );
    // select nodes "flop" node IDs
    vFfIds = Vec_IntStart( Gia_ManObjNum(pGia) );
    if ( fUseRiDrivers )
    {
        Gia_ManForEachRi( pGia, pObjR, i )
            Vec_IntWriteEntry( vFfIds, Gia_ObjFaninId0p(pGia, pObjR), 1 );
    }
    else
    {
        Gia_ManForEachRo( pGia, pObjR, i )
            Vec_IntWriteEntry( vFfIds, Gia_ObjId(pGia, pObjR), 1 );
    }
    // remove all non-flop constants
    Gia_ManForEachConst( pGia, i )
    {
        iLitsOld++;
        assert( pGia->pNexts[i] == 0 );
        if ( !Vec_IntEntry(vFfIds, i) )
            Gia_ObjUnsetRepr( pGia, i );
        else
            iLitsNew++;
    }
    // clear the classes
    if ( fFlopsOnly )
    {
        Gia_ManForEachClass( pGia, i )
        {
            Vec_IntClear( vNodes );
            Gia_ClassForEachObj( pGia, i, iObj )
            {
                if ( Vec_IntEntry(vFfIds, iObj) )
                    Vec_IntPush( vNodes, iObj );
                iLitsOld++;
            }
            iLitsOld--;
            // undo equivalence classes
            for ( iObj = i, iNext = Gia_ObjNext(pGia, iObj); iObj;
                  iObj = iNext, iNext = Gia_ObjNext(pGia, iObj) )
            {
                Gia_ObjUnsetRepr( pGia, iObj );
                Gia_ObjSetNext( pGia, iObj, 0 );
            }
            assert( !Gia_ObjIsHead(pGia, i) );
            if ( Vec_IntSize(vNodes) > 1 )
            {
                // create new class
                iPrev = iRepr = Vec_IntEntry( vNodes, 0 );
                Vec_IntForEachEntryStart( vNodes, iObj, k, 1 )
                {
                    Gia_ObjSetRepr( pGia, iObj, iRepr );
                    Gia_ObjSetNext( pGia, iPrev, iObj );
                    iPrev = iObj;
                    iLitsNew++;
                }
                assert( Gia_ObjNext(pGia, iPrev) == 0 );
            }
        }
    }
    else
    {
        Gia_ManForEachClass( pGia, i )
        {
            int fSeenFlop = 0;
            Gia_ClassForEachObj( pGia, i, iObj )
            {
                if ( Vec_IntEntry(vFfIds, iObj) )
                    fSeenFlop = 1;
                iLitsOld++;
                iLitsNew++;
            }
            iLitsOld--;
            iLitsNew--;
            if ( fSeenFlop )
                continue;
            // undo equivalence classes
            for ( iObj = i, iNext = Gia_ObjNext(pGia, iObj); iObj;
                  iObj = iNext, iNext = Gia_ObjNext(pGia, iObj) )
            {
                Gia_ObjUnsetRepr( pGia, iObj );
                Gia_ObjSetNext( pGia, iObj, 0 );
                iLitsNew--;
            }
            iLitsNew++;
            assert( !Gia_ObjIsHead(pGia, i) );
        }
    }
    Vec_IntFree( vNodes );
    Vec_IntFree( vFfIds );
    Abc_Print( 1, "The number of literals: Before = %d. After = %d.\n", iLitsOld, iLitsNew );
}

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


ABC_NAMESPACE_IMPL_END
