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

  FileName    [abcBlifMv.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [Procedures to process BLIF-MV networks and AIGs.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "abc.h"

////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

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

  Synopsis    [Starts the Mv-Var manager.]

  Description []
  
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkStartMvVars( Abc_Ntk_t * pNtk ) 
{
    Vec_Att_t * pAttMan;
    assert( Abc_NtkMvVar(pNtk) == NULL );
    pAttMan = Vec_AttAlloc( 0, Abc_NtkObjNumMax(pNtk) + 1, Extra_MmFlexStart(), Extra_MmFlexStop, NULL, NULL );
    Vec_PtrWriteEntry( pNtk->vAttrs, VEC_ATTR_MVVAR, pAttMan );
//fprintf(outStream,  "allocing attr\n" );
}

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

  Synopsis    [Stops the Mv-Var manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkFreeMvVars( Abc_Ntk_t * pNtk ) 
{ 
    void * pUserMan;
    pUserMan = Abc_NtkAttrFree( pNtk, VEC_ATTR_GLOBAL_BDD, 0 ); 
    Extra_MmFlexStop( pUserMan );
}

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

  Synopsis    [Duplicate the MV variable.]

  Description []
  
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkSetMvVarValues( Abc_Obj_t * pObj, int nValues )
{
    Extra_MmFlex_t * pFlex;
    struct temp 
    { 
        int nValues; 
        char ** pNames; 
    } * pVarStruct;
    assert( nValues > 1 );
    // skip binary signals
    if ( nValues == 2 )
        return;
    // skip already assigned signals
    if ( Abc_ObjMvVar(pObj) != NULL )
        return;
    // create the structure
    pFlex = Abc_NtkMvVarMan( pObj->pNtk );
    pVarStruct = (void *)Extra_MmFlexEntryFetch( pFlex, sizeof(struct temp) );
    pVarStruct->nValues = nValues;
    pVarStruct->pNames = NULL;
    Abc_ObjSetMvVar( pObj, pVarStruct );
}

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

  Synopsis    [Strashes the BLIF-MV netlist.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Abc_StringGetNumber( char ** ppStr )
{
    char * pStr = *ppStr;
    int Number = 0;
    assert( *pStr >= '0' && *pStr <= '9' );
    for ( ; *pStr >= '0' && *pStr <= '9'; pStr++ )
        Number = 10 * Number + *pStr - '0';
    *ppStr = pStr;
    return Number;
}

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

  Synopsis    [Strashes one node in the BLIF-MV netlist.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeStrashBlifMv( Abc_Ntk_t * pNtkNew, Abc_Obj_t * pObj )
{
    char * pSop;
    Abc_Obj_t ** pValues, ** pValuesF, ** pValuesF2;
    Abc_Obj_t * pTemp, * pTemp2, * pFanin, * pFanin2, * pNet;
    int k, v, Def, DefIndex, Index, nValues, nValuesF, nValuesF2;

    // start the output values
    assert( Abc_ObjIsNode(pObj) );
    pNet = Abc_ObjFanout0(pObj);
    nValues = Abc_ObjMvVarNum(pNet);
    pValues = ALLOC( Abc_Obj_t *, nValues );
    for ( k = 0; k < nValues; k++ )
        pValues[k] = Abc_ObjNot( Abc_AigConst1(pNtkNew) );

    // get the BLIF-MV formula
    pSop = pObj->pData;
    // skip the value line
//    while ( *pSop++ != '\n' );

    // handle the constant
    if ( Abc_ObjFaninNum(pObj) == 0 )
    {
        // skip the default if present
        if ( *pSop == 'd' )
            while ( *pSop++ != '\n' );
        // skip space if present
        if ( *pSop == ' ' )
            pSop++;
        Index = Abc_StringGetNumber( &pSop );
        assert( Index < nValues );
        pValues[Index] = Abc_AigConst1(pNtkNew);
        // save the values in the fanout net
        pNet->pCopy = (Abc_Obj_t *)pValues;
        return 1;
    }

    // parse the default line
    Def = DefIndex = -1;
    if ( *pSop == 'd' )
    {
        pSop++;
        if ( *pSop == '=' )
        {
            pSop++;
            DefIndex = Abc_StringGetNumber( &pSop );
            assert( DefIndex < Abc_ObjFaninNum(pObj) );
        }
        else if ( *pSop == '-' )
        {
            pSop++;
            Def = 0;
        }
        else
        {
            Def = Abc_StringGetNumber( &pSop );
            assert( Def < nValues );
        }
        assert( *pSop == '\n' );
        pSop++;
    }

    // convert the values
    while ( *pSop )
    {
        // extract the values for each cube
        pTemp = Abc_AigConst1(pNtkNew); 
        Abc_ObjForEachFanin( pObj, pFanin, k )
        {
            if ( *pSop == '-' )
            {
                pSop += 2;
                continue;
            }
            if ( *pSop == '!' )
            {
                fprintf(outStream,  "Abc_NodeStrashBlifMv(): Cannot handle complement in the MV function of node %s.\n", Abc_ObjName(Abc_ObjFanout0(pObj)) );
                return 0;
            }
            if ( *pSop == '{' )
            {
                fprintf(outStream,  "Abc_NodeStrashBlifMv(): Cannot handle braces in the MV function of node %s.\n", Abc_ObjName(Abc_ObjFanout0(pObj)) );
                return 0;
            }
            // get the value set
            nValuesF = Abc_ObjMvVarNum(pFanin);
            pValuesF = (Abc_Obj_t **)pFanin->pCopy;
            if ( *pSop == '(' )
            {
                pSop++;
                pTemp2 = Abc_ObjNot( Abc_AigConst1(pNtkNew) );
                while ( *pSop != ')' )
                {
                    Index = Abc_StringGetNumber( &pSop );
                    assert( Index < nValuesF );
                    pTemp2 = Abc_AigOr( pNtkNew->pManFunc, pTemp2, pValuesF[Index] );
                    assert( *pSop == ')' || *pSop == ',' );
                    if ( *pSop == ',' )
                        pSop++;
                }
                assert( *pSop == ')' );
                pSop++;
            }
            else if ( *pSop == '=' )
            {
                pSop++;
                // get the fanin index
                Index = Abc_StringGetNumber( &pSop );
                assert( Index < Abc_ObjFaninNum(pObj) );
                assert( Index != k );
                // get the fanin
                pFanin2 = Abc_ObjFanin( pObj, Index );
                nValuesF2 = Abc_ObjMvVarNum(pFanin2);
                pValuesF2 = (Abc_Obj_t **)pFanin2->pCopy;
                // create the sum of products of values
                assert( nValuesF == nValuesF2 );
                pTemp2 = Abc_ObjNot( Abc_AigConst1(pNtkNew) );
                for ( v = 0; v < nValues; v++ )
                    pTemp2 = Abc_AigOr( pNtkNew->pManFunc, pTemp2, Abc_AigAnd(pNtkNew->pManFunc, pValuesF[v], pValuesF2[v]) );
            }
            else
            {
                Index = Abc_StringGetNumber( &pSop );
                assert( Index < nValuesF );
                pTemp2 = pValuesF[Index];
            }
            // compute the compute
            pTemp = Abc_AigAnd( pNtkNew->pManFunc, pTemp, pTemp2 );
            // advance the reading point
            assert( *pSop == ' ' );
            pSop++;
        }
        // check if the output value is an equal construct
        if ( *pSop == '=' )
        {
            pSop++;
            // get the output value
            Index = Abc_StringGetNumber( &pSop );
            assert( Index < Abc_ObjFaninNum(pObj) );
            // add values of the given fanin with the given cube
            pFanin = Abc_ObjFanin( pObj, Index );
            nValuesF = Abc_ObjMvVarNum(pFanin);
            pValuesF = (Abc_Obj_t **)pFanin->pCopy;
            assert( nValuesF == nValues ); // should be guaranteed by the parser
            for ( k = 0; k < nValuesF; k++ )
                pValues[k] = Abc_AigOr( pNtkNew->pManFunc, pValues[k], Abc_AigAnd(pNtkNew->pManFunc, pTemp, pValuesF[k]) );
        }
        else
        {
            // get the output value
            Index = Abc_StringGetNumber( &pSop );
            assert( Index < nValues );
            pValues[Index] = Abc_AigOr( pNtkNew->pManFunc, pValues[Index], pTemp );
        }
        // advance the reading point
        assert( *pSop == '\n' );
        pSop++;
    }

    // compute the default value
    if ( Def >= 0 || DefIndex >= 0 )
    {
        pTemp = Abc_AigConst1(pNtkNew);
        for ( k = 0; k < nValues; k++ )
        {
            if ( k == Def )
                continue;
            pTemp = Abc_AigAnd( pNtkNew->pManFunc, pTemp, Abc_ObjNot(pValues[k]) );
        }

        // assign the default value
        if ( Def >= 0 )
            pValues[Def] = pTemp;
        else
        {
            assert( DefIndex >= 0 );
            // add values of the given fanin with the given cube
            pFanin = Abc_ObjFanin( pObj, DefIndex );
            nValuesF = Abc_ObjMvVarNum(pFanin);
            pValuesF = (Abc_Obj_t **)pFanin->pCopy;
            assert( nValuesF == nValues ); // should be guaranteed by the parser
            for ( k = 0; k < nValuesF; k++ )
                pValues[k] = Abc_AigOr( pNtkNew->pManFunc, pValues[k], Abc_AigAnd(pNtkNew->pManFunc, pTemp, pValuesF[k]) );
        }

    }

    // save the values in the fanout net
    pNet->pCopy = (Abc_Obj_t *)pValues;
    return 1;
}

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

  Synopsis    [Assigns name with index.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Abc_NtkConvertAssignName( Abc_Obj_t * pObj, Abc_Obj_t * pNet, int Index )
{
    char Suffix[16];
    assert( Abc_ObjIsTerm(pObj) );
    assert( Abc_ObjIsNet(pNet) );
    sprintf( Suffix, "[%d]", Index );
    Abc_ObjAssignName( pObj, Abc_ObjName(pNet), Suffix );
}

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

  Synopsis    [Strashes the BLIF-MV netlist.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Ntk_t * Abc_NtkStrashBlifMv( Abc_Ntk_t * pNtk )
{
    int fUsePositional = 0;
    Vec_Ptr_t * vNodes;
    Abc_Obj_t ** pBits;
    Abc_Obj_t ** pValues;
    Abc_Ntk_t * pNtkNew;
    Abc_Obj_t * pObj, * pTemp, * pBit, * pNet;
    int i, k, v, nValues, nValuesMax, nBits;

    assert( Abc_NtkIsNetlist(pNtk) );
    assert( Abc_NtkHasBlifMv(pNtk) );
    assert( Abc_NtkWhiteboxNum(pNtk) == 0 );
    assert( Abc_NtkBlackboxNum(pNtk) == 0 );

    // get the largest number of values
    nValuesMax = 2;
    Abc_NtkForEachNet( pNtk, pObj, i )
    {
        nValues = Abc_ObjMvVarNum(pObj);
        if ( nValuesMax < nValues )
            nValuesMax = nValues;
    }
    nBits = Extra_Base2Log( nValuesMax );
    pBits = ALLOC( Abc_Obj_t *, nBits );

    // clean the node copy fields
    Abc_NtkCleanCopy( pNtk );
    // collect the nodes
    vNodes = Abc_NtkDfs( pNtk, 0 );

    // start the network
    pNtkNew = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 );
    // duplicate the name and the spec
    pNtkNew->pName = Extra_UtilStrsav( pNtk->pName );
//    pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pName );

    // encode the CI nets
    Abc_NtkIncrementTravId( pNtk );
    if ( fUsePositional )
    {
        Abc_NtkForEachCi( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanout0(pObj);
            nValues = Abc_ObjMvVarNum(pNet);
            pValues = ALLOC( Abc_Obj_t *, nValues );
            // create PIs for the values
            for ( v = 0; v < nValues; v++ )
            {
                pValues[v] = Abc_NtkCreatePi( pNtkNew );
                Abc_NtkConvertAssignName( pValues[v], pNet, v );
            }
            // save the values in the fanout net
            pNet->pCopy = (Abc_Obj_t *)pValues;
            // mark the net
            Abc_NodeSetTravIdCurrent( pNet );
        }
    }
    else
    {
        Abc_NtkForEachCi( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanout0(pObj);
            nValues = Abc_ObjMvVarNum(pNet);
            pValues = ALLOC( Abc_Obj_t *, nValues );
            // create PIs for the encoding bits
            nBits = Extra_Base2Log( nValues );
            for ( k = 0; k < nBits; k++ )
            {
                pBits[k] = Abc_NtkCreatePi( pNtkNew );
                Abc_NtkConvertAssignName( pBits[k], pNet, k );
            }
            // encode the values
            for ( v = 0; v < nValues; v++ )
            {
                pValues[v] = Abc_AigConst1(pNtkNew);
                for ( k = 0; k < nBits; k++ )
                {
                    pBit = Abc_ObjNotCond( pBits[k], (v&(1<<k)) == 0 );
                    pValues[v] = Abc_AigAnd( pNtkNew->pManFunc, pValues[v], pBit );
                }
            }
            // save the values in the fanout net
            pNet->pCopy = (Abc_Obj_t *)pValues;
            // mark the net
            Abc_NodeSetTravIdCurrent( pNet );
        }
    }

    // process nodes in the topological order
    Vec_PtrForEachEntry( vNodes, pObj, i )
        if ( !Abc_NodeStrashBlifMv( pNtkNew, pObj ) )
        {
            Abc_NtkDelete( pNtkNew );
            return NULL;
        }
    Vec_PtrFree( vNodes );

    // encode the CO nets
    if ( fUsePositional )
    {
        Abc_NtkForEachCo( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanin0(pObj);
            // skip marked nets
            if ( Abc_NodeIsTravIdCurrent(pNet) )
                continue;
            Abc_NodeSetTravIdCurrent( pNet );
            nValues = Abc_ObjMvVarNum(pNet);
            pValues = (Abc_Obj_t **)pNet->pCopy;
            for ( v = 0; v < nValues; v++ )
            {
                pTemp = Abc_NtkCreatePo( pNtkNew );
                Abc_ObjAddFanin( pTemp, pValues[v] );
                Abc_NtkConvertAssignName( pTemp, pNet, v );
            }
        }
    }
    else
    {
        Abc_NtkForEachCo( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanin0(pObj);
            // skip marked nets
            if ( Abc_NodeIsTravIdCurrent(pNet) )
                continue;
            Abc_NodeSetTravIdCurrent( pNet );
            nValues = Abc_ObjMvVarNum(pNet);
            pValues = (Abc_Obj_t **)pNet->pCopy;
            nBits = Extra_Base2Log( nValues );
            for ( k = 0; k < nBits; k++ )
            {
                pBit = Abc_ObjNot( Abc_AigConst1(pNtkNew) );
                for ( v = 0; v < nValues; v++ )
                    if ( v & (1<<k) )
                        pBit = Abc_AigOr( pNtkNew->pManFunc, pBit, pValues[v] );
                pTemp = Abc_NtkCreatePo( pNtkNew );
                Abc_ObjAddFanin( pTemp, pBit );
                Abc_NtkConvertAssignName( pTemp, pNet, k );
            }
        }
    }

    // cleanup
    free( pBits );
    Abc_NtkForEachObj( pNtk, pObj, i )
        if ( pObj->pCopy )
            free( pObj->pCopy );

    // remove dangling nodes
    i = Abc_AigCleanup(pNtkNew->pManFunc);
//    fprintf(outStream,  "Cleanup removed %d nodes.\n", i );
//    Abc_NtkReassignIds( pNtkNew );

    // check integrity
    if ( !Abc_NtkCheck( pNtkNew ) )
    {
        fprintf( outStream, "Abc_NtkStrashBlifMv(): Network check has failed.\n" );
        Abc_NtkDelete( pNtkNew );
        return NULL;
    }
    return pNtkNew;
}

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

  Synopsis    [Extract the MV-skeleton of the BLIF-MV network.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Ntk_t * Abc_NtkSkeletonBlifMv( Abc_Ntk_t * pNtk )
{
    int fUsePositional = 0;
    Abc_Ntk_t * pNtkNew;
    Abc_Obj_t * pObj, * pNet, * pNetNew, * pNodeNew, * pTermNew, * pBoxNew;
    int i, k, v, nValues, nBits;

    assert( Abc_NtkIsNetlist(pNtk) );
    assert( Abc_NtkHasBlifMv(pNtk) );
    assert( Abc_NtkWhiteboxNum(pNtk) == 0 );
    assert( Abc_NtkBlackboxNum(pNtk) == 0 );

    // clean the node copy fields
    Abc_NtkCleanCopy( pNtk );

    // start the network
    pNtkNew = Abc_NtkAlloc( pNtk->ntkType, pNtk->ntkFunc, 1 );
    // duplicate the name and the spec
    pNtkNew->pName = Extra_UtilStrsav( pNtk->pName );
    pNtkNew->pSpec = Extra_UtilStrsav( pNtk->pName );
    // create the internal box (it is important to put it first!)
    pBoxNew = Abc_NtkCreateWhitebox( pNtkNew );
    // create PIs and their nets
    Abc_NtkForEachPi( pNtk, pObj, i )
    {
        Abc_NtkDupObj( pNtkNew, pObj, 0 );
        pNet = Abc_ObjFanout0(pObj);
        Abc_NtkDupObj( pNtkNew, pNet, 1 );
        Abc_ObjAddFanin( pNet->pCopy, pObj->pCopy );
    }
    // create POs and their nets
    Abc_NtkForEachPo( pNtk, pObj, i )
    {
        Abc_NtkDupObj( pNtkNew, pObj, 0 );
        pNet = Abc_ObjFanin0(pObj);
        if ( pNet->pCopy == NULL )
            Abc_NtkDupObj( pNtkNew, pNet, 1 );
        Abc_ObjAddFanin( pObj->pCopy, pNet->pCopy );
    }
    // create latches
    Abc_NtkForEachLatch( pNtk, pObj, i )
    {
        Abc_NtkDupBox( pNtkNew, pObj, 0 );
        // latch outputs
        pNet = Abc_ObjFanout0(Abc_ObjFanout0(pObj));
        assert( pNet->pCopy == NULL );
        Abc_NtkDupObj( pNtkNew, pNet, 1 );
        Abc_ObjAddFanin( pNet->pCopy, Abc_ObjFanout0(pObj)->pCopy );
        // latch inputs
        pNet = Abc_ObjFanin0(Abc_ObjFanin0(pObj));
        if ( pNet->pCopy == NULL )
            Abc_NtkDupObj( pNtkNew, pNet, 1 );
        Abc_ObjAddFanin( Abc_ObjFanin0(pObj)->pCopy, pNet->pCopy );
    }

    // encode the CI nets
    Abc_NtkIncrementTravId( pNtk );
    if ( fUsePositional )
    {
        Abc_NtkForEachCi( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanout0(pObj);
            nValues = Abc_ObjMvVarNum(pNet);
            for ( v = 0; v < nValues; v++ )
            {
                pNodeNew = Abc_NtkCreateNode( pNtkNew );
                pNodeNew->pData = Abc_SopEncoderPos( pNtkNew->pManFunc, v, nValues );
                pNetNew = Abc_NtkCreateNet( pNtkNew );
                pTermNew = Abc_NtkCreateBi( pNtkNew );
                Abc_ObjAddFanin( pNodeNew, pNet->pCopy );
                Abc_ObjAddFanin( pNetNew, pNodeNew );
                Abc_ObjAddFanin( pTermNew, pNetNew );
                Abc_ObjAddFanin( pBoxNew, pTermNew );
            }
            // mark the net
            Abc_NodeSetTravIdCurrent( pNet );
        }
    }
    else
    {
        Abc_NtkForEachCi( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanout0(pObj);
            nValues = Abc_ObjMvVarNum(pNet);
            nBits = Extra_Base2Log( nValues );
            for ( k = 0; k < nBits; k++ )
            {
                pNodeNew = Abc_NtkCreateNode( pNtkNew );
                pNodeNew->pData = Abc_SopEncoderLog( pNtkNew->pManFunc, k, nValues );
                pNetNew = Abc_NtkCreateNet( pNtkNew );
                pTermNew = Abc_NtkCreateBi( pNtkNew );
                Abc_ObjAddFanin( pNodeNew, pNet->pCopy );
                Abc_ObjAddFanin( pNetNew, pNodeNew );
                Abc_ObjAddFanin( pTermNew, pNetNew );
                Abc_ObjAddFanin( pBoxNew, pTermNew );
            }
            // mark the net
            Abc_NodeSetTravIdCurrent( pNet );
        }
    }

    // encode the CO nets
    if ( fUsePositional )
    {
        Abc_NtkForEachCo( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanin0(pObj);
            // skip marked nets
            if ( Abc_NodeIsTravIdCurrent(pNet) )
                continue;
            Abc_NodeSetTravIdCurrent( pNet );
            nValues = Abc_ObjMvVarNum(pNet);
            pNodeNew = Abc_NtkCreateNode( pNtkNew );
            pNodeNew->pData = Abc_SopDecoderPos( pNtkNew->pManFunc, nValues );
            for ( v = 0; v < nValues; v++ )
            {
                pTermNew = Abc_NtkCreateBo( pNtkNew );
                pNetNew = Abc_NtkCreateNet( pNtkNew );
                Abc_ObjAddFanin( pTermNew, pBoxNew );
                Abc_ObjAddFanin( pNetNew, pTermNew );
                Abc_ObjAddFanin( pNodeNew, pNetNew );
            }
            Abc_ObjAddFanin( pNet->pCopy, pNodeNew );
        }
    }
    else
    {
        Abc_NtkForEachCo( pNtk, pObj, i )
        {
            pNet = Abc_ObjFanin0(pObj);
            // skip marked nets
            if ( Abc_NodeIsTravIdCurrent(pNet) )
                continue;
            Abc_NodeSetTravIdCurrent( pNet );
            nValues = Abc_ObjMvVarNum(pNet);
            nBits = Extra_Base2Log( nValues );
            pNodeNew = Abc_NtkCreateNode( pNtkNew );
            pNodeNew->pData = Abc_SopDecoderLog( pNtkNew->pManFunc, nValues );
            for ( k = 0; k < nBits; k++ )
            {
                pTermNew = Abc_NtkCreateBo( pNtkNew );
                pNetNew = Abc_NtkCreateNet( pNtkNew );
                Abc_ObjAddFanin( pTermNew, pBoxNew );
                Abc_ObjAddFanin( pNetNew, pTermNew );
                Abc_ObjAddFanin( pNodeNew, pNetNew );
            }
            Abc_ObjAddFanin( pNet->pCopy, pNodeNew );
        }
    }

    // if it is a BLIF-MV netlist transfer the values of all nets
    if ( Abc_NtkHasBlifMv(pNtk) && Abc_NtkMvVar(pNtk) )
    {
        if ( Abc_NtkMvVar( pNtkNew ) == NULL )
            Abc_NtkStartMvVars( pNtkNew );
        Abc_NtkForEachNet( pNtk, pObj, i )
            if ( pObj->pCopy )
                Abc_NtkSetMvVarValues( pObj->pCopy, Abc_ObjMvVarNum(pObj) );
    }

    // check integrity
    if ( !Abc_NtkCheck( pNtkNew ) )
    {
        fprintf( outStream, "Abc_NtkSkeletonBlifMv(): Network check has failed.\n" );
        Abc_NtkDelete( pNtkNew );
        return NULL;
    }
    return pNtkNew;
}

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

  Synopsis    [Inserts processed network into original base MV network.]

  Description [The original network remembers the interface of combinational 
  logic (PIs/POs/latches names and values). The processed network may 
  be binary or multi-valued (currently, multi-value is not supported). 
  The resulting network has the same interface as the original network 
  while the internal logic is the same as that of the processed network.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_Ntk_t * Abc_NtkInsertBlifMv( Abc_Ntk_t * pNtkBase, Abc_Ntk_t * pNtkLogic )
{
    Abc_Ntk_t * pNtkSkel, * pNtkNew;
    Abc_Obj_t * pBox;

    assert( Abc_NtkIsNetlist(pNtkBase) );
    assert( Abc_NtkHasBlifMv(pNtkBase) );
    assert( Abc_NtkWhiteboxNum(pNtkBase) == 0 );
    assert( Abc_NtkBlackboxNum(pNtkBase) == 0 );

    assert( Abc_NtkIsNetlist(pNtkLogic) );
    assert( Abc_NtkHasBlifMv(pNtkLogic) );
    assert( Abc_NtkWhiteboxNum(pNtkLogic) == 0 );
    assert( Abc_NtkBlackboxNum(pNtkLogic) == 0 );

    // extract the skeleton of the old network
    pNtkSkel = Abc_NtkSkeletonBlifMv( pNtkBase );

    // set the implementation of the box to be the same as the processed network
    assert( Abc_NtkWhiteboxNum(pNtkSkel) == 1 );
    pBox = Abc_NtkBox( pNtkSkel, 0 );
    assert( Abc_ObjIsWhitebox(pBox) );
    assert( pBox->pData == NULL );
    assert( Abc_ObjFaninNum(pBox) == Abc_NtkPiNum(pNtkLogic) );
    assert( Abc_ObjFanoutNum(pBox) == Abc_NtkPoNum(pNtkLogic) );
    pBox->pData = pNtkLogic;

    // flatten the hierarchy to insert the processed network
    pNtkNew = Abc_NtkFlattenLogicHierarchy( pNtkSkel );
    pBox->pData = NULL;
    Abc_NtkDelete( pNtkSkel );
    return pNtkNew;    
}

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

  Synopsis    [Converts SOP netlist into BLIF-MV netlist.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkConvertToBlifMv( Abc_Ntk_t * pNtk )
{
    Extra_MmFlex_t * pMmFlex;
    Abc_Obj_t * pNode;
    Vec_Str_t * vCube;
    char * pSop0, * pSop1, * pBlifMv, * pCube, * pCur;
    int Value, nCubes, nSize, i, k;

    assert( Abc_NtkIsNetlist(pNtk) );
    if ( !Abc_NtkToBdd(pNtk) )
    {
        fprintf(outStream,  "Converting logic functions to BDDs has failed.\n" );
        return 0;
    }

    pMmFlex = Extra_MmFlexStart();
    vCube   = Vec_StrAlloc( 100 );
    Abc_NtkForEachNode( pNtk, pNode, i )
    {
        // convert BDD into cubes for on-set and off-set
        Abc_NodeBddToCnf( pNode, pMmFlex, vCube, 0, &pSop0, &pSop1 );
        // allocate room for the MV-SOP
        nCubes = Abc_SopGetCubeNum(pSop0) + Abc_SopGetCubeNum(pSop1);
        nSize = nCubes*(2*Abc_ObjFaninNum(pNode) + 2)+1;
        pBlifMv = Extra_MmFlexEntryFetch( pMmFlex, nSize );
        // add the cubes
        pCur = pBlifMv;
        Abc_SopForEachCube( pSop0, Abc_ObjFaninNum(pNode), pCube )
        {
            Abc_CubeForEachVar( pCube, Value, k )
            {
                *pCur++ = Value;
                *pCur++ = ' ';
            }
            *pCur++ = '0';
            *pCur++ = '\n';
        }
        Abc_SopForEachCube( pSop1, Abc_ObjFaninNum(pNode), pCube )
        {
            Abc_CubeForEachVar( pCube, Value, k )
            {
                *pCur++ = Value;
                *pCur++ = ' ';
            }
            *pCur++ = '1';
            *pCur++ = '\n';
        }
        *pCur++ = 0;
        assert( pCur - pBlifMv == nSize );
        // update the node representation
        Cudd_RecursiveDeref( pNtk->pManFunc, pNode->pData );
        pNode->pData = pBlifMv;
    }

    // update the functionality type
    pNtk->ntkFunc = ABC_FUNC_BLIFMV;
    Cudd_Quit( pNtk->pManFunc );
    pNtk->pManFunc = pMmFlex;

    Vec_StrFree( vCube );
    return 1;
}

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

  Synopsis    [Converts SOP into MV-SOP.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
char * Abc_NodeConvertSopToMvSop( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 )
{
    char * pMvSop, * pCur;
    unsigned uCube;
    int nCubes, nSize, Value, i, k;
    // consider the case of the constant node
    if ( Vec_IntSize(vSop0) == 0 || Vec_IntSize(vSop1) == 0 )
    {
        // (temporary) create a tautology cube
        pMvSop = ALLOC( char, nVars + 3 );
        for ( k = 0; k < nVars; k++ )
            pMvSop[k] = '-';
        pMvSop[nVars] = '0' + (int)(Vec_IntSize(vSop1) > 0);
        pMvSop[nVars+1] = '\n';
        pMvSop[nVars+2] = 0;
        return pMvSop;
    }
    // find the total number of cubes
    nCubes = Vec_IntSize(vSop0) + Vec_IntSize(vSop1);
    // find the size of the MVSOP represented as a C-string
    // (each cube has nVars variables + one output literal + end-of-line,
    // and the string is zero-terminated)
    nSize = nCubes * (nVars + 2) + 1; 
    // allocate memory
    pMvSop = pCur = ALLOC( char, nSize );
    // fill in the negative polarity cubes
    Vec_IntForEachEntry( vSop0, uCube, i )
    {
        for ( k = 0; k < nVars; k++ )
        {
            Value = (uCube >> (2*k)) & 3;
            if ( Value == 1 )
                *pCur++ = '0';
            else if ( Value == 2 )
                *pCur++ = '1';
            else if ( Value == 0 )
                *pCur++ = '-';
            else
                assert( 0 );
        }
        *pCur++ = '0';
        *pCur++ = '\n';
    }
    // fill in the positive polarity cubes
    Vec_IntForEachEntry( vSop1, uCube, i )
    {
        for ( k = 0; k < nVars; k++ )
        {
            Value = (uCube >> (2*k)) & 3;
            if ( Value == 1 )
                *pCur++ = '0';
            else if ( Value == 2 )
                *pCur++ = '1';
            else if ( Value == 0 )
                *pCur++ = '-';
            else
                assert( 0 );
        }
        *pCur++ = '1';
        *pCur++ = '\n';
    }
    *pCur++ = 0;
    assert( pCur - pMvSop == nSize );
    return pMvSop;
}


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

  Synopsis    [A prototype of internal cost evaluation procedure.]

  Description [This procedure takes the number of variables (nVars),
  the array of values of the inputs and the output (pVarValues) 
  (note that this array has nVars+1 entries), and an MV-SOP represented
  as a C-string with one charater for each literal, including inputs
  and output. Each cube is terminated with the new-line character ('\n').
  The string is zero-terminated.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeEvalMvCostInternal( int nVars, int * pVarValues, char * pMvSop ) 
{
    // for now, return the number of cubes in the MV-SOP
    int Counter = 0;
    while ( *pMvSop ) Counter += (*pMvSop++ == '\n');
    return Counter;
}


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

  Synopsis    [Evaluates the cost of the cut.]

  Description [The Boolean function of the cut is specified by two SOPs, 
  which represent the negative/positive polarities of the cut function.
  Converts these two SOPs into a mutually-agreed-upon representation 
  to be passed to the internal cost-evaluation procedure (see the above
  prototype Abc_NodeEvalMvCostInternal).]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeEvalMvCost( int nVars, Vec_Int_t * vSop0, Vec_Int_t * vSop1 ) 
{
    char * pMvSop;
    int * pVarValues;
    int i, RetValue;
    // collect the input and output values (currently, they are binary)
    pVarValues = ALLOC( int, nVars + 1 );
    for ( i = 0; i <= nVars; i++ )
        pVarValues[i] = 2;
    // prepare MV-SOP for evaluation
    pMvSop = Abc_NodeConvertSopToMvSop( nVars, vSop0, vSop1 );
    // have a look at the MV-SOP:
//    fprintf(outStream,  "%s\n", pMvSop );
    // get the result of internal cost evaluation
    RetValue = Abc_NodeEvalMvCostInternal( nVars, pVarValues, pMvSop );
    // cleanup
    free( pVarValues );
    free( pMvSop );
    return RetValue;
}

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


