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

  FileName    [giaMfs.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Interface with the MFS package.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "bool/kit/kit.h"
#include "opt/sfm/sfm.h"
#include "misc/tim/tim.h"

ABC_NAMESPACE_IMPL_START


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

static word s_ElemVar  = ABC_CONST(0xAAAAAAAAAAAAAAAA);
static word s_ElemVar2 = ABC_CONST(0xCCCCCCCCCCCCCCCC);

extern int Kit_TruthToGia( Gia_Man_t * pMan, unsigned * pTruth, int nVars, Vec_Int_t * vMemory, Vec_Int_t * vLeaves, int fHash );

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManExtractMfs_rec( Gia_Man_t * p, int iObj, Vec_Int_t * vId2Mfs, Vec_Wec_t * vFanins, Vec_Str_t * vFixed, Vec_Wrd_t * vTruths, Vec_Wrd_t * vTruthsTemp )
{
    Vec_Int_t * vArray;
    int i, Fanin;
    Gia_Obj_t * pObj = Gia_ManObj( p, iObj );
    assert( Gia_ObjIsLut(p, iObj) );
    if ( !~pObj->Value )
        return;
    Gia_LutForEachFanin( p, iObj, Fanin, i )
        Gia_ManExtractMfs_rec( p, Fanin, vId2Mfs, vFanins, vFixed, vTruths, vTruthsTemp );
    pObj->Value = Vec_WecSize(vFanins);
    vArray = Vec_WecPushLevel( vFanins );
    Vec_IntGrow( vArray, Gia_ObjLutSize(p, iObj) );
    Gia_LutForEachFanin( p, iObj, Fanin, i )
        Vec_IntPush( vArray, Gia_ManObj(p, Fanin)->Value );
    Vec_StrPush( vFixed, (char)0 );
    Vec_WrdPush( vTruths, Gia_ObjComputeTruthTable6Lut(p, iObj, vTruthsTemp) );
    Vec_IntWriteEntry( vId2Mfs, iObj, pObj->Value );
}
void Gia_ManExtractMfs_rec2( Gia_Man_t * p, int iObj, Vec_Int_t * vId2Mfs, Vec_Wec_t * vFanins, Vec_Str_t * vFixed, Vec_Wrd_t * vTruths )
{
    Vec_Int_t * vArray;
    Gia_Obj_t * pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsTravIdCurrent(p, pObj) )
        return;
    Gia_ObjSetTravIdCurrent(p, pObj);
    assert( Gia_ObjIsAnd(pObj) );
    Gia_ManExtractMfs_rec2( p, Gia_ObjFaninId0(pObj, iObj), vId2Mfs, vFanins, vFixed, vTruths );
    Gia_ManExtractMfs_rec2( p, Gia_ObjFaninId1(pObj, iObj), vId2Mfs, vFanins, vFixed, vTruths );
    pObj->Value = Vec_WecSize(vFanins);
    vArray = Vec_WecPushLevel( vFanins );
    Vec_IntGrow( vArray, 2 );
    Vec_IntPush( vArray, Gia_ObjFanin0(pObj)->Value );
    Vec_IntPush( vArray, Gia_ObjFanin1(pObj)->Value );
    Vec_StrPush( vFixed, (char)1 );
    Vec_WrdPush( vTruths, (Gia_ObjFaninC0(pObj) ? ~s_ElemVar : s_ElemVar) & (Gia_ObjFaninC1(pObj) ? ~s_ElemVar2 : s_ElemVar2) );
    Vec_IntWriteEntry( vId2Mfs, iObj, pObj->Value );
}
Sfm_Ntk_t * Gia_ManExtractMfs( Gia_Man_t * p, Gia_Man_t * pBoxes, Vec_Int_t ** pvId2Mfs )
{
    Tim_Man_t * pManTime = (Tim_Man_t *)p->pManTime;
    Vec_Int_t * vPoNodes;
    Vec_Int_t * vId2Mfs;
    Vec_Wec_t * vFanins;
    Vec_Str_t * vFixed;
    Vec_Wrd_t * vTruths, * vTruthsTemp;
    Vec_Int_t * vArray;
    Gia_Obj_t * pObj, * pObjBox;
    int i, k, nRealPis, nRealPos, nPiNum, nPoNum, curCi, curCo;
    assert( pManTime == NULL || Tim_ManCiNum(pManTime) == Gia_ManCiNum(p) );
    assert( pManTime == NULL || Tim_ManCoNum(pManTime) == Gia_ManCoNum(p) );
    // get the real number of PIs and POs
    nRealPis = pManTime ? Tim_ManPiNum(pManTime) : Gia_ManCiNum(p);
    nRealPos = pManTime ? Tim_ManPoNum(pManTime) : Gia_ManCoNum(p);
    // create mapping from GIA into MFS
    vId2Mfs  = Vec_IntStartFull( Gia_ManObjNum(p) );
    // collect PO nodes
    vPoNodes = Vec_IntAlloc( 1000 );
    // create the arrays
    vFanins  = Vec_WecAlloc( 1000 );
    vFixed   = Vec_StrAlloc( 1000 );
    vTruths  = Vec_WrdAlloc( 1000 );
    vTruthsTemp = Vec_WrdStart( Gia_ManObjNum(p) );
    // assign MFS ids to primary inputs
    Gia_ManFillValue( p );
    for ( i = 0; i < nRealPis; i++ )
    {
        pObj = Gia_ManPi( p, i );
        pObj->Value = Vec_WecSize(vFanins);
        Vec_WecPushLevel( vFanins );
        Vec_StrPush( vFixed, (char)0 );
        Vec_WrdPush( vTruths, (word)0 );
        Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), pObj->Value );
    }
    // assign MFS ids to black box outputs
    curCi = nRealPis;
    curCo = 0;
    if ( pManTime )
    for ( i = 0; i < Tim_ManBoxNum(pManTime); i++ )
    {
        if ( !Tim_ManBoxIsBlack(pManTime, i) )
        {
            // collect POs
            for ( k = 0; k < Tim_ManBoxInputNum(pManTime, i); k++ )
            {
                pObj = Gia_ManPo( p, curCo + k );
                Vec_IntPush( vPoNodes, Gia_ObjId(p, pObj) );
            }
            // assign values to the PIs
            for ( k = 0; k < Tim_ManBoxOutputNum(pManTime, i); k++ )
            {
                pObj = Gia_ManPi( p, curCi + k );
                pObj->Value = Vec_WecSize(vFanins);
                Vec_WecPushLevel( vFanins );
                Vec_StrPush( vFixed, (char)1 );
                Vec_WrdPush( vTruths, (word)0 );
                Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), pObj->Value );
            }
        }
        curCo += Tim_ManBoxInputNum(pManTime, i);
        curCi += Tim_ManBoxOutputNum(pManTime, i);
    }
    // collect POs
