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

  FileName    [wlcNtk.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Verilog parser.]

  Synopsis    [Network data-structure.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - August 22, 2014.]

  Revision    [$Id: wlcNtk.c,v 1.00 2014/09/12 00:00:00 alanmi Exp $]

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

#include <math.h>
#include "wlc.h"
#include "misc/vec/vecWec.h"

ABC_NAMESPACE_IMPL_START


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

// object types
static char * Wlc_Names[WLC_OBJ_NUMBER+1] = { 
    NULL,                  // 00: unknown
    "pi",                  // 01: primary input 
    "po",                  // 02: primary output (unused)
    "ff",                  // 03: flop output
    "bi",                  // 04: flop input (unused)
    "ff",                  // 05: flop (unused)
    "const",               // 06: constant
    "buf",                 // 07: buffer
    "mux",                 // 08: multiplexer
    ">>",                  // 09: shift right
    ">>>",                 // 10: shift right (arithmetic)
    "<<",                  // 11: shift left
    "<<<",                 // 12: shift left (arithmetic)
    "rotR",                // 13: rotate right
    "rotL",                // 14: rotate left
    "~",                   // 15: bitwise NOT
    "&",                   // 16: bitwise AND
    "|",                   // 17: bitwise OR
    "^",                   // 18: bitwise XOR
    "~&",                  // 19: bitwise NAND
    "~|",                  // 20: bitwise NOR
    "~^",                  // 21: bitwise NXOR
    "[:]",                 // 22: bit selection
    "{,}",                 // 23: bit concatenation
    "zPad",                // 24: zero padding
    "sExt",                // 25: sign extension
    "!",                   // 26: logic NOT
    "=>",                  // 27: logic implication
    "&&",                  // 28: logic AND
    "||",                  // 29: logic OR
    "^^",                  // 30: logic XOR
    "==",                  // 31: compare equal
    "!=",                  // 32: compare not equal
    "<",                   // 33: compare less
    ">",                   // 34: compare more
    "<=",                  // 35: compare less or equal
    ">=",                  // 36: compare more or equal
    "&",                   // 37: reduction AND
    "|",                   // 38: reduction OR
    "^",                   // 39: reduction XOR
    "~&",                  // 40: reduction NAND
    "~|",                  // 41: reduction NOR
    "~^",                  // 42: reduction NXOR
    "+",                   // 43: arithmetic addition
    "-",                   // 44: arithmetic subtraction
    "*",                   // 45: arithmetic multiplier
    "/",                   // 46: arithmetic division
    "%",                   // 47: arithmetic reminder
    "mod",                 // 48: arithmetic modulus
    "**",                  // 49: arithmetic power
    "-",                   // 50: arithmetic minus
    "sqrt",                // 51: integer square root
    "squar",               // 52: integer square
    "table",               // 53: bit table
    "READ",                // 54: mem read port
    "WRITE",               // 55: mem write port
    "addsub",              // 56: adder/subtractor
    "sel",                 // 57: selector
    "dec",                 // 58: decoder
    "LUT",                 // 59: lookup table
    NULL                   // 58: unused
};

