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

  FileName    [pdrTsim.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Property driven reachability.]

  Synopsis    [Improved ternary simulation.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - November 20, 2010.]

  Revision    [$Id: pdrTsim.c,v 1.00 2010/11/20 00:00:00 alanmi Exp $]

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

#include "pdrInt.h"
#include "aig/gia/giaAig.h"

ABC_NAMESPACE_IMPL_START

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

struct Txs_Man_t_
{
    Gia_Man_t * pGia;      // user's AIG
    Vec_Int_t * vPrio;     // priority of each flop
    Vec_Int_t * vCiObjs;   // cone leaves (CI obj IDs)
    Vec_Int_t * vCoObjs;   // cone roots (CO obj IDs)
    Vec_Int_t * vCiVals;   // cone leaf values (0/1 CI values)
    Vec_Int_t * vCoVals;   // cone root values (0/1 CO values)
    Vec_Int_t * vNodes;    // cone nodes (node obj IDs)
    Vec_Int_t * vTemp;     // cone nodes (node obj IDs)
    Vec_Int_t * vPiLits;   // resulting array of PI literals
    Vec_Int_t * vFfLits;   // resulting array of flop literals
    Pdr_Man_t * pMan;      // calling manager
};

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

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

  Synopsis    [Start and stop the ternary simulation engine.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Txs_Man_t * Txs_ManStart( Pdr_Man_t * pMan, Aig_Man_t * pAig, Vec_Int_t * vPrio )
{
    Txs_Man_t * p;
//    Aig_Obj_t * pObj;
//    int i;
    assert( Vec_IntSize(vPrio) == Aig_ManRegNum(pAig) );
    p = ABC_CALLOC( Txs_Man_t, 1 );
    p->pGia    = Gia_ManFromAigSimple(pAig); // user's AIG
//    Aig_ManForEachObj( pAig, pObj, i )
//        assert( i == 0 || pObj->iData == Abc_Var2Lit(i, 0) );
    p->vPrio   = vPrio;                // priority of each flop
    p->vCiObjs = Vec_IntAlloc( 100 );  // cone leaves (CI obj IDs)
    p->vCoObjs = Vec_IntAlloc( 100 );  // cone roots (CO obj IDs)
    p->vCiVals = Vec_IntAlloc( 100 );  // cone leaf values (0/1 CI values)
    p->vCoVals = Vec_IntAlloc( 100 );  // cone root values (0/1 CO values)
    p->vNodes  = Vec_IntAlloc( 100 );  // cone nodes (node obj IDs)
    p->vTemp   = Vec_IntAlloc( 100 );  // cone nodes (node obj IDs)
    p->vPiLits = Vec_IntAlloc( 100 );  // resulting array of PI literals
    p->vFfLits = Vec_IntAlloc( 100 );  // resulting array of flop literals
    p->pMan    = pMan;                 // calling manager
    return p;
}
void Txs_ManStop( Txs_Man_t * p )
{
    Gia_ManStop( p->pGia );
    Vec_IntFree( p->vCiObjs );
    Vec_IntFree( p->vCoObjs );
    Vec_IntFree( p->vCiVals );
    Vec_IntFree( p->vCoVals );
    Vec_IntFree( p->vNodes );
    Vec_IntFree( p->vTemp );
    Vec_IntFree( p->vPiLits );
    Vec_IntFree( p->vFfLits );
    ABC_FREE( p );
}

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

  Synopsis    [Marks the TFI cone and collects CIs and nodes.]

  Description [For this procedure to work Value should not be ~0 
  at the beginning.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Txs_ManCollectCone_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vCiObjs, Vec_Int_t * vNodes )
{
    if ( !~pObj->Value )
        return;
    pObj->Value = ~0;
    if ( Gia_ObjIsCi(pObj) )
    {
        Vec_IntPush( vCiObjs, Gia_ObjId(p, pObj) );
        return;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Txs_ManCollectCone_rec( p, Gia_ObjFanin0(pObj), vCiObjs, vNodes );
    Txs_ManCollectCone_rec( p, Gia_ObjFanin1(pObj), vCiObjs, vNodes );
    Vec_IntPush( vNodes, Gia_ObjId(p, pObj) );
}
void Txs_ManCollectCone( Gia_Man_t * p, Vec_Int_t * vCoObjs, Vec_Int_t * vCiObjs, Vec_Int_t * vNodes )
{
    Gia_Obj_t * pObj; int i;
//    printf( "Collecting cones for clause with %d literals.\n", Vec_IntSize(vCoObjs) );
    Vec_IntClear( vCiObjs );
    Vec_IntClear( vNodes );
    Gia_ManConst0(p)->Value = ~0;
    Gia_ManForEachObjVec( vCoObjs, p, pObj, i )
        Txs_ManCollectCone_rec( p, Gia_ObjFanin0(pObj), vCiObjs, vNodes );
}

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

  Synopsis    [Propagate values and assign priority.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Txs_ManForwardPass( Gia_Man_t * p, 
                         Vec_Int_t * vPrio, Vec_Int_t * vCiObjs, Vec_Int_t * vCiVals, 
                         Vec_Int_t * vNodes, Vec_Int_t * vCoObjs, Vec_Int_t * vCoVals )
{
    Gia_Obj_t * pObj, * pFan0, * pFan1; 
    int i, value0, value1;
    pObj = Gia_ManConst0(p);
    pObj->fMark0 = 0;
    pObj->fMark1 = 0;
    Gia_ManForEachObjVec( vCiObjs, p, pObj, i )
    {
        pObj->fMark0 = Vec_IntEntry(vCiVals, i);
        pObj->fMark1 = 0;
        pObj->Value = Gia_ObjIsPi(p, pObj) ? 0x7FFFFFFF : Vec_IntEntry(vPrio, Gia_ObjCioId(pObj)-Gia_ManPiNum(p));
        assert( ~pObj->Value );
    }
    Gia_ManForEachObjVec( vNodes, p, pObj, i )
    {
        pFan0 = Gia_ObjFanin0(pObj);
        pFan1 = Gia_ObjFanin1(pObj);
        value0 = pFan0->fMark0 ^ Gia_ObjFaninC0(pObj);
        value1 = pFan1->fMark0 ^ Gia_ObjFaninC1(pObj);
        pObj->fMark0 = value0 && value1;
        pObj->fMark1 = 0;
        if ( pObj->fMark0 )
            pObj->Value = Abc_MinInt( pFan0->Value, pFan1->Value );
        else if ( value0 )
            pObj->Value = pFan1->Value;
        else if ( value1 )
            pObj->Value = pFan0->Value;
        else // if ( value0 == 0 && value1 == 0 )
            pObj->Value = Abc_MaxInt( pFan0->Value, pFan1->Value );
        assert( ~pObj->Value );
    }
    Gia_ManForEachObjVec( vCoObjs, p, pObj, i )
    {
        pFan0 = Gia_ObjFanin0(pObj);
        pObj->fMark0 = (pFan0->fMark0 ^ Gia_ObjFaninC0(pObj));
        pFan0->fMark1 = 1;
        assert( (int)pObj->fMark0 == Vec_IntEntry(vCoVals, i) );
    }
}

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

  Synopsis    [Propagate requirements and collect results.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Txs_ObjIsJust( Gia_Man_t * p, Gia_Obj_t * pObj )
{
    Gia_Obj_t * pFan0 = Gia_ObjFanin0(pObj);
    Gia_Obj_t * pFan1 = Gia_ObjFanin1(pObj);
    int value0 = pFan0->fMark0 ^ Gia_ObjFaninC0(pObj);
    int value1 = pFan1->fMark0 ^ Gia_ObjFaninC1(pObj);
    assert( Gia_ObjIsAnd(pObj) );
    if ( pObj->fMark0 )
        return pFan0->fMark1 && pFan1->fMark1;
    assert( !pObj->fMark0 );
    assert( !value0 || !value1 );
    if ( value0 )
        return pFan1->fMark1 || Gia_ObjIsPi(p, pFan1);
    if ( value1 )
        return pFan0->fMark1 || Gia_ObjIsPi(p, pFan0);
    assert( !value0 && !value1 );
    return pFan0->fMark1 || pFan1->fMark1 || Gia_ObjIsPi(p, pFan0) || Gia_ObjIsPi(p, pFan1);
}
void Txs_ManBackwardPass( Gia_Man_t * p, Vec_Int_t * vCiObjs, Vec_Int_t * vNodes, Vec_Int_t * vPiLits, Vec_Int_t * vFfLits )
{
    Gia_Obj_t * pObj, * pFan0, * pFan1; int i, value0, value1;
    Gia_ManForEachObjVecReverse( vNodes, p, pObj, i )
    {
        if ( !pObj->fMark1 )
            continue;
        pObj->fMark1 = 0;
        pFan0 = Gia_ObjFanin0(pObj);
        pFan1 = Gia_ObjFanin1(pObj);
        if ( pObj->fMark0 )
        {
            pFan0->fMark1 = 1;
            pFan1->fMark1 = 1;
            continue;
        }
        value0 = pFan0->fMark0 ^ Gia_ObjFaninC0(pObj);
        value1 = pFan1->fMark0 ^ Gia_ObjFaninC1(pObj);
        assert( !value0 || !value1 );
        if ( value0 )
            pFan1->fMark1 = 1;
        else if ( value1 )
            pFan0->fMark1 = 1;
        else // if ( !value0 && !value1 )
        {
            if ( pFan0->fMark1 || pFan1->fMark1 )
                continue;
            if ( Gia_ObjIsPi(p, pFan0) )
                pFan0->fMark1 = 1;
            else if ( Gia_ObjIsPi(p, pFan1) )
                pFan1->fMark1 = 1;
            else if ( Gia_ObjIsAnd(pFan0) && Txs_ObjIsJust(p, pFan0) )
                pFan0->fMark1 = 1;
            else if ( Gia_ObjIsAnd(pFan1) && Txs_ObjIsJust(p, pFan1) )
                pFan1->fMark1 = 1;
            else
            {
                if ( pFan0->Value >= pFan1->Value )
                    pFan0->fMark1 = 1;
                else
                    pFan1->fMark1 = 1;
            }
        }
    }
    Vec_IntClear( vPiLits );
    Vec_IntClear( vFfLits );
    Gia_ManForEachObjVec( vCiObjs, p, pObj, i )
    {
        if ( !pObj->fMark1 )
            continue;
        if ( Gia_ObjIsPi(p, pObj) )
            Vec_IntPush( vPiLits, Abc_Var2Lit(Gia_ObjCioId(pObj),                 !pObj->fMark0) );
        else
            Vec_IntPush( vFfLits, Abc_Var2Lit(Gia_ObjCioId(pObj)-Gia_ManPiNum(p), !pObj->fMark0) );
    }
    assert( Vec_IntSize(vFfLits) > 0 );
}

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

  Synopsis    [Collects justification path.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Txs_ManSelectJustPath( Gia_Man_t * p, Vec_Int_t * vNodes, Vec_Int_t * vCoObjs, Vec_Int_t * vRes )
{
    Gia_Obj_t * pObj, * pFan0, * pFan1; 
    int i, value0, value1;
    // mark CO drivers
    Gia_ManForEachObjVec( vCoObjs, p, pObj, i )
        Gia_ObjFanin0(pObj)->fMark1 = 1;
    // collect just paths
    Vec_IntClear( vRes );
    Gia_ManForEachObjVecReverse( vNodes, p, pObj, i )
    {
        if ( !pObj->fMark1 )
            continue;
        pObj->fMark1 = 0;
        Vec_IntPush( vRes, Gia_ObjId(p, pObj) );
        pFan0 = Gia_ObjFanin0(pObj);
        pFan1 = Gia_ObjFanin1(pObj);
        if ( pObj->fMark0 )
        {
            pFan0->fMark1 = 1;
            pFan1->fMark1 = 1;
            continue;
        }
        value0 = pFan0->fMark0 ^ Gia_ObjFaninC0(pObj);
        value1 = pFan1->fMark0 ^ Gia_ObjFaninC1(pObj);
        assert( !value0 || !value1 );
        if ( value0 )
            pFan1->fMark1 = 1;
        else if ( value1 )
            pFan0->fMark1 = 1;
        else // if ( !value0 && !value1 )
        {
            pFan0->fMark1 = 1;
            pFan1->fMark1 = 1;
        }
    }
    Vec_IntReverseOrder( vRes );
}
void Txs_ManCollectJustPis( Gia_Man_t * p, Vec_Int_t * vCiObjs, Vec_Int_t * vPiLits )
{
    Gia_Obj_t * pObj; int i;
    Vec_IntClear( vPiLits );
    Gia_ManForEachObjVec( vCiObjs, p, pObj, i )
        if ( pObj->fMark1 && Gia_ObjIsPi(p, pObj) )
            Vec_IntPush( vPiLits, Abc_Var2Lit(Gia_ObjCioId(pObj), !pObj->fMark0) );
}
void Txs_ManInitPrio( Gia_Man_t * p, Vec_Int_t * vCiObjs )
{
    Gia_Obj_t * pObj; int i;
    Gia_ManConst0(p)->Value = 0x7FFFFFFF;
    Gia_ManForEachObjVec( vCiObjs, p, pObj, i )
        pObj->Value = Gia_ObjIsPi(p, pObj) ? 0x7FFFFFFF : Gia_ObjCioId(pObj) - Gia_ManPiNum(p);
}
void Txs_ManPropagatePrio( Gia_Man_t * p, Vec_Int_t * vNodes, Vec_Int_t * vPrio )
{
    Gia_Obj_t * pObj, * pFan0, * pFan1;
    int i, value0, value1;
    Gia_ManForEachObjVec( vNodes, p, pObj, i )
    {
        pFan0 = Gia_ObjFanin0(pObj);
        pFan1 = Gia_ObjFanin1(pObj);
        if ( pObj->fMark0 )
        {
//            pObj->Value = Abc_MinInt( pFan0->Value, pFan1->Value );
            if ( pFan0->Value == 0x7FFFFFFF )
                pObj->Value = pFan1->Value;
            else if ( pFan1->Value == 0x7FFFFFFF )
                pObj->Value = pFan0->Value;
            else if ( Vec_IntEntry(vPrio, pFan0->Value) < Vec_IntEntry(vPrio, pFan1->Value) )
                pObj->Value = pFan0->Value;
            else
                pObj->Value = pFan1->Value;
            continue;
        }
        value0 = pFan0->fMark0 ^ Gia_ObjFaninC0(pObj);
        value1 = pFan1->fMark0 ^ Gia_ObjFaninC1(pObj);
        assert( !value0 || !value1 );
        if ( value0 )
            pObj->Value = pFan1->Value;
        else if ( value1 )
            pObj->Value = pFan0->Value;
        else // if ( value0 == 0 && value1 == 0 )
        {
//            pObj->Value = Abc_MaxInt( pFan0->Value, pFan1->Value );
            if ( pFan0->Value == 0x7FFFFFFF || pFan1->Value == 0x7FFFFFFF )
                pObj->Value = 0x7FFFFFFF;
            else if ( Vec_IntEntry(vPrio, pFan0->Value) >= Vec_IntEntry(vPrio, pFan1->Value) )
                pObj->Value = pFan0->Value;
            else
                pObj->Value = pFan1->Value;
        }
        assert( ~pObj->Value );
    }
}
int Txs_ManFindMinId( Gia_Man_t * p, Vec_Int_t * vCoObjs, Vec_Int_t * vPrio )
{
    Gia_Obj_t * pObj; int i, iMinId = -1;
    Gia_ManForEachObjVec( vCoObjs, p, pObj, i )
        if ( Gia_ObjFanin0(pObj)->Value != 0x7FFFFFFF )
        {
            if ( iMinId == -1 || Vec_IntEntry(vPrio, iMinId) > Vec_IntEntry(vPrio, Gia_ObjFanin0(pObj)->Value) )
                iMinId = Gia_ObjFanin0(pObj)->Value;
        }
    return iMinId;
}
void Txs_ManFindCiReduction( Gia_Man_t * p, 
                         Vec_Int_t * vPrio, Vec_Int_t * vCiObjs, 
                         Vec_Int_t * vNodes, Vec_Int_t * vCoObjs, 
                         Vec_Int_t * vPiLits, Vec_Int_t * vFfLits, Vec_Int_t * vTemp )
{
    Gia_Obj_t * pObj;
    int iPrioCi;
    // propagate PI influence
    Txs_ManSelectJustPath( p, vNodes, vCoObjs, vTemp );
    Txs_ManCollectJustPis( p, vCiObjs, vPiLits );
//    printf( "%d -> %d  ", Vec_IntSize(vNodes), Vec_IntSize(vTemp) );
    // iteratively detect and remove smallest IDs
    Vec_IntClear( vFfLits );
    Txs_ManInitPrio( p, vCiObjs );
    while ( 1 )
    {
        Txs_ManPropagatePrio( p, vTemp, vPrio );
        iPrioCi = Txs_ManFindMinId( p, vCoObjs, vPrio );
        if ( iPrioCi == -1 )
            break;
        pObj = Gia_ManCi( p, Gia_ManPiNum(p)+iPrioCi );
        Vec_IntPush( vFfLits, Abc_Var2Lit(iPrioCi, !pObj->fMark0) );
        pObj->Value = 0x7FFFFFFF;
    }
}
void Txs_ManPrintFlopLits( Vec_Int_t * vFfLits, Vec_Int_t * vPrio )
{
    int i, Entry;
    printf( "%3d : ", Vec_IntSize(vFfLits) );
    Vec_IntForEachEntry( vFfLits, Entry, i )
        printf( "%s%d(%d) ", Abc_LitIsCompl(Entry)? "+":"-", Abc_Lit2Var(Entry), Vec_IntEntry(vPrio, Abc_Lit2Var(Entry)) );
    printf( "\n" );
}

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

  Synopsis    [Verify the result.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Txs_ManVerify( Gia_Man_t * p, Vec_Int_t * vCiObjs, Vec_Int_t * vNodes, Vec_Int_t * vPiLits, Vec_Int_t * vFfLits, Vec_Int_t * vCoObjs, Vec_Int_t * vCoVals )
{
    Gia_Obj_t * pObj;
    int i, iLit;
    Gia_ObjTerSimSet0( Gia_ManConst0(p) );
    Gia_ManForEachObjVec( vCiObjs, p, pObj, i )
        Gia_ObjTerSimSetX( pObj );
    Vec_IntForEachEntry( vPiLits, iLit, i )
    {
        pObj = Gia_ManPi( p, Abc_Lit2Var(iLit) );
        assert( Gia_ObjTerSimGetX(pObj) );
        if ( Abc_LitIsCompl(iLit) )
            Gia_ObjTerSimSet0( pObj );
        else
            Gia_ObjTerSimSet1( pObj );
    }
    Vec_IntForEachEntry( vFfLits, iLit, i )
    {
        pObj = Gia_ManCi( p, Gia_ManPiNum(p) + Abc_Lit2Var(iLit) );
        assert( Gia_ObjTerSimGetX(pObj) );
        if ( Abc_LitIsCompl(iLit) )
            Gia_ObjTerSimSet0( pObj );
        else
            Gia_ObjTerSimSet1( pObj );
    }
    Gia_ManForEachObjVec( vNodes, p, pObj, i )
        Gia_ObjTerSimAnd( pObj );
    Gia_ManForEachObjVec( vCoObjs, p, pObj, i )
    {
        Gia_ObjTerSimCo( pObj );
        if ( Vec_IntEntry(vCoVals, i) )
            assert( Gia_ObjTerSimGet1(pObj) );
        else
            assert( Gia_ObjTerSimGet0(pObj) );
    }
}

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

  Synopsis    [Shrinks values using ternary simulation.]

  Description [The cube contains the set of flop index literals which,
  when converted into a clause and applied to the combinational outputs, 
  led to a satisfiable SAT run in frame k (values stored in the SAT solver).
  If the cube is NULL, it is assumed that the first property output was
  asserted and failed.
  The resulting array is a set of flop index literals that asserts the COs.
  Priority contains 0 for i-th entry if the i-th FF is desirable to remove.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Pdr_Set_t * Txs_ManTernarySim( Txs_Man_t * p, int k, Pdr_Set_t * pCube )
{
    int fTryNew = 1;
    Pdr_Set_t * pRes;
    Gia_Obj_t * pObj;
    // collect CO objects
    Vec_IntClear( p->vCoObjs );
    if ( pCube == NULL ) // the target is the property output
    {
        pObj = Gia_ManCo(p->pGia, p->pMan->iOutCur);
        Vec_IntPush( p->vCoObjs, Gia_ObjId(p->pGia, pObj) );
    }
    else // the target is the cube
    {
        int i;
        for ( i = 0; i < pCube->nLits; i++ )
        {
            if ( pCube->Lits[i] == -1 )
                continue;
            pObj = Gia_ManCo(p->pGia, Gia_ManPoNum(p->pGia) + Abc_Lit2Var(pCube->Lits[i]));
            Vec_IntPush( p->vCoObjs, Gia_ObjId(p->pGia, pObj) );
        }
    }
if ( 0 )
{
Abc_Print( 1, "Trying to justify cube " );
if ( pCube )
    Pdr_SetPrint( stdout, pCube, Gia_ManRegNum(p->pGia), NULL );
else
    Abc_Print( 1, "<prop=fail>" );
Abc_Print( 1, " in frame %d.\n", k );
}

    // collect CI objects
    Txs_ManCollectCone( p->pGia, p->vCoObjs, p->vCiObjs, p->vNodes );
    // collect values
    Pdr_ManCollectValues( p->pMan, k, p->vCiObjs, p->vCiVals );
    Pdr_ManCollectValues( p->pMan, k, p->vCoObjs, p->vCoVals );

    // perform two passes
    Txs_ManForwardPass( p->pGia, p->vPrio, p->vCiObjs, p->vCiVals, p->vNodes, p->vCoObjs, p->vCoVals );
    if ( fTryNew )
        Txs_ManFindCiReduction( p->pGia, p->vPrio, p->vCiObjs, p->vNodes, p->vCoObjs, p->vPiLits, p->vFfLits, p->vTemp );
    else
        Txs_ManBackwardPass( p->pGia, p->vCiObjs, p->vNodes, p->vPiLits, p->vFfLits );
    Txs_ManVerify( p->pGia, p->vCiObjs, p->vNodes, p->vPiLits, p->vFfLits, p->vCoObjs, p->vCoVals );

    // derive the final set
    pRes = Pdr_SetCreate( p->vFfLits, p->vPiLits );
    //ZH: Disabled assertion because this invariant doesn't hold with down
    //because of the join operation which can bring in initial states
    //assert( k == 0 || !Pdr_SetIsInit(pRes, -1) );
    return pRes;
}

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


ABC_NAMESPACE_IMPL_END