//    for ( i = Tim_ManCoNum(pManTime) - Tim_ManPoNum(pManTime); i < Tim_ManCoNum(pManTime); i++ )
    for ( i = Gia_ManCoNum(p) - nRealPos; i < Gia_ManCoNum(p); i++ )
    {
        pObj = Gia_ManPo( p, i );
        Vec_IntPush( vPoNodes, Gia_ObjId(p, pObj) );
    }
    curCo += nRealPos;
    // verify counts
    assert( curCi == Gia_ManPiNum(p) );
    assert( curCo == Gia_ManPoNum(p) );
    // remeber the end of PIs
    nPiNum = Vec_WecSize(vFanins);
    nPoNum = Vec_IntSize(vPoNodes);
    // assign value to constant node
    pObj = Gia_ManConst0(p);
    Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), Vec_WecSize(vFanins) );
    pObj->Value = Vec_WecSize(vFanins);
    Vec_WecPushLevel( vFanins );
    Vec_StrPush( vFixed, (char)0 );
    Vec_WrdPush( vTruths, (word)0 );
    Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), pObj->Value );
    // create internal nodes
    curCi = nRealPis;
    curCo = 0;
    if ( pManTime )
    for ( i = 0; i < Tim_ManBoxNum(pManTime); i++ )
    {
        // recursively add for box inputs
        Gia_ManIncrementTravId( pBoxes );
        for ( k = 0; k < Tim_ManBoxInputNum(pManTime, i); k++ )
        {
            // build logic
            pObj = Gia_ManPo( p, curCo + k );
            Gia_ManExtractMfs_rec( p, Gia_ObjFaninId0p(p, pObj), vId2Mfs, vFanins, vFixed, vTruths, vTruthsTemp );
            // add buffer/inverter
            pObj->Value = Vec_WecSize(vFanins);
            vArray = Vec_WecPushLevel( vFanins );
            Vec_IntGrow( vArray, 1 );
            assert( !~Gia_ObjFanin0(pObj)->Value );
            Vec_IntPush( vArray, Gia_ObjFanin0(pObj)->Value );
            Vec_StrPush( vFixed, (char)0 );
            Vec_WrdPush( vTruths, Gia_ObjFaninC0(pObj) ? ~s_ElemVar : s_ElemVar );
            Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), pObj->Value );
            // transfer to the PI
            pObjBox = Gia_ManPi( pBoxes, k );
            pObjBox->Value = pObj->Value;
            Gia_ObjSetTravIdCurrent( pBoxes, pObjBox );
        }
        if ( !Tim_ManBoxIsBlack(pManTime, i) )
        {
            pObjBox = Gia_ManConst0(pBoxes);
            pObjBox->Value = Vec_WecSize(vFanins);
            Vec_WecPushLevel( vFanins );
            Vec_StrPush( vFixed, (char)0 );
            Vec_WrdPush( vTruths, (word)0 );
            Gia_ObjSetTravIdCurrent( pBoxes, pObjBox );
            // add internal nodes and transfer
            for ( k = 0; k < Tim_ManBoxOutputNum(pManTime, i); k++ )
            {
                // build logic
                pObjBox = Gia_ManPo( pBoxes, curCi - Tim_ManPiNum(pManTime) + k );
                Gia_ManExtractMfs_rec2( pBoxes, Gia_ObjFaninId0p(pBoxes, pObjBox), vId2Mfs, vFanins, vFixed, vTruths );
                // add buffer/inverter
                vArray = Vec_WecPushLevel( vFanins );
                Vec_IntGrow( vArray, 1 );
                assert( !~Gia_ObjFanin0(pObjBox)->Value );
                Vec_IntPush( vArray, Gia_ObjFanin0(pObjBox)->Value );
                Vec_StrPush( vFixed, (char)1 );
                Vec_WrdPush( vTruths, Gia_ObjFaninC0(pObjBox) ? ~s_ElemVar : s_ElemVar );
                // transfer to the PI
                pObj = Gia_ManPi( p, curCi + k );
                pObj->Value = pObjBox->Value;
            }
        }
        curCo += Tim_ManBoxInputNum(pManTime, i);
        curCi += Tim_ManBoxOutputNum(pManTime, i);
    }
    // create POs with buffers
    Gia_ManForEachObjVec( vPoNodes, p, pObj, i )
    {
        Gia_ManExtractMfs_rec( p, Gia_ObjFaninId0p(p, pObj), vId2Mfs, vFanins, vFixed, vTruths, vTruthsTemp );
        pObj->Value = Vec_WecSize(vFanins);
        // add buffer/inverter
        vArray = Vec_WecPushLevel( vFanins );
        Vec_IntGrow( vArray, 1 );
        assert( !~Gia_ObjFanin0(pObj)->Value );
        Vec_IntPush( vArray, Gia_ObjFanin0(pObj)->Value );
        Vec_StrPush( vFixed, (char)0 );
        Vec_WrdPush( vTruths, Gia_ObjFaninC0(pObj) ? ~s_ElemVar : s_ElemVar );
        Vec_IntWriteEntry( vId2Mfs, Gia_ObjId(p, pObj), pObj->Value );
    }
    Vec_IntFree( vPoNodes );
    Vec_WrdFree( vTruthsTemp );
    *pvId2Mfs = vId2Mfs;
    return Sfm_NtkConstruct( vFanins, nPiNum, nPoNum, vFixed, NULL, vTruths );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManInsertMfs( Gia_Man_t * p, Sfm_Ntk_t * pNtk, Vec_Int_t * vId2Mfs )
{
    Gia_Man_t * pNew;
    Gia_Obj_t * pObj;
    Vec_Int_t * vMfsTopo, * vMfs2New, * vArray, * vCover;
    int i, k, Fanin, iMfsId, iLitNew;
    word * pTruth;
    // collect MFS nodes in the topo order
    vMfsTopo = Sfm_NtkDfs( pNtk );
    // create mapping from MFS to new GIA literals
    vMfs2New = Vec_IntStartFull( Vec_IntCap(vMfsTopo) );
    // start new GIA
    pNew = Gia_ManStart( Gia_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    // map primary inputs
    Gia_ManForEachCi( p, pObj, i )
    {
        iMfsId = Vec_IntEntry( vId2Mfs, Gia_ObjId(p, pObj) );
        assert( iMfsId >= 0 );
        Vec_IntWriteEntry( vMfs2New, iMfsId, Gia_ManAppendCi(pNew) );
    }
    // map internal nodes
    vCover = Vec_IntAlloc( 1 << 16 );
    Vec_IntForEachEntry( vMfsTopo, iMfsId, i )
    {
        assert( Sfm_NodeReadUsed(pNtk, iMfsId) );
        pTruth = Sfm_NodeReadTruth( pNtk, iMfsId );
        if ( pTruth[0] == 0 || ~pTruth[0] == 0 )
        {
            Vec_IntWriteEntry( vMfs2New, iMfsId, 0 );
            continue;
        }
        vArray = Sfm_NodeReadFanins( pNtk, iMfsId ); // belongs to pNtk
        Vec_IntForEachEntry( vArray, Fanin, k )
        {
            iLitNew = Vec_IntEntry( vMfs2New, Fanin );
            assert( iLitNew >= 0 );
            Vec_IntWriteEntry( vArray, k, iLitNew );            
        }
        // derive new function
        iLitNew = Kit_TruthToGia( pNew, (unsigned *)pTruth, Vec_IntSize(vArray), vCover, vArray, 0 );
        Vec_IntWriteEntry( vMfs2New, iMfsId, iLitNew );
    }
    Vec_IntFree( vCover );
    // map output nodes
    Gia_ManForEachCo( p, pObj, i )
    {
        iMfsId = Vec_IntEntry( vId2Mfs, Gia_ObjId(p, pObj) );
        assert( iMfsId >= 0 );
        vArray = Sfm_NodeReadFanins( pNtk, iMfsId ); // belongs to pNtk
        assert( Vec_IntSize(vArray) == 1 );
        // get the fanin
        iLitNew = Vec_IntEntry( vMfs2New, Vec_IntEntry(vArray, 0) );
        assert( iLitNew >= 0 );
        // create CO
        pTruth = Sfm_NodeReadTruth( pNtk, iMfsId );
        assert( pTruth[0] == s_ElemVar || ~pTruth[0] == s_ElemVar );
        Gia_ManAppendCo( pNew, Abc_LitNotCond(iLitNew, (int)(pTruth[0] != s_ElemVar)) );
    }
    Vec_IntFree( vMfs2New );
    Vec_IntFree( vMfsTopo );
    return pNew;
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManPerformMfs( Gia_Man_t * p, Sfm_Par_t * pPars )
{
    Sfm_Ntk_t * pNtk;
    Vec_Int_t * vId2Mfs;
    Gia_Man_t * pNew;
    int nFaninMax, nNodes;
    assert( Gia_ManRegNum(p) == 0 );
    assert( p->vMapping != NULL );
    if ( p->pManTime != NULL && p->pAigExtra == NULL )
    {
        Abc_Print( 1, "Timing manager is given but there is no GIA of boxes.\n" );
        return NULL;
    }
    // count fanouts
    nFaninMax = Gia_ManLutSizeMax( p );
    if ( nFaninMax > 6 )
    {
        Abc_Print( 1, "Currently \"&mfs\" cannot process the network containing nodes with more than 6 fanins.\n" );
        return NULL;
    }
    // collect information
    pNtk = Gia_ManExtractMfs( p, p->pAigExtra, &vId2Mfs );
    // perform optimization
    nNodes = Sfm_NtkPerform( pNtk, pPars );
    // call the fast extract procedure
    if ( nNodes == 0 )
    {
        Abc_Print( 1, "The network is not changed by \"&mfs\".\n" );
        pNew = Gia_ManDup( p );
        pNew->vMapping = Vec_IntDup( p->vMapping );
    }
    else
    {
        pNew = Gia_ManInsertMfs( p, pNtk, vId2Mfs );
        if( pPars->fVerbose )
            Abc_Print( 1, "The network has %d nodes changed by \"&mfs\".\n", nNodes );
    }
    Vec_IntFree( vId2Mfs );
    Sfm_NtkFree( pNtk );
    return pNew;
}


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


ABC_NAMESPACE_IMPL_END