char * Wlc_ObjTypeName( Wlc_Obj_t * p ) { return p ? (p->Type < WLC_OBJ_NUMBER ? Wlc_Names[p->Type] : (char *)"out_of_bound") : (char *)"no_obj"; }

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_ManSetDefaultParams( Wlc_Par_t * pPars )
{
    memset( pPars, 0, sizeof(Wlc_Par_t) );
    pPars->nBitsAdd      = ABC_INFINITY;   // adder bit-width
    pPars->nBitsMul      = ABC_INFINITY;   // multiplier bit-widht 
    pPars->nBitsMux      = ABC_INFINITY;   // MUX bit-width
    pPars->nBitsFlop     = ABC_INFINITY;   // flop bit-width
    pPars->nIterMax      =         1000;   // the max number of iterations
    pPars->nLimit        = ABC_INFINITY;   // the max number of signals
    pPars->fXorOutput    =            1;   // XOR outputs of word-level miter
    pPars->fCheckClauses =            1;   // Check clauses in the reloaded trace                    
    pPars->fPushClauses  =            0;   // Push clauses in the reloaded trace                    
    pPars->fMFFC         =            1;   // Refine the entire MFFC of a PPI 
    pPars->fPdra         =            0;   // Use pdr -nct 
    pPars->fLoadTrace    =            1;   // Load previous PDR traces 
    pPars->fProofRefine  =            0;   // Use proof-based refinement
    pPars->fHybrid       =            1;   // Use a hybrid of CBR and PBR
    pPars->fCheckCombUnsat =          0;   // Check if ABS becomes comb. unsat
    pPars->fAbs2         =            0;   // Use UFAR style createAbs
    pPars->fProofUsePPI  =            0;   // Use PPI values in PBR
    pPars->fUseBmc3      =            0;   // Run BMC3 in parallel
    pPars->fShrinkAbs    =            0;   // Shrink Abs with BMC
    pPars->fShrinkScratch=            0;   // Restart pdr from scratch after shrinking
    pPars->fVerbose      =            0;   // verbose output`
    pPars->fPdrVerbose   =            0;   // show verbose PDR output
}

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

  Synopsis    [Working with models.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Wlc_Ntk_t * Wlc_NtkAlloc( char * pName, int nObjsAlloc )
{
    Wlc_Ntk_t * p;
    p = ABC_CALLOC( Wlc_Ntk_t, 1 );
    p->pName = pName ? Extra_FileNameGeneric( pName ) : NULL;
    Vec_IntGrow( &p->vPis, 111 );
    Vec_IntGrow( &p->vPos, 111 );
    Vec_IntGrow( &p->vCis, 111 );
    Vec_IntGrow( &p->vCos, 111 );
    Vec_IntGrow( &p->vFfs, 111 );
    p->pMemFanin = Mem_FlexStart();
    p->nObjsAlloc = nObjsAlloc;  
    p->pObjs = ABC_CALLOC( Wlc_Obj_t, p->nObjsAlloc );
    p->iObj = 1;
    return p;
}
void Wlc_ObjSetCi( Wlc_Ntk_t * p, Wlc_Obj_t * pObj )
{
    assert( Wlc_ObjIsCi(pObj) );
    assert( Wlc_ObjFaninNum(pObj) == 0 );
    if ( Wlc_NtkPiNum(p) == Wlc_NtkCiNum(p) || pObj->Type != WLC_OBJ_PI )
    {
        pObj->Fanins[1] = Vec_IntSize(&p->vCis);
        Vec_IntPush( &p->vCis, Wlc_ObjId(p, pObj) );
    }
    else // insert in the array of CI at the end of PIs
    {
        Wlc_Obj_t * pTemp; int i;
        Vec_IntInsert( &p->vCis, Wlc_NtkPiNum(p), Wlc_ObjId(p, pObj) );
        // other CI IDs are invalidated... naive fix!
        Wlc_NtkForEachCi( p, pTemp, i )
            pTemp->Fanins[1] = i;
    }
    if ( pObj->Type == WLC_OBJ_PI )
        Vec_IntPush( &p->vPis, Wlc_ObjId(p, pObj) );
}
void Wlc_ObjSetCo( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, int fFlopInput )
{
//    pObj->Fanins[1] = Vec_IntSize(&p->vCos);
    Vec_IntPush( &p->vCos, Wlc_ObjId(p, pObj) );
    if ( !fFlopInput )
        Vec_IntPush( &p->vPos, Wlc_ObjId(p, pObj) );
    if ( fFlopInput )
        pObj->fIsFi = 1;
    else
        pObj->fIsPo = 1;
}
int Wlc_ObjAlloc( Wlc_Ntk_t * p, int Type, int Signed, int End, int Beg )
{
    Wlc_Obj_t * pObj;
    assert( Type != WLC_OBJ_PO && Type != WLC_OBJ_FI );
    if ( p->iObj == p->nObjsAlloc )
    {
        p->pObjs = ABC_REALLOC( Wlc_Obj_t, p->pObjs, 2 * p->nObjsAlloc );
        memset( p->pObjs + p->nObjsAlloc, 0, sizeof(Wlc_Obj_t) * p->nObjsAlloc );
        p->nObjsAlloc *= 2;
    }
    pObj = Wlc_NtkObj( p, p->iObj );
    pObj->Type   = Type;
    pObj->Signed = Signed;
    pObj->End    = End;
    pObj->Beg    = Beg;
    if ( Wlc_ObjIsCi(pObj) )
        Wlc_ObjSetCi( p, pObj );
    p->nObjs[Type]++;
    return p->iObj++;
}
int Wlc_ObjCreate( Wlc_Ntk_t * p, int Type, int Signed, int End, int Beg, Vec_Int_t * vFanins )
{
    int iFaninNew = Wlc_ObjAlloc( p, Type, Signed, End, Beg );
    Wlc_ObjAddFanins( p, Wlc_NtkObj(p, iFaninNew), vFanins );
    return iFaninNew;
}
char * Wlc_ObjName( Wlc_Ntk_t * p, int iObj )
{
    static char Buffer[100];
    if ( Wlc_NtkHasNameId(p) && Wlc_ObjNameId(p, iObj) )
        return Abc_NamStr( p->pManName, Wlc_ObjNameId(p, iObj) );
    sprintf( Buffer, "n%d", iObj );
    return Buffer;
}                          
void Wlc_ObjUpdateType( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, int Type )
{
    assert( pObj->Type == WLC_OBJ_NONE );
    p->nObjs[pObj->Type]--;
    pObj->Type = Type;
    p->nObjs[pObj->Type]++;
}
void Wlc_ObjAddFanins( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, Vec_Int_t * vFanins )
{
    assert( pObj->nFanins == 0 );
    pObj->nFanins = Vec_IntSize(vFanins);
    // special treatment of CONST, SELECT and TABLE
    if ( pObj->Type == WLC_OBJ_CONST )
        pObj->nFanins = 0;
    else if ( pObj->Type == WLC_OBJ_BIT_SELECT || pObj->Type == WLC_OBJ_TABLE )
        pObj->nFanins = 1;
    if ( Wlc_ObjHasArray(pObj) )
        pObj->pFanins[0] = (int *)Mem_FlexEntryFetch( p->pMemFanin, Vec_IntSize(vFanins) * sizeof(int) );
    memcpy( Wlc_ObjFanins(pObj), Vec_IntArray(vFanins), sizeof(int) * Vec_IntSize(vFanins) );
}
void Wlc_NtkFree( Wlc_Ntk_t * p )
{
    if ( p->pManName )
        Abc_NamStop( p->pManName );
    if ( p->pMemFanin )
        Mem_FlexStop( p->pMemFanin, 0 );
    if ( p->pMemTable )
        Mem_FlexStop( p->pMemTable, 0 );
    ABC_FREE( p->vPoPairs.pArray );
    Vec_PtrFreeP( &p->vTables );
    Vec_WrdFreeP( &p->vLutTruths );
    ABC_FREE( p->vPis.pArray );
    ABC_FREE( p->vPos.pArray );
    ABC_FREE( p->vCis.pArray );
    ABC_FREE( p->vCos.pArray );
    ABC_FREE( p->vFfs.pArray );
    ABC_FREE( p->vFfs2.pArray );
    Vec_IntFreeP( &p->vInits );
    Vec_IntFreeP( &p->vArsts );
    ABC_FREE( p->vTravIds.pArray );
    ABC_FREE( p->vNameIds.pArray );
    ABC_FREE( p->vValues.pArray );
    ABC_FREE( p->vCopies.pArray );
    ABC_FREE( p->vBits.pArray );
    ABC_FREE( p->vLevels.pArray );
    ABC_FREE( p->vRefs.pArray );
    ABC_FREE( p->pInits );
    ABC_FREE( p->pObjs );
    ABC_FREE( p->pName );
    ABC_FREE( p->pSpec );
    ABC_FREE( p );
}
int Wlc_NtkMemUsage( Wlc_Ntk_t * p )
{
    int Mem = sizeof(Wlc_Ntk_t);
    Mem += 4 * p->vPis.nCap;
    Mem += 4 * p->vPos.nCap;
    Mem += 4 * p->vCis.nCap;
    Mem += 4 * p->vCos.nCap;
    Mem += 4 * p->vFfs.nCap;
    Mem += 4 * p->vFfs2.nCap;
    Mem += sizeof(Wlc_Obj_t) * p->nObjsAlloc;
    Mem += Abc_NamMemUsed(p->pManName);
    Mem += Mem_FlexReadMemUsage(p->pMemFanin);
    return Mem;
}

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

  Synopsis    [Assigns object levels.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Wlc_NtkCreateLevels_( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj; 
    int i, k, iFanin, Level, LevelMax = 0;
    Vec_IntFill( &p->vLevels, Wlc_NtkObjNumMax(p), 0 );
    Wlc_NtkForEachObj( p, pObj, i )
    {
        Level = 0;
        Wlc_ObjForEachFanin( pObj, iFanin, k )
            Level = Abc_MaxInt( Level, Wlc_ObjLevelId(p, iFanin) + 1 );
        Vec_IntWriteEntry( &p->vLevels, i, Level );
        LevelMax = Abc_MaxInt( LevelMax, Level );
    }
    return LevelMax;
}
int Wlc_NtkCreateLevelsRev( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj; 
    int i, k, iFanin, Level, LevelMax = 0;
    Vec_IntFill( &p->vLevels, Wlc_NtkObjNumMax(p), 0 );
    Wlc_NtkForEachObjReverse( p, pObj, i )
    {
        if ( Wlc_ObjIsCi(pObj) )
            continue;
        Level = Wlc_ObjLevel(p, pObj) + 1;
        Wlc_ObjForEachFanin( pObj, iFanin, k )
            Vec_IntUpdateEntry( &p->vLevels, iFanin, Level );
        LevelMax = Abc_MaxInt( LevelMax, Level );
    }
    // reverse the values
    Wlc_NtkForEachObj( p, pObj, i )
        Vec_IntWriteEntry( &p->vLevels, i, LevelMax - Wlc_ObjLevelId(p, i) );
    Wlc_NtkForEachCi( p, pObj, i )
        Vec_IntWriteEntry( &p->vLevels, Wlc_ObjId(p, pObj), 0 );
    //Wlc_NtkForEachObj( p, pObj, i )
    //    printf( "%d -> %d\n", i, Wlc_ObjLevelId(p, i) );
    return LevelMax;
}

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

  Synopsis    [Assigns object levels.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkCreateLevels_rec( Wlc_Ntk_t * p, Wlc_Obj_t * pObj )
{
    int k, iFanin, Level = 0, iObj = Wlc_ObjId(p, pObj);
    if ( Wlc_ObjIsCi(pObj) || Wlc_ObjIsFf(p, iObj) || Wlc_ObjFaninNum(pObj) == 0 || Wlc_ObjLevel(p, pObj) > 0 )
        return;
    Wlc_ObjForEachFanin( pObj, iFanin, k ) if ( iFanin )
        Wlc_NtkCreateLevels_rec( p, Wlc_NtkObj(p, iFanin) );
    Wlc_ObjForEachFanin( pObj, iFanin, k ) if ( iFanin )
        Level = Abc_MaxInt( Level, Wlc_ObjLevelId(p, iFanin) );
    Vec_IntWriteEntry( &p->vLevels, Wlc_ObjId(p, pObj), Level + 1 );
}
int Wlc_NtkCreateLevels( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj;  int i, LeveMax = 0;
    Vec_IntFill( &p->vLevels, Wlc_NtkObjNumMax(p), 0 );
    Wlc_NtkForEachObj( p, pObj, i )
        Wlc_NtkCreateLevels_rec( p, pObj );
    Wlc_NtkForEachObj( p, pObj, i )
        if ( !Wlc_ObjIsCi(pObj) && Wlc_ObjFaninNum(pObj) )
            Vec_IntAddToEntry( &p->vLevels, i, 1 );
    LeveMax = Vec_IntFindMax( &p->vLevels );
    Wlc_NtkForEachFf2( p, pObj, i )
        Vec_IntWriteEntry( &p->vLevels, Wlc_ObjId(p, pObj), LeveMax+1 );
    //Wlc_NtkPrintObjects( p ); 
    return LeveMax+1;
}
int Wlc_NtkRemapLevels( Wlc_Ntk_t * p, Vec_Int_t * vObjs, int nLevels )
{
    int i, k, iFanin, iObj, Entry, Level = 0, Res = nLevels;
    Vec_Int_t * vMap  = Vec_IntStart( nLevels+1 );
    Vec_Int_t * vUsed = Vec_IntStart( nLevels+1 );
    // mark used levels
    Vec_IntWriteEntry( vUsed, nLevels, 1 );
    Vec_IntForEachEntry( vObjs, iObj, i )
    {
        Vec_IntWriteEntry( vUsed, Wlc_ObjLevelId(p, iObj), 1 );
        Wlc_ObjForEachFanin( Wlc_NtkObj(p, iObj), iFanin, k ) if ( iFanin )
            Vec_IntWriteEntry( vUsed, Wlc_ObjLevelId(p, iFanin), 1 );
    }
    // create level map
    Vec_IntForEachEntry( vUsed, Entry, i )
        if ( Entry )
            Vec_IntWriteEntry( vMap, i, Level++ );
    //printf( "Total used levels %d -> %d\n", nLevels, Level );
    // remap levels
    Vec_IntForEachEntry( &p->vLevels, Level, i )
    {
        if ( Vec_IntEntry(vUsed, Level) )
            Vec_IntWriteEntry( &p->vLevels, i, Vec_IntEntry(vMap, Level) );
        else
            Vec_IntWriteEntry( &p->vLevels, i, -1 );
    }
    Res = Vec_IntEntry( vMap, nLevels );
    Vec_IntFree( vUsed );
    Vec_IntFree( vMap );
    return Res;
}

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

  Synopsis    [Collects statistics for each side of the miter.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkCollectStats( Wlc_Ntk_t * p, int nObjs[2][WLC_OBJ_NUMBER] )
{
    Wlc_Obj_t * pObj;
    int n, i;
    if ( Wlc_NtkPoNum(p) != 2 )
        return;
    for ( n = 0; n < 2; n++ )
    {
        Wlc_NtkMarkCone( p, n, 1, 1, 0 );
        Wlc_NtkForEachObj( p, pObj, i )
            if ( pObj->Mark )
                nObjs[n][pObj->Type]++;
    }
    Wlc_NtkCleanMarks( p );
}
int Wlc_NtkCountRealPis( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj;
    int i, Count = 0;
    Wlc_NtkMarkCone( p, -1, -1, 1, 0 );
    Wlc_NtkForEachPi( p, pObj, i )
        Count += pObj->Mark;
    Wlc_NtkCleanMarks( p );
    return Count;
}

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

  Synopsis    [Prints distribution of operator types.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Vec_WrdSelectSortCost2( word * pArray, int nSize, word * pCosts )
{
    int i, j, best_i;
    for ( i = 0; i < nSize-1; i++ )
    {
        best_i = i;
        for ( j = i+1; j < nSize; j++ )
            if ( pCosts[j] < pCosts[best_i] )
                best_i = j;
        ABC_SWAP( word, pArray[i], pArray[best_i] );
        ABC_SWAP( word, pCosts[i], pCosts[best_i] );
    }
}
static inline word Wlc_NtkPrintDistribMakeSign( int s, int s0, int s1 )
{
    return ((word)s1 << 42) | ((word)s0 << 21) | (word)s;
}
static inline void Wlc_NtkPrintDistribFromSign( word sss, int * s, int * s0, int * s1 )
{
    *s1 =  (int)(sss >> 42);  *s0 = (int)(sss >> 21) & 0x1FFFFF;  *s  =  (int)sss & 0x1FFFFF;
}
static inline void Wlc_NtkPrintDistribAddOne( Vec_Ptr_t * vTypes, Vec_Ptr_t * vOccurs, int Type, word Sign )
{
    Vec_Wrd_t * vType  = (Vec_Wrd_t *)Vec_PtrEntry( vTypes, Type );
    Vec_Wrd_t * vOccur = (Vec_Wrd_t *)Vec_PtrEntry( vOccurs, Type );
    word Entry; int i;
    Vec_WrdForEachEntry( vType, Entry, i )
        if ( Entry == Sign )
        {
            Vec_WrdAddToEntry( vOccur, i, 1 );
            return;
        }
    Vec_WrdPush( vType, Sign );
    Vec_WrdPush( vOccur, 1 );
}
void Wlc_NtkPrintDistribSortOne( Vec_Ptr_t * vTypes, Vec_Ptr_t * vOccurs, int Type )
{
    Vec_Wrd_t * vType  = (Vec_Wrd_t *)Vec_PtrEntry( vTypes, Type );
    Vec_Wrd_t * vOccur = (Vec_Wrd_t *)Vec_PtrEntry( vOccurs, Type );
    Vec_WrdSelectSortCost2( Vec_WrdArray(vType), Vec_WrdSize(vType), Vec_WrdArray(vOccur) );
    Vec_WrdReverseOrder( vType );
    Vec_WrdReverseOrder( vOccur );
}
void Wlc_NtkPrintDistrib( Wlc_Ntk_t * p, int fTwoSides, int fVerbose )
{
    int nObjs[2][WLC_OBJ_NUMBER] = {{0}}; // counter of objects of each type
    Wlc_Obj_t * pObj, * pObjRange = NULL; int nCountRange = 0;
    Vec_Ptr_t * vTypes, * vOccurs;
    Vec_Int_t * vAnds = Vec_IntStart( WLC_OBJ_NUMBER );
    word Sign = 0;
    int i, k, s, s0, s1;
    if ( Wlc_NtkPoNum(p) != 2 )
        fTwoSides = 0;
    if ( fTwoSides )
        Wlc_NtkCollectStats( p, nObjs );
    // allocate statistics arrays
    vTypes  = Vec_PtrStart( WLC_OBJ_NUMBER );
    vOccurs = Vec_PtrStart( WLC_OBJ_NUMBER );
    for ( i = 0; i < WLC_OBJ_NUMBER; i++ )
        Vec_PtrWriteEntry( vTypes, i, Vec_WrdAlloc(16) );
    for ( i = 0; i < WLC_OBJ_NUMBER; i++ )
        Vec_PtrWriteEntry( vOccurs, i, Vec_WrdAlloc(16) );
    // add nodes
    Wlc_NtkForEachObj( p, pObj, i )
    {
//        char * pName = Wlc_ObjName(p, i);
        if ( Wlc_ObjSign(pObj) > 0x1FFFFF )
            printf( "Object %6d has range %d, which is reduced to %d in the statistics.\n", 
                i, Wlc_ObjRange(pObj), Wlc_ObjRange(pObj) & 0xFFFFF );
        if ( pObj->Beg )
        {
            if ( pObjRange == NULL )
                pObjRange = pObj;
            nCountRange++;
        }
        // 0-input types
        if ( Wlc_ObjIsCi(pObj) || pObj->Type == WLC_OBJ_CONST || pObj->Type == WLC_OBJ_BIT_CONCAT )
            Sign = Wlc_NtkPrintDistribMakeSign( Wlc_ObjSign(pObj), 0, 0 );
        // 1-input types
        else if ( pObj->Type == WLC_OBJ_BUF    || pObj->Type == WLC_OBJ_BIT_SELECT  || pObj->Type == WLC_OBJ_TABLE ||
             pObj->Type == WLC_OBJ_BIT_ZEROPAD || pObj->Type == WLC_OBJ_BIT_SIGNEXT || 
             pObj->Type == WLC_OBJ_BIT_NOT     || pObj->Type == WLC_OBJ_LOGIC_NOT   || pObj->Type == WLC_OBJ_ARI_MINUS )
            Sign = Wlc_NtkPrintDistribMakeSign( Wlc_ObjSign(pObj), Wlc_ObjSign(Wlc_ObjFanin0(p, pObj)), 0 );
        // 2-input types (including MUX)
        else if ( Wlc_ObjFaninNum(pObj) == 0 )
            printf( "Object %d with name \"%s\" has type 0. Looks like it was declared by not defined...\n", i, Wlc_ObjName(p, i) );
        else if ( Wlc_ObjFaninNum(pObj) == 1 )
            Sign = Wlc_NtkPrintDistribMakeSign( Wlc_ObjSign(pObj), Wlc_ObjSign(Wlc_ObjFanin0(p, pObj)), 0 );
        else
        {
            assert( Wlc_ObjFaninNum(pObj) >= 2 );
            Sign = Wlc_NtkPrintDistribMakeSign( Wlc_ObjSign(pObj), Wlc_ObjFaninId(pObj, 0) ? Wlc_ObjSign(Wlc_ObjFanin0(p, pObj)) : 0, Wlc_ObjFaninId(pObj, 1) ? Wlc_ObjSign(Wlc_ObjFanin1(p, pObj)) : 0 );
        }
        // add to storage
        Wlc_NtkPrintDistribAddOne( vTypes, vOccurs, pObj->Type, Sign );
        // count the number of AIG nodes
        if ( pObj->Type == WLC_OBJ_MUX )
            Vec_IntAddToEntry( vAnds, WLC_OBJ_MUX,      3 * Wlc_ObjRange(pObj) * (Wlc_ObjFaninNum(pObj) - 2) );
        else if ( pObj->Type == WLC_OBJ_SHIFT_R )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_SHIFT_R,  Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_SHIFT_RA )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_SHIFT_RA, Wlc_ObjRange(pObj) * Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_SHIFT_L )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_SHIFT_L,  Wlc_ObjRange(pObj) * Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_SHIFT_LA )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_SHIFT_LA, Wlc_ObjRange(pObj) * Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_ROTATE_R )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ROTATE_R, Wlc_ObjRange(pObj) * Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_ROTATE_L )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ROTATE_L, Wlc_ObjRange(pObj) * Abc_MinInt(Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Abc_Base2Log(Wlc_ObjRange(pObj))) * 3 );
        else if ( pObj->Type == WLC_OBJ_BIT_NOT )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_NOT, 0 );
        else if ( pObj->Type == WLC_OBJ_BIT_AND )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_AND,      Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_OR )       
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_OR,       Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_XOR )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_XOR,  3 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_NAND )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_NAND,     Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_NOR )       
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_NOR,      Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_NXOR )      
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_NXOR, 3 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_BIT_SELECT )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_SELECT, 0 );
        else if ( pObj->Type == WLC_OBJ_BIT_CONCAT )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_CONCAT, 0 );
        else if ( pObj->Type == WLC_OBJ_BIT_ZEROPAD )  
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_ZEROPAD, 0 );
        else if ( pObj->Type == WLC_OBJ_BIT_SIGNEXT )  
            Vec_IntAddToEntry( vAnds, WLC_OBJ_BIT_SIGNEXT, 0 );
        else if ( pObj->Type == WLC_OBJ_LOGIC_NOT )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_LOGIC_NOT,        Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_LOGIC_IMPL )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_LOGIC_IMPL,       Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) + Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_LOGIC_AND )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_LOGIC_AND,        Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) + Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_LOGIC_OR )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_LOGIC_OR,         Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) + Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_LOGIC_XOR )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_LOGIC_XOR,        Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) + Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)) + 1 );
        else if ( pObj->Type == WLC_OBJ_COMP_EQU )     
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_EQU,     4 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_COMP_NOTEQU )  
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_NOTEQU,  4 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_COMP_LESS )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_LESS,    6 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 6 );
        else if ( pObj->Type == WLC_OBJ_COMP_MORE )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_MORE,    6 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 6 );
        else if ( pObj->Type == WLC_OBJ_COMP_LESSEQU ) 
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_LESSEQU, 6 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 6 );
        else if ( pObj->Type == WLC_OBJ_COMP_MOREEQU ) 
            Vec_IntAddToEntry( vAnds, WLC_OBJ_COMP_MOREEQU, 6 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 6 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_AND )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_AND,       Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_OR )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_OR,        Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_XOR )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_XOR,   3 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 3 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_NAND )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_NAND,      Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_NOR )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_NOR,       Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 1 );
        else if ( pObj->Type == WLC_OBJ_REDUCT_NXOR )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_REDUCT_NXOR,  3 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 3 );
        else if ( pObj->Type == WLC_OBJ_ARI_ADD )       
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_ADD,      9 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_ARI_SUB )       
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_SUB,      9 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_ARI_MULTI )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_MULTI,    9 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_ARI_DIVIDE )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_DIVIDE,  13 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 19 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) + 10 );
        else if ( pObj->Type == WLC_OBJ_ARI_REM )  
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_REM,     13 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 7 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 2  );
        else if ( pObj->Type == WLC_OBJ_ARI_MODULUS )  
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_MODULUS, 13 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 7 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) - 2  );
        else if ( pObj->Type == WLC_OBJ_ARI_POWER ) 
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_POWER,   10 * (int)pow((double)Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)),(double)Wlc_ObjRange(Wlc_ObjFanin0(p, pObj))) );
        else if ( pObj->Type == WLC_OBJ_ARI_MINUS )   
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_MINUS,    4 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) );
        else if ( pObj->Type == WLC_OBJ_ARI_SQRT )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_SQRT,    11 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) / 8 + 5 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) / 2 - 5  );
        else if ( pObj->Type == WLC_OBJ_ARI_SQUARE )    
            Vec_IntAddToEntry( vAnds, WLC_OBJ_ARI_SQUARE,   5 * Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)) * Wlc_ObjRange(Wlc_ObjFanin1(p, pObj))  );
    }
    if ( nCountRange && Vec_IntSize(&p->vNameIds) > 0 )
    {
        printf( "Warning: %d objects of the design have non-zero-based ranges.\n", nCountRange );
        printf( "In particular, object %6d with name \"%s\" has range %d=[%d:%d]\n", Wlc_ObjId(p, pObjRange), 
            Abc_NamStr(p->pManName, Wlc_ObjNameId(p, Wlc_ObjId(p, pObjRange))), Wlc_ObjRange(pObjRange), pObjRange->End, pObjRange->Beg );
    }
    // print by occurrence
    printf( "ID  :  name  occurrence%s    and2 (occurrence)<output_range>=<input_range>.<input_range> ...\n", fTwoSides ? "     Left Share Right":"" );
    for ( i = 0; i < WLC_OBJ_NUMBER; i++ )
    {
        Vec_Wrd_t * vType  = (Vec_Wrd_t *)Vec_PtrEntry( vTypes, i );
        Vec_Wrd_t * vOccur = (Vec_Wrd_t *)Vec_PtrEntry( vOccurs, i );
        if ( p->nObjs[i] == 0 )
            continue;
        printf( "%2d  :  %-8s  %6d", i, Wlc_Names[i], p->nObjs[i] );
        if ( fTwoSides )
        {
            int nTotal = i == WLC_OBJ_PI ? Wlc_NtkCountRealPis(p) : p->nObjs[i];
            printf( "   " );
            printf( "%6d", nObjs[0][i] );
            printf( "%6d", nObjs[0][i]+nObjs[1][i]-nTotal );
            printf( "%6d", nObjs[1][i] );
        }
        printf( "%8d ", Vec_IntEntry(vAnds, i) );
        // sort by occurence
        Wlc_NtkPrintDistribSortOne( vTypes, vOccurs, i );
        Vec_WrdForEachEntry( vType, Sign, k )
        {
            Wlc_NtkPrintDistribFromSign( Sign, &s, &s0, &s1 );
            if ( ((k % 6) == 5 && s1) || ((k % 8) == 7 && !s1) )
            {
                printf( "\n                                " );
                if ( fTwoSides )
                    printf( "                     " );
            }
            printf( "(%d)", (int)Vec_WrdEntry( vOccur, k ) );
            printf( "%s%d",      Abc_LitIsCompl(s)?"-":"",  Abc_Lit2Var(s) );
            if ( s0 )
                printf( "=%s%d", Abc_LitIsCompl(s0)?"-":"", Abc_Lit2Var(s0) );
            if ( s1 )
                printf( ".%s%d", Abc_LitIsCompl(s1)?"-":"", Abc_Lit2Var(s1) );
            printf( " " );
        }
        printf( "\n" );
    }
    Vec_VecFree( (Vec_Vec_t *)vTypes );
    Vec_VecFree( (Vec_Vec_t *)vOccurs );
    Vec_IntFree( vAnds );
}
void Wlc_NtkPrintNode( Wlc_Ntk_t * p, Wlc_Obj_t * pObj )
{
    printf( "%8d  :  ", Wlc_ObjId(p, pObj) );
    if ( Vec_IntSize(&p->vLevels) )
        printf( "Lev = %2d  ", Vec_IntEntry(&p->vLevels, Wlc_ObjId(p,pObj)) );
    printf( "%6d%s = ", Wlc_ObjRange(pObj), Wlc_ObjIsSigned(pObj) ? "s" : " " );
    if ( pObj->Type == WLC_OBJ_PI )
    {
        printf( "            PI                   :    %-12s\n", Wlc_ObjName(p, Wlc_ObjId(p, pObj)) );
        return;
    }
    if ( pObj->Type == WLC_OBJ_FO )
    {
        printf( "            FO                   :    %-12s = %-12s\n", Wlc_ObjName(p, Wlc_ObjId(p, pObj)), Wlc_ObjName(p, Wlc_ObjId(p, Wlc_ObjFo2Fi(p, pObj))) );
        return;
    }
    if ( pObj->Type != WLC_OBJ_CONST && Wlc_ObjFaninNum(pObj) == 0 )
    {
        printf( "Unknown object without fanins    :    %-12s\n", Wlc_ObjName(p, Wlc_ObjId(p, pObj)) );
        return;
    }
    if ( pObj->Type != WLC_OBJ_CONST )
    {
        printf( "%6d%s  %5s  ",  Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)), Wlc_ObjIsSigned(Wlc_ObjFanin0(p, pObj)) ? "s" : " ", Wlc_Names[(int)pObj->Type] );
        if ( Wlc_ObjFaninNum(pObj) > 1 )
            printf( "%6d%s ",       Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)), Wlc_ObjIsSigned(Wlc_ObjFanin1(p, pObj)) ? "s" : " " );
        else
            printf( "        " );
        if ( Wlc_ObjFaninNum(pObj) > 2 )
            printf( "%6d%s ",       Wlc_ObjRange(Wlc_ObjFanin2(p, pObj)), Wlc_ObjIsSigned(Wlc_ObjFanin2(p, pObj)) ? "s" : " " );
        else
            printf( "        " );
    }
    else
        printf( "                                " );
    printf( " :    " );
    printf( "%-12s",   Wlc_ObjName(p, Wlc_ObjId(p, pObj)) );
    if ( pObj->Type == WLC_OBJ_CONST )
    {
        printf( " = %d\'%sh", Wlc_ObjRange(pObj), Wlc_ObjIsSigned(pObj) ? "s":"" );
        if ( pObj->fXConst )
        {
            int k;
            for ( k = 0; k < (Wlc_ObjRange(pObj) + 3) / 4; k++ )
                printf( "x" );
        }
        else
            Abc_TtPrintHexArrayRev( stdout, (word *)Wlc_ObjConstValue(pObj), (Wlc_ObjRange(pObj) + 3) / 4 );
    }
    else
    {
        printf( " =  %-12s  %5s  ", Wlc_ObjName(p, Wlc_ObjFaninId0(pObj)), Wlc_Names[(int)pObj->Type] );
        if ( Wlc_ObjFaninNum(pObj) > 1 )
            printf( "%-12s ",       Wlc_ObjName(p, Wlc_ObjFaninId1(pObj)) );
        else
            printf( "             " );
        if ( Wlc_ObjFaninNum(pObj) > 2 )
            printf( "%-12s ",       Wlc_ObjName(p, Wlc_ObjFaninId2(pObj)) );
    }
    printf( "\n" );
}
void Wlc_NtkPrintNodeArray( Wlc_Ntk_t * p, Vec_Int_t * vArray )
{
    Wlc_Obj_t * pObj; 
    int i;
    Wlc_NtkForEachObjVec( vArray, p, pObj, i )
        Wlc_NtkPrintNode( p, pObj );
}
void Wlc_NtkPrintNodes( Wlc_Ntk_t * p, int Type )
{
    Wlc_Obj_t * pObj; 
    int i, Counter = 0;
    printf( "Operation %s\n", Wlc_Names[Type] );
    Wlc_NtkForEachObj( p, pObj, i )
    {
        if ( (int)pObj->Type != Type )
            continue;
        printf( "%8d  :", Counter++ );
        Wlc_NtkPrintNode( p, pObj );
    }
}
void Wlc_NtkPrintStats( Wlc_Ntk_t * p, int fDistrib, int fTwoSides, int fVerbose )
{
    int i;
    printf( "%-20s : ",        p->pName );
    printf( "PI = %4d  ",      Wlc_NtkCountRealPis(p) ); //Wlc_NtkPiNum(p) );
    printf( "PO = %4d  ",      Wlc_NtkPoNum(p) );
    printf( "FF = %4d  ",      Wlc_NtkFfNum(p) );
    printf( "Obj = %6d  ",     Wlc_NtkObjNum(p) - Wlc_NtkPiNum(p) - Wlc_NtkPoNum(p) - Wlc_NtkFfNum(p) );
    printf( "Mem = %.3f MB",   1.0*Wlc_NtkMemUsage(p)/(1<<20) );
    printf( "\n" );
    if ( fDistrib )
    {
        Wlc_NtkPrintDistrib( p, fTwoSides, fVerbose );
        return;
    }
    if ( !fVerbose )
        return;
    printf( "Node type statistics:\n" );
    for ( i = 1; i < WLC_OBJ_NUMBER; i++ )
    {
        if ( !p->nObjs[i] )
            continue;
        if ( p->nAnds[0] && p->nAnds[i] )
            printf( "%2d  :  %-8s  %6d  %7.2f %%\n", i, Wlc_Names[i], p->nObjs[i], 100.0*p->nAnds[i]/p->nAnds[0] );
        else
            printf( "%2d  :  %-8s  %6d\n", i, Wlc_Names[i], p->nObjs[i] );
    }
}
void Wlc_NtkPrintObjects( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj; int i;
    Wlc_NtkForEachObj( p, pObj, i )
        Wlc_NtkPrintNode( p, pObj );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkTransferNames( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p )
{
    int i;
    assert( !Wlc_NtkHasCopy(pNew)   && Wlc_NtkHasCopy(p)   );
    assert( !Wlc_NtkHasNameId(pNew) && Wlc_NtkHasNameId(p) );
    assert( pNew->pManName  == NULL && p->pManName != NULL );
    Wlc_NtkCleanNameId( pNew );
    for ( i = 0; i < p->nObjsAlloc; i++ )
        if ( Wlc_ObjCopy(p, i) > 0 && i < Vec_IntSize(&p->vNameIds) && Wlc_ObjNameId(p, i) )
            Wlc_ObjSetNameId( pNew, Wlc_ObjCopy(p, i), Wlc_ObjNameId(p, i) );
    pNew->pManName = p->pManName; 
    p->pManName = NULL;
    Vec_IntErase( &p->vNameIds );
    // transfer table
    pNew->pMemTable = p->pMemTable;  p->pMemTable = NULL;
    pNew->vTables = p->vTables;      p->vTables = NULL;
}
char * Wlc_NtkNewName( Wlc_Ntk_t * p, int iCoId, int fSeq )
{
    static char pBuffer[1000];
    sprintf( pBuffer, "%s_o%d_%s", p->pName, iCoId, fSeq ? "seq": "comb" );
    return pBuffer;
}

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

  Synopsis    [Reduce init vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Wlc_ReduceMarkedInitVec( Wlc_Ntk_t * p, Vec_Int_t * vInit )
{
    Vec_Int_t * vInitNew = Vec_IntDup( vInit );
    Wlc_Obj_t * pObj; int i, k = 0;
    assert( Vec_IntSize(vInit) == Wlc_NtkCiNum(p) - Wlc_NtkPiNum(p) );
    Wlc_NtkForEachCi( p, pObj, i )
        if ( !Wlc_ObjIsPi(pObj) && pObj->Mark )
            Vec_IntWriteEntry( vInitNew, k++, Vec_IntEntry(vInit, i - Wlc_NtkPiNum(p)) );
    Vec_IntShrink( vInitNew, k );
    return vInitNew;
}
char * Wlc_ReduceMarkedInitStr( Wlc_Ntk_t * p, char * pInit )
{
    char * pInitNew = Abc_UtilStrsav( pInit );
    Wlc_Obj_t * pObj; int i, b, nBits = 0, k = 0;
    Wlc_NtkForEachCi( p, pObj, i )
    {
        if ( !Wlc_ObjIsPi(pObj) && pObj->Mark )
            for ( b = 0; b < Wlc_ObjRange(pObj); b++ )
                pInitNew[k++] = pInitNew[nBits+b];
        if ( !Wlc_ObjIsPi(pObj) )
            nBits += Wlc_ObjRange(pObj);
    }
    pInitNew[k] = '\0';
    assert( nBits == (int)strlen(pInit) );
    return pInitNew;
}

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

  Synopsis    [Duplicates the network in a topological order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_ObjCollectCopyFanins( Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins )
{
    int i, iFanin;
    Wlc_Obj_t * pObj = Wlc_NtkObj( p, iObj );
    Vec_IntClear( vFanins );
    Wlc_ObjForEachFanin( pObj, iFanin, i )
        Vec_IntPush( vFanins, Wlc_ObjCopy(p, iFanin) );
    // special treatment of CONST and SELECT
    if ( pObj->Type == WLC_OBJ_CONST )
    {
        int * pInts = Wlc_ObjConstValue( pObj );
        int nInts = Abc_BitWordNum( Wlc_ObjRange(pObj) );
        for ( i = 0; i < nInts; i++ )
            Vec_IntPush( vFanins, pInts[i] );
    }
    else if ( pObj->Type == WLC_OBJ_BIT_SELECT )
    {
        assert( Vec_IntSize(vFanins) == 1 );
        Vec_IntPushTwo( vFanins, Wlc_ObjRangeEnd(pObj), Wlc_ObjRangeBeg(pObj) );
    }
    else if ( pObj->Type == WLC_OBJ_TABLE )
    {
        assert( Vec_IntSize(vFanins) == 1 );
        Vec_IntPush( vFanins, pObj->Fanins[1] );
    }
}
int Wlc_ObjDup( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins )
{
    Wlc_Obj_t * pObj = Wlc_NtkObj( p, iObj );
    int iFaninNew = Wlc_ObjAlloc( pNew, pObj->Type, Wlc_ObjIsSigned(pObj), pObj->End, pObj->Beg );
    Wlc_Obj_t * pObjNew = Wlc_NtkObj(pNew, iFaninNew);
    Wlc_ObjCollectCopyFanins( p, iObj, vFanins );
    Wlc_ObjAddFanins( pNew, pObjNew, vFanins );
    Wlc_ObjSetCopy( p, iObj, iFaninNew );
    pObjNew->fXConst = pObj->fXConst;
    return iFaninNew;
}
void Wlc_NtkDupDfs_rec( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins )
{
    Wlc_Obj_t * pObj;
    int i, iFanin;
    if ( iObj == 0 )
        return;
    if ( Wlc_ObjCopy(p, iObj) )
        return;
    pObj = Wlc_NtkObj( p, iObj );
    //printf( "Visiting node %16s (ID %6d) of type %5s (type ID %2d)\n", Wlc_ObjName(p, iObj), iObj, Wlc_ObjTypeName(pObj), Wlc_NtkObj(p, iObj)->Type );
    assert( pObj->Type != WLC_OBJ_FF );
    Wlc_ObjForEachFanin( pObj, iFanin, i )
        Wlc_NtkDupDfs_rec( pNew, p, iFanin, vFanins );
    Wlc_ObjDup( pNew, p, iObj, vFanins );
}

Wlc_Ntk_t * Wlc_NtkDupDfsSimple( Wlc_Ntk_t * p )
{
    Wlc_Ntk_t * pNew;
    Wlc_Obj_t * pObj;
    Vec_Int_t * vFanins;
    int i;
    Wlc_NtkCleanCopy( p );
    vFanins = Vec_IntAlloc( 100 );
    pNew = Wlc_NtkAlloc( p->pName, p->nObjsAlloc );
    pNew->fSmtLib = p->fSmtLib;
    pNew->fAsyncRst = p->fAsyncRst;
    pNew->fMemPorts = p->fMemPorts;
    pNew->fEasyFfs = p->fEasyFfs;
    Wlc_NtkForEachCi( p, pObj, i )
        Wlc_ObjDup( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    Wlc_NtkForEachCo( p, pObj, i )
        Wlc_NtkDupDfs_rec( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    Wlc_NtkForEachCo( p, pObj, i )
        Wlc_ObjSetCo( pNew, Wlc_ObjCopyObj(pNew, p, pObj), pObj->fIsFi );
    if ( p->vInits )
    pNew->vInits = Vec_IntDup( p->vInits );
    if ( p->pInits )
    pNew->pInits = Abc_UtilStrsav( p->pInits );
    Vec_IntFree( vFanins );
    if ( p->pSpec )
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    return pNew;
}

Wlc_Ntk_t * Wlc_NtkDupDfs( Wlc_Ntk_t * p, int fMarked, int fSeq )
{
    Wlc_Ntk_t * pNew;
    Wlc_Obj_t * pObj, * pObjNew;
    Vec_Int_t * vFanins;
    int i, k, iObj, iFanin;
    vFanins = Vec_IntAlloc( 100 );
    Wlc_NtkCleanCopy( p );
    pNew = Wlc_NtkAlloc( p->pName, p->nObjsAlloc );
    pNew->fSmtLib = p->fSmtLib;
    pNew->fAsyncRst = p->fAsyncRst;
    pNew->fMemPorts = p->fMemPorts;
    pNew->fEasyFfs = p->fEasyFfs;
    Wlc_NtkForEachCi( p, pObj, i )
        if ( !fMarked || pObj->Mark )
        {
            unsigned Type = pObj->Type;
            if ( !fSeq ) pObj->Type = WLC_OBJ_PI;
            Wlc_ObjDup( pNew, p, Wlc_ObjId(p, pObj), vFanins );
            pObj->Type = Type;
        }
    Wlc_NtkForEachFf2( p, pObj, i )
    {
        int iObjNew = Wlc_ObjAlloc( pNew, pObj->Type, Wlc_ObjIsSigned(pObj), pObj->End, pObj->Beg );
        Wlc_ObjSetCopy( p, Wlc_ObjId(p, pObj), iObjNew );
        Vec_IntPush( &pNew->vFfs2, iObjNew );
    }
    Wlc_NtkForEachCo( p, pObj, i )
        if ( !fMarked || pObj->Mark )
            Wlc_NtkDupDfs_rec( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    Wlc_NtkForEachCo( p, pObj, i )
        if ( !fMarked || pObj->Mark )
            Wlc_ObjSetCo( pNew, Wlc_ObjCopyObj(pNew, p, pObj), fSeq ? pObj->fIsFi : 0 );
    Wlc_NtkForEachFf2( p, pObj, i )
    {
        iObj = Wlc_ObjId(p, pObj);
        Wlc_ObjForEachFanin( pObj, iFanin, k )
            Wlc_NtkDupDfs_rec( pNew, p, iFanin, vFanins );
        Wlc_ObjCollectCopyFanins( p, iObj, vFanins );
        pObjNew = Wlc_NtkObj( pNew, Wlc_ObjCopy(p, iObj) );
        Wlc_ObjAddFanins( pNew, pObjNew, vFanins );
        pObjNew->fXConst = pObj->fXConst;
    }
    Vec_IntFree( vFanins );
    if ( fSeq && p->vInits )
    {
        if ( fMarked )
        {
            if ( p->vInits )
                pNew->vInits = Wlc_ReduceMarkedInitVec( p, p->vInits );
            if ( p->pInits )
                pNew->pInits = Wlc_ReduceMarkedInitStr( p, p->pInits );
        }
        else
        {
            if ( p->vInits )
                pNew->vInits = Vec_IntDup( p->vInits );
            if ( p->pInits )
                pNew->pInits = Abc_UtilStrsav( p->pInits );
        }
    }
    if ( p->pSpec )
        pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    if ( Wlc_NtkHasNameId(p) )
        Wlc_NtkTransferNames( pNew, p );
    if ( Vec_IntSize(&p->vPoPairs) )
        Vec_IntAppend( &pNew->vPoPairs, &p->vPoPairs );
    return pNew;
}
Wlc_Ntk_t * Wlc_NtkDupDfsAbs( Wlc_Ntk_t * p, Vec_Int_t * vPisOld, Vec_Int_t * vPisNew, Vec_Int_t * vFlops )
{
    Wlc_Ntk_t * pNew;
    Wlc_Obj_t * pObj;
    Vec_Int_t * vFanins;
    int i;
    Wlc_NtkCleanCopy( p );
    pNew = Wlc_NtkAlloc( p->pName, p->nObjsAlloc );
    pNew->fSmtLib = p->fSmtLib;
    pNew->fAsyncRst = p->fAsyncRst;
    pNew->fMemPorts = p->fMemPorts;
    pNew->fEasyFfs = p->fEasyFfs;

    // duplicate marked PIs
    vFanins = Vec_IntAlloc( 100 );
    Wlc_NtkForEachObjVec( vPisOld, p, pObj, i )
    {
        assert( Wlc_ObjIsPi(pObj) );
        Wlc_ObjDup( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    }
    // duplicate additional PIs
    Wlc_NtkForEachObjVec( vPisNew, p, pObj, i )
    {
        unsigned Type = pObj->Type;
        int nFanins = Wlc_ObjFaninNum(pObj);
        assert( !Wlc_ObjIsPi(pObj) );
        pObj->Type = WLC_OBJ_PI;
        pObj->nFanins = 0;
        Wlc_ObjDup( pNew, p, Wlc_ObjId(p, pObj), vFanins );
        pObj->Type = Type;
        pObj->nFanins = (unsigned)nFanins;
    }
    // duplicate flop outputs
    Wlc_NtkForEachObjVec( vFlops, p, pObj, i )
    {
        assert( !Wlc_ObjIsPi(pObj) && Wlc_ObjIsCi(pObj) );
        Wlc_ObjDup( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    }

    // duplicate logic cones of primary outputs
    Wlc_NtkForEachPo( p, pObj, i )
        Wlc_NtkDupDfs_rec( pNew, p, Wlc_ObjId(p, pObj), vFanins );
    // duplidate logic cone of flop inputs
    Wlc_NtkForEachObjVec( vFlops, p, pObj, i )
        Wlc_NtkDupDfs_rec( pNew, p, Wlc_ObjId(p, Wlc_ObjFo2Fi(p, pObj)), vFanins );

    // duplicate POs
    Wlc_NtkForEachPo( p, pObj, i )
        Wlc_ObjSetCo( pNew, Wlc_ObjCopyObj(pNew, p, pObj), 0 );
    // duplicate flop inputs 
    Wlc_NtkForEachObjVec( vFlops, p, pObj, i )
        Wlc_ObjSetCo( pNew, Wlc_ObjCopyObj(pNew, p, Wlc_ObjFo2Fi(p, pObj)), 1 );
    Vec_IntFree( vFanins );

    // mark flop outputs
    Wlc_NtkForEachObjVec( vFlops, p, pObj, i )
        pObj->Mark = 1;
    if ( p->vInits )
        pNew->vInits = Wlc_ReduceMarkedInitVec( p, p->vInits );
    if ( p->pInits )
        pNew->pInits = Wlc_ReduceMarkedInitStr( p, p->pInits );
    Wlc_NtkCleanMarks( p );

    if ( p->pSpec )
        pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    //Wlc_NtkTransferNames( pNew, p );
    return pNew;
}

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

  Synopsis    [Select the cone of the given output.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkCleanMarks( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj;
    int i;
    Wlc_NtkForEachObj( p, pObj, i )
        pObj->Mark = 0;
}
int Wlc_NtkCountMarked( Wlc_Ntk_t * p, int * pnPis, int * pnFos, int * pnAdders, int * pnMults )
{
    Wlc_Obj_t * pObj;
    int i, nNodes = 0;
    *pnPis = *pnFos = *pnAdders = *pnMults = 0;
    Wlc_NtkForEachObj( p, pObj, i )
    {
        if ( !pObj->Mark )
            continue;
        if ( Wlc_ObjIsPi(pObj) )
            (*pnPis)++;
        else if ( Wlc_ObjIsCi(pObj) )
            (*pnFos)++;
        else if ( pObj->Mark )
        {
            nNodes++;
            if ( pObj->Type == WLC_OBJ_ARI_ADD || pObj->Type == WLC_OBJ_ARI_SUB )
                (*pnAdders)++;
            else if ( pObj->Type == WLC_OBJ_ARI_MULTI )
                (*pnMults)++;
        }
    }
    return nNodes;
}
void Wlc_NtkMarkCone_rec( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, Vec_Int_t * vFlops )
{
    int i, iFanin;
    if ( pObj->Mark )
        return;
    pObj->Mark = 1;
    if ( Wlc_ObjIsCi(pObj) )
    {
        if ( !Wlc_ObjIsPi(pObj) )
            Vec_IntPush( vFlops, Wlc_ObjCiId(pObj) );
        return;
    }
    Wlc_ObjForEachFanin( pObj, iFanin, i ) if ( iFanin )
        Wlc_NtkMarkCone_rec( p, Wlc_NtkObj(p, iFanin), vFlops );
}
void Wlc_NtkMarkCone( Wlc_Ntk_t * p, int iCoId, int Range, int fSeq, int fAllPis )
{
    Vec_Int_t * vFlops;
    Wlc_Obj_t * pObj;
    int i, CiId, CoId;
    Wlc_NtkCleanMarks( p );
    if ( fAllPis )
    Wlc_NtkForEachPi( p, pObj, i )
        pObj->Mark = 1;
    vFlops = Vec_IntAlloc( 100 );
    Wlc_NtkForEachCo( p, pObj, i )
        if ( iCoId == -1 || (i >= iCoId && i < iCoId + Range) )
            Wlc_NtkMarkCone_rec( p, pObj, vFlops );
    if ( fSeq )
    Vec_IntForEachEntry( vFlops, CiId, i )
    {
        CoId = Wlc_NtkPoNum(p) + CiId - Wlc_NtkPiNum(p);
        Wlc_NtkMarkCone_rec( p, Wlc_NtkCo(p, CoId), vFlops );
    }
    Vec_IntFree( vFlops );
}
void Wlc_NtkProfileCones( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj; 
    int i, nPis, nFos, nNodes, nAdders, nMults;
    Wlc_NtkForEachCo( p, pObj, i )
    {
        Wlc_NtkMarkCone( p, i, 1, 0, 0 );
        nNodes = Wlc_NtkCountMarked( p, &nPis, &nFos, &nAdders, &nMults );
        printf( "Cone %5d : ", i );
        printf( "PI = %4d  ", nPis );
        printf( "FO = %4d  ", nFos );
        printf( "Node = %6d  ", nNodes );
        printf( "Add/Sub = %4d  ", nAdders );
        printf( "Mult = %4d  ", nMults );
        printf( "\n" );
    }
    Wlc_NtkCleanMarks( p );
}

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

  Synopsis    [Duplicates the network by copying each node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Wlc_Ntk_t * Wlc_NtkDupSingleNodes( Wlc_Ntk_t * p )
{
    Wlc_Ntk_t * pNew;
    Vec_Int_t * vFanins;
    Wlc_Obj_t * pObj, * pObjNew;
    Wlc_Obj_t * pFanin, * pFaninNew;
    int i, k, iFanin, iFaninNew, iObjNew, Count = 0;
    // count objects
    Wlc_NtkForEachObj( p, pObj, i )
        if ( !Wlc_ObjIsCi(pObj) )
            Count += 1 + Wlc_ObjFaninNum(pObj);
    // copy objects
    Wlc_NtkCleanCopy( p );
    vFanins = Vec_IntAlloc( 100 );
    pNew = Wlc_NtkAlloc( p->pName, p->nObjsAlloc );
    pNew->fSmtLib = p->fSmtLib;
    pNew->fAsyncRst = p->fAsyncRst;
    pNew->fMemPorts = p->fMemPorts;
    pNew->fEasyFfs = p->fEasyFfs;
    Wlc_NtkForEachObj( p, pObj, i )
    {
        if ( Wlc_ObjIsCi(pObj) )
            continue;
        if ( pObj->Type == WLC_OBJ_ARI_MULTI )
            continue;
        if ( pObj->Type == WLC_OBJ_MUX && Wlc_ObjFaninNum(pObj) > 3 )
            continue;
        // create CIs for the fanins
        Wlc_ObjForEachFanin( pObj, iFanin, k )
        {
            pFanin = Wlc_NtkObj(p, iFanin);
            iFaninNew = Wlc_ObjAlloc( pNew, WLC_OBJ_PI, pFanin->Signed, pFanin->End, pFanin->Beg );
            pFaninNew = Wlc_NtkObj(pNew, iFaninNew);
            Wlc_ObjSetCopy( p, iFanin, iFaninNew );
            //Wlc_ObjSetCi( pNew, pFaninNew );
        }
        // create object
        iObjNew = Wlc_ObjDup( pNew, p, i, vFanins );
        pObjNew = Wlc_NtkObj(pNew, iObjNew);
        pObjNew->fIsPo = 1;
        Vec_IntPush( &pNew->vPos, iObjNew );
    }
    Vec_IntFree( vFanins );
    Wlc_NtkTransferNames( pNew, p );
    if ( p->pSpec )
    pNew->pSpec = Abc_UtilStrsav( p->pSpec );
    return pNew;
}

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

  Synopsis    [Creates short names for all objects.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkShortNames( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj;
    char pBuffer[1000];
    int NameId, fFound, i;
    int nFlops = Wlc_NtkCoNum(p) - Wlc_NtkPoNum(p);
    unsigned char nDigits = (unsigned char)Abc_Base10Log( nFlops );
    Wlc_NtkForEachCo( p, pObj, i )
    {
        if ( Wlc_ObjIsPo(pObj) )
            continue;
        sprintf( pBuffer, "%s%0*d", "fi", nDigits, i - Wlc_NtkPoNum(p) );
        NameId = Abc_NamStrFindOrAdd( p->pManName, pBuffer, &fFound );
        Wlc_ObjSetNameId( p, Wlc_ObjId(p, pObj), NameId );
    }
    Wlc_NtkForEachCi( p, pObj, i )
    {
        if ( Wlc_ObjIsPi(pObj) )
            continue;
        sprintf( pBuffer, "%s%0*d", "fo", nDigits, i - Wlc_NtkPiNum(p) );
        NameId = Abc_NamStrFindOrAdd( p->pManName, pBuffer, &fFound );
        Wlc_ObjSetNameId( p, Wlc_ObjId(p, pObj), NameId );
    }
    nDigits = Abc_Base10Log( Wlc_NtkPoNum(p) );
    Wlc_NtkForEachPo( p, pObj, i )
    {
        sprintf( pBuffer, "%s%0*d", "po", nDigits, i );
        NameId = Abc_NamStrFindOrAdd( p->pManName, pBuffer, &fFound );
        Wlc_ObjSetNameId( p, Wlc_ObjId(p, pObj), NameId );
    }
    nDigits = Abc_Base10Log( Wlc_NtkPiNum(p) );
    Wlc_NtkForEachPi( p, pObj, i )
    {
        sprintf( pBuffer, "%s%0*d", "pi", nDigits, i );
        NameId = Abc_NamStrFindOrAdd( p->pManName, pBuffer, &fFound );
        Wlc_ObjSetNameId( p, Wlc_ObjId(p, pObj), NameId );
    }
    nDigits = Abc_Base10Log( Wlc_NtkObjNum(p) );
    Wlc_NtkForEachObj( p, pObj, i )
    {
        if ( Wlc_ObjIsCi(pObj) || Wlc_ObjIsCo(pObj) )
            continue;
        sprintf( pBuffer, "%s%0*d", "n", nDigits, i );
        NameId = Abc_NamStrFindOrAdd( p->pManName, pBuffer, &fFound );
        Wlc_ObjSetNameId( p, Wlc_ObjId(p, pObj), NameId );
    }
}

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

  Synopsis    [Count the number of flops initialized to DC value.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Wlc_NtkDcFlopNum( Wlc_Ntk_t * p )
{
    int i, nFlops, Count = 0;
    if ( p->pInits == NULL )
        return 0;
    nFlops = strlen(p->pInits);
    for ( i = 0; i < nFlops; i++ )
        Count += (p->pInits[i] == 'x' || p->pInits[i] == 'X');
    return Count;
}

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

  Synopsis    [Create references.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Wlc_NtkSetRefs( Wlc_Ntk_t * p )
{
    Wlc_Obj_t * pObj; int i, k, Fanin;
    Vec_IntFill( &p->vRefs, Wlc_NtkObjNumMax(p), 0 );
    Wlc_NtkForEachObj( p, pObj, i )
        Wlc_ObjForEachFanin( pObj, Fanin, k )
            Vec_IntAddToEntry( &p->vRefs, Fanin, 1 );
    Wlc_NtkForEachCo( p, pObj, i )
        Vec_IntAddToEntry( &p->vRefs, Wlc_ObjId(p, pObj), 1 );
}

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

  Synopsis    [This procedure simply count the number of PPI bits.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Wlc_NtkCountObjBits( Wlc_Ntk_t * p, Vec_Int_t * vPisNew )
{
    Wlc_Obj_t * pObj; 
    int i, Count = 0;
    Wlc_NtkForEachObjVec( vPisNew, p, pObj, i )
        Count += Wlc_ObjRange(pObj);
    return Count;
}

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


ABC_NAMESPACE_IMPL_END