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

  FileName    [cbaNtk.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Hierarchical word-level netlist.]

  Synopsis    [Network manipulation.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - July 21, 2015.]

  Revision    [$Id: cbaNtk.c,v 1.00 2014/11/29 00:00:00 alanmi Exp $]

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

#include <math.h>
#include "cba.h"

ABC_NAMESPACE_IMPL_START

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

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

/**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 Cba_NtkPrintDistribMakeSign( int s, int s0, int s1 )
{
    return ((word)s1 << 42) | ((word)s0 << 21) | (word)s;
}
static inline void Cba_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 Cba_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 Cba_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 Cba_NtkPrintDistrib( Cba_Ntk_t * p, int fVerbose )
{
    Vec_Ptr_t * vTypes, * vOccurs;
    Vec_Int_t * vAnds = Vec_IntStart( CBA_BOX_LAST );
    int iRnObj = -1, nCountRange = 0;
    int i, k, s, s0, s1, Type;  word Sign;
    char * pTypeNames[CBA_BOX_LAST];
    Cba_ManCreatePrimMap( pTypeNames );
    // allocate statistics arrays
    vTypes  = Vec_PtrStart( CBA_BOX_LAST );
    vOccurs = Vec_PtrStart( CBA_BOX_LAST );
    for ( i = 0; i < CBA_BOX_LAST; i++ )
        Vec_PtrWriteEntry( vTypes, i, Vec_WrdAlloc(16) );
    for ( i = 0; i < CBA_BOX_LAST; i++ )
        Vec_PtrWriteEntry( vOccurs, i, Vec_WrdAlloc(16) );
    // add nodes
    Cba_NtkForEachObj( p, i )
    {
//        char * pName = Cba_ObjName(p, i);
        Type = Cba_ObjType( p, i );
        if ( Cba_ObjSign(p, i) > 0x1FFFFF )
            printf( "Object %6d has range %d, which is reduced to %d in the statistics.\n", 
                i, Cba_ObjRangeSize(p, i), Cba_ObjRangeSize(p, i) & 0xFFFFF );
        if ( Cba_ObjLeft(p, i) && Cba_ObjRight(p, i) )
        {
            if ( iRnObj == -1 )
                iRnObj = 1;
            nCountRange++;
        }
        // 0-input types
        if ( Cba_ObjIsPi(p, i) || (Type == CBA_BOX_BUF && Cba_FonIsConst(Cba_ObjFinFon(p, i, 0))) || Type == CBA_BOX_CONCAT )
            Sign = Cba_NtkPrintDistribMakeSign( Cba_ObjSign(p, i), 0, 0 );
        // 1-input types
        else if ( Cba_TypeIsUnary(Type)  )
            Sign = Cba_NtkPrintDistribMakeSign( Cba_ObjSign(p, i), Cba_ObjSign(p, Cba_ObjFinFon(p, i, 0)), 0 );
        // 2-input types (including MUX)
        else if ( Cba_ObjFinNum(p, i) == 1 )
            Sign = Cba_NtkPrintDistribMakeSign( Cba_ObjSign(p, i), Cba_ObjSign(p, Cba_ObjFinFon(p, i, 0)), 0 );
        else
        {
            assert( Cba_ObjFinNum(p, i) >= 2 );
            Sign = Cba_NtkPrintDistribMakeSign( Cba_ObjSign(p, i), Cba_ObjSign(p, Cba_ObjFinFon(p, i, 0)), Cba_ObjSign(p, Cba_ObjFinFon(p, i, 1)) );
        }
        // add to storage
        Cba_NtkPrintDistribAddOne( vTypes, vOccurs, Type, Sign );
        // count the number of AIG nodes
        if ( Type == CBA_BOX_MUX )
            Vec_IntAddToEntry( vAnds, CBA_BOX_MUX,      3 * Cba_ObjRangeSize(p, i) * (Cba_ObjFinNum(p, i) - 2) );
        else if ( Type == CBA_BOX_SHIR )      
            Vec_IntAddToEntry( vAnds, CBA_BOX_SHIR,  Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_SHIRA )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_SHIRA, Cba_ObjRangeSize(p, i) * Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_SHIL )      
            Vec_IntAddToEntry( vAnds, CBA_BOX_SHIL,  Cba_ObjRangeSize(p, i) * Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_SHILA )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_SHILA, Cba_ObjRangeSize(p, i) * Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_ROTR )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_ROTR,  Cba_ObjRangeSize(p, i) * Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_ROTL )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_ROTL,  Cba_ObjRangeSize(p, i) * Abc_MinInt(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Abc_Base2Log(Cba_ObjRangeSize(p, i))) * 3 );
        else if ( Type == CBA_BOX_INV )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_INV, 0 );
        else if ( Type == CBA_BOX_AND )      
            Vec_IntAddToEntry( vAnds, CBA_BOX_AND,         Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_OR )       
            Vec_IntAddToEntry( vAnds, CBA_BOX_OR,          Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_XOR )      
            Vec_IntAddToEntry( vAnds, CBA_BOX_XOR,     3 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_SLICE )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_SLICE, 0 );
        else if ( Type == CBA_BOX_CONCAT )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_CONCAT, 0 );
        else if ( Type == CBA_BOX_LNOT )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_LNOT,        Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 1 );
        else if ( Type == CBA_BOX_LAND )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_LAND,        Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) + Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 1)) - 1 );
        else if ( Type == CBA_BOX_LOR )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_LOR,         Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) + Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 1)) - 1 );
        else if ( Type == CBA_BOX_LXOR )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_LXOR,        Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) + Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 1)) + 1 );
        else if ( Type == CBA_BOX_EQU )     
            Vec_IntAddToEntry( vAnds, CBA_BOX_EQU,      4 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 1 );
        else if ( Type == CBA_BOX_NEQU )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_NEQU,     4 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 1 );
        else if ( Type == CBA_BOX_LTHAN )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_LTHAN,    6 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 6 );
        else if ( Type == CBA_BOX_MTHAN )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_MTHAN,    6 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 6 );
        else if ( Type == CBA_BOX_LETHAN ) 
            Vec_IntAddToEntry( vAnds, CBA_BOX_LETHAN,   6 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 6 );
        else if ( Type == CBA_BOX_METHAN ) 
            Vec_IntAddToEntry( vAnds, CBA_BOX_METHAN,   6 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 6 );
        else if ( Type == CBA_BOX_RAND )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_RAND,         Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 1 );
        else if ( Type == CBA_BOX_ROR )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_ROR,          Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 1 );
        else if ( Type == CBA_BOX_RXOR )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_RXOR,     3 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 3 );
        else if ( Type == CBA_BOX_ADD )       
            Vec_IntAddToEntry( vAnds, CBA_BOX_ADD,      9 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_SUB )       
            Vec_IntAddToEntry( vAnds, CBA_BOX_SUB,      9 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_MUL )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_MUL,      9 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 1)) );
        else if ( Type == CBA_BOX_DIV )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_DIV,     13 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 19 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) + 10 );
        else if ( Type == CBA_BOX_MOD )  
            Vec_IntAddToEntry( vAnds, CBA_BOX_MOD,     13 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 7 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) - 2  );
        else if ( Type == CBA_BOX_POW ) 
            Vec_IntAddToEntry( vAnds, CBA_BOX_POW,     10 * (int)pow(Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)),Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0))) );
        else if ( Type == CBA_BOX_MIN )   
            Vec_IntAddToEntry( vAnds, CBA_BOX_MIN,      4 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) );
        else if ( Type == CBA_BOX_SQRT )    
            Vec_IntAddToEntry( vAnds, CBA_BOX_SQRT,    11 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) / 8 + 5 * Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)) / 2 - 5  );
    }
    if ( nCountRange )
    {
        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", 
            iRnObj, Cba_FonNameStr(p, Cba_ObjFon0(p, iRnObj)), 
            Cba_ObjRangeSize(p, iRnObj), Cba_ObjLeft(p, iRnObj), Cba_ObjRight(p, iRnObj) );
    }
    // print by occurrence
    printf( "ID  :  name  occurrence    and2 (occurrence)<output_range>=<input_range>.<input_range> ...\n" );
    for ( i = 0; i < CBA_BOX_LAST; 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->pDesign->nObjs[i] == 0 )
            continue;
        printf( "%2d  :  %-8s  %6d%8d ", i, pTypeNames[i], p->pDesign->nObjs[i], Vec_IntEntry(vAnds, i) );
        // sort by occurence
        Cba_NtkPrintDistribSortOne( vTypes, vOccurs, i );
        Vec_WrdForEachEntry( vType, Sign, k )
        {
            Cba_NtkPrintDistribFromSign( Sign, &s, &s0, &s1 );
            if ( ((k % 6) == 5 && s1) || ((k % 8) == 7 && !s1) )
                printf( "\n                                " );
            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 Cba_NtkPrintNodes( Cba_Ntk_t * p, int Type )
{
    int i, iFon0, iFon1, Counter = 0;
    char * pTypeNames[CBA_BOX_LAST];
    Cba_ManCreatePrimMap( pTypeNames );
    printf( "Operation %s\n", pTypeNames[Type] );
    Cba_NtkForEachObj( p, i )
    {
        if ( (int)Type != Type )
            continue;
        iFon0 = Cba_ObjFinFon(p, i, 0);
        iFon1 = Cba_ObjFinFon(p, i, 1);

        printf( "%8d  :",      Counter++ );
        printf( "%8d  :  ",    i );
        printf( "%3d%s = ",    Cba_ObjRangeSize(p, i),                      Cba_ObjSigned(p, i) ? "s" : " " );
        printf( "%3d%s  %s ",  Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 0)), Cba_ObjSigned(p, iFon0) ? "s" : " ", pTypeNames[Type] );
        printf( "%3d%s ",      Cba_ObjRangeSize(p, Cba_ObjFinFon(p, i, 1)), Cba_ObjSigned(p, iFon1) ? "s" : " " );
        printf( " :    " );
        printf( "%-12s =  ",   Cba_ObjNameStr(p, i) );
        printf( "%-12s  %s  ", Cba_FonIsConst(iFon0) ? Cba_NtkConst(p, Cba_FonConst(iFon0)) : Cba_FonNameStr(p, iFon0), pTypeNames[Type] );
        printf( "%-12s ",      Cba_FonIsConst(iFon1) ? Cba_NtkConst(p, Cba_FonConst(iFon1)) : Cba_FonNameStr(p, iFon1) );
        printf( "\n" );
    }
}
void Cba_NtkPrintStatsFull( Cba_Ntk_t * p, int fDistrib, int fVerbose )
{
    int i;
    char * pTypeNames[CBA_BOX_LAST];
    Cba_ManCreatePrimMap( pTypeNames );
    printf( "%-20s : ",        Cba_NtkName(p) );
    printf( "PI = %4d  ",      Cba_NtkPiNum(p) );
    printf( "PO = %4d  ",      Cba_NtkPoNum(p) );
    printf( "FF = %4d  ",      Cba_NtkBoxSeqNum(p) );
    printf( "Obj = %6d  ",     Cba_NtkObjNum(p) );
    printf( "Mem = %.3f MB",   1.0*Cba_NtkMemory(p)/(1<<20) );
    printf( "\n" );
    if ( fDistrib )
    {
        Cba_NtkPrintDistrib( p, fVerbose );
        return;
    }
    if ( !fVerbose )
        return;
    printf( "Node type statistics:\n" );
    for ( i = 1; i < CBA_BOX_LAST; i++ )
    {
        if ( !p->pDesign->nObjs[i] )
            continue;
        if ( p->pDesign->nAnds[0] && p->pDesign->nAnds[i] )
            printf( "%2d  :  %-8s  %6d  %7.2f %%\n", i, pTypeNames[i], p->pDesign->nObjs[i], 100.0*p->pDesign->nAnds[i]/p->pDesign->nAnds[0] );
        else
            printf( "%2d  :  %-8s  %6d\n", i, pTypeNames[i], p->pDesign->nObjs[i] );
    }
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_NtkCollectDistrib( Cba_Ntk_t * p, int * pCounts, int * pUserCounts )
{
    int i;
    Cba_NtkForEachBox( p, i )
        if ( Cba_ObjIsBoxUser(p, i) )
            pUserCounts[Cba_ObjNtkId(p, i)]++;
        else
            pCounts[Cba_ObjType(p, i)]++;
}

void Cba_NtkPrintDistribStat( Cba_Ntk_t * p, int * pCounts, int * pUserCounts )
{
    Cba_Ntk_t * pNtk; int i;
    printf( "Primitives (%d):\n", Cba_NtkBoxPrimNum(p) );
    for ( i = 0; i < CBA_BOX_LAST; i++ )
        if ( pCounts[i] )
            printf( "%-20s = %5d\n", Cba_NtkTypeName(p, i), pCounts[i] );
    printf( "User hierarchy (%d):\n", Cba_NtkBoxUserNum(p) );
    Cba_ManForEachNtk( p->pDesign, pNtk, i )
        if ( pUserCounts[i] )
            printf( "%-20s = %5d\n", Cba_NtkName(pNtk), pUserCounts[i] );
}
void Cba_NtkPrintDistribOld( Cba_Ntk_t * p )
{
    int pCounts[CBA_BOX_LAST] = {0};
    int * pUserCounts = ABC_CALLOC( int, Cba_ManNtkNum(p->pDesign) + 1 );
    Cba_ManCreatePrimMap( p->pDesign->pTypeNames );
    Cba_NtkCollectDistrib( p, pCounts, pUserCounts );
    Cba_NtkPrintDistribStat( p, pCounts, pUserCounts );
    ABC_FREE( pUserCounts );
}

void Cba_ManPrintDistribStat( Cba_Man_t * p, int * pCounts, int * pUserCounts )
{
    Cba_Ntk_t * pNtk = Cba_ManRoot(p); int i;
    printf( "Primitives:\n" );
    for ( i = 0; i < CBA_BOX_LAST; i++ )
        if ( pCounts[i] )
            printf( "%-20s = %5d\n", Cba_NtkTypeName(pNtk, i), pCounts[i] );
    printf( "User hierarchy:\n" );
    Cba_ManForEachNtk( p, pNtk, i )
        if ( pUserCounts[i] )
            printf( "%-20s = %5d\n", Cba_NtkName(pNtk), pUserCounts[i] );
}
void Cba_ManPrintDistrib( Cba_Man_t * p )
{
    Cba_Ntk_t * pNtk; int i;
    int pCounts[CBA_BOX_LAST] = {0};
    int * pUserCounts = ABC_CALLOC( int, Cba_ManNtkNum(p) + 1 );
    Cba_ManCreatePrimMap( p->pTypeNames );
    Cba_ManForEachNtk( p, pNtk, i )
        Cba_NtkCollectDistrib( pNtk, pCounts, pUserCounts );
    Cba_ManPrintDistribStat( p, pCounts, pUserCounts );
    ABC_FREE( pUserCounts );
}

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

  Synopsis    [Order objects by box type and then by name.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
// compares two numbers with the first mismatching char in i-th position
static inline int Cba_CharIsDigit( char c ) { return c >= '0' && c <= '9'; }
int Cba_StrCmpInt( char * p1, char * p2, int i )
{
    // check if one of the mismatching chars is a digit
    if ( Cba_CharIsDigit(p1[i]) || Cba_CharIsDigit(p2[i]) )
    {
        // if previous (equal) char was a digit or if this is first digit on both sides, scroll back
        if ( (i > 0 && Cba_CharIsDigit(p1[i-1])) || (Cba_CharIsDigit(p1[i]) && Cba_CharIsDigit(p2[i])) )
        {
            int Num1, Num2;
            // find the first digit
            for ( --i; i >= 0; i-- )
                if ( !Cba_CharIsDigit(p1[i]) )
                    break;
            i++;
            // current char is digit
            assert( Cba_CharIsDigit(p1[i]) );
            assert( Cba_CharIsDigit(p2[i]) );
            // previous char does not exist or is not a digit
            assert( i == 0 || !Cba_CharIsDigit(p1[i-1]) );
            assert( i == 0 || !Cba_CharIsDigit(p2[i-1]) );
            // compare numbers
            Num1 = atoi( p1 + i );
            Num2 = atoi( p2 + i );
            if ( Num1 < Num2 )
                return -1;
            if ( Num1 > Num2 ) 
                return 1;
            assert( 0 );
            return 0;
        }
    }
    // compare as usual
    if ( p1[i] < p2[i] )
        return -1;
    if ( p1[i] > p2[i] )
        return 1;
    assert( 0 );
    return 0;
}
int Cba_StrCmp( char ** pp1, char ** pp2 )
{
    char * p1 = *pp1;
    char * p2 = *pp2; int i;
    for ( i = 0; p1[i] && p2[i]; i++ )
        if ( p1[i] != p2[i] )
            return Cba_StrCmpInt( p1, p2, i );
    assert( !p1[i] || !p2[i] );
    return Cba_StrCmpInt( p1, p2, i );
}
void Cba_NtkObjOrder( Cba_Ntk_t * p, Vec_Int_t * vObjs, Vec_Int_t * vNameIds )
{
    char Buffer[1000], * pName;
    Vec_Ptr_t * vNames;
    int i, iObj;
    if ( Vec_IntSize(vObjs) < 2 )
        return;
    vNames = Vec_PtrAlloc( Vec_IntSize(vObjs) );
    Vec_IntForEachEntry( vObjs, iObj, i )
    {
        char * pTypeName = Cba_NtkTypeName( p, Cba_ObjType(p, iObj) );
        char * pInstName = vNameIds ? Cba_NtkStr(p, Vec_IntEntry(vNameIds, i)) : Cba_ObjNameStr(p, iObj);
        sprintf( Buffer, "%s_%s_%d", pTypeName, pInstName, iObj );
        Vec_PtrPush( vNames, Abc_UtilStrsav(Buffer) );
    }
    // print before
//    Vec_PtrForEachEntry( char *, vNames, pName, i )
//        printf( "%s \n", pName );
//    printf( "\n" );
    // do the sorting
    Vec_PtrSort( vNames, (int (*)(void))Cba_StrCmp );
    // print after
//    Vec_PtrForEachEntry( char *, vNames, pName, i )
//        printf( "%s \n", pName );
//    printf( "\n" );
    // reload in a new order
    Vec_IntClear( vObjs );
    Vec_PtrForEachEntry( char *, vNames, pName, i )
        Vec_IntPush( vObjs, atoi(strrchr(pName, '_')+1) );
    Vec_PtrFreeFree( vNames );
}


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

  Synopsis    [Returns the number of CI fons.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cba_NtkCiFonNum( Cba_Ntk_t * p )
{
    int i, iObj, Count = Cba_NtkPiNum(p);
    Cba_NtkForEachBoxSeq( p, iObj, i )
        Count += Cba_ObjFonNum(p, iObj);
    return Count;  
}
int Cba_NtkCoFinNum( Cba_Ntk_t * p )
{
    int i, iObj, Count = Cba_NtkPoNum(p);
    Cba_NtkForEachBoxSeq( p, iObj, i )
        Count += Cba_ObjFinNum(p, iObj);
    return Count;  
}

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

  Synopsis    [Returns 1 if the manager is in the topo order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cba_NtkIsTopoOrder( Cba_Ntk_t * p )
{
    int i, k, iObj, iFin, iFanin, fTopo = 1;
    Vec_Bit_t * vVisited = Vec_BitStart( Cba_NtkObjNum(p) + 1 );
    // mark PIs and seq objects
    Cba_NtkForEachPi( p, iObj, i )
        Vec_BitWriteEntry( vVisited, iObj, 1 );
    Cba_NtkForEachBoxSeq( p, iObj, i )
        Vec_BitWriteEntry( vVisited, iObj, 1 );
    // visit combinational nodes
    Cba_NtkForEachBox( p, iObj )
        if ( !Cba_ObjIsSeq(p, iObj) )
        {
            Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
                if ( !Vec_BitEntry(vVisited, iFanin) )
                    fTopo = 0;
            if ( !fTopo )
                break;
            Vec_BitWriteEntry( vVisited, iObj, 1 );
        }
    // visit POs and seq objects
    if ( fTopo )
    Cba_NtkForEachPo( p, iObj, i )
    {
        Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
            if ( !Vec_BitEntry(vVisited, iFanin) )
                fTopo = 0;
        if ( !fTopo )
            break;
    }
    if ( fTopo )
    Cba_NtkForEachBoxSeq( p, iObj, i )
    {
        Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
            if ( !Vec_BitEntry(vVisited, iFanin) )
                fTopo = 0;
        if ( !fTopo )
            break;
    }
    Vec_BitFree( vVisited );
    return fTopo;
}
int Cba_ManIsTopoOrder( Cba_Man_t * p )
{
    Cba_Ntk_t * pNtk; int i;
    Cba_ManForEachNtk( p, pNtk, i )
        if ( !Cba_NtkIsTopoOrder(pNtk) )
            return 0;
    return 1;
}

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

  Synopsis    [Collects user boxes in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cba_NtkCheckComboLoop_rec( Cba_Ntk_t * p, int iObj )
{
    int k, iFin, iFanin;
    if ( Cba_ObjIsPi(p, iObj) )
        return 1;
    if ( Cba_ObjCopy(p, iObj) == 1 ) // visited
        return 1;
    if ( Cba_ObjCopy(p, iObj) == 0 ) // loop
        return 0;
    Cba_ObjSetCopy( p, iObj, 0 );
    Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
        if ( !Cba_ObjIsSeq(p, iFanin) )
            if ( !Cba_NtkCheckComboLoop_rec( p, iFanin ) )
                return 0;
    //Cba_ObjSetCopy( p, iObj, 1 );
    Vec_IntSetEntry( &p->vObjCopy, iObj, 1 );
    return 1;
}
int Cba_NtkCheckComboLoop( Cba_Ntk_t * p )
{
    int iObj;
    Cba_NtkCleanObjCopies( p ); // -1 = not visited; 0 = on the path; 1 = finished
    Cba_NtkForEachBox( p, iObj )
        if ( !Cba_ObjIsSeq(p, iObj) )
            if ( !Cba_NtkCheckComboLoop_rec( p, iObj ) )
            {
                printf( "Cyclic dependency of user boxes is detected.\n" );
                return 0;
            }
    return 1;
}

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

  Synopsis    [Find one missing object.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Cba_NtkFindMissing( Vec_Int_t * vObjs, int nObjs )
{
    Vec_Int_t * vMap = Vec_IntStartFull( nObjs + 1 );
    int i, iObj;
    Vec_IntForEachEntry( vObjs, iObj, i )
        Vec_IntWriteEntry( vMap, iObj, i );
    Vec_IntForEachEntryStart( vMap, i, iObj, 1 )
        if ( i == -1 )
        {
            Vec_IntFree( vMap );
            return iObj;
        }
    Vec_IntFree( vMap );
    return -1;
}

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

  Synopsis    [Collect nodes in the DFS order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_NtkCollectDfs_rec( Cba_Ntk_t * p, int iObj, Vec_Int_t * vObjs )
{
    int iFin, iFanin, k;
    if ( !Cba_ObjCopy(p, iObj) )
        return;
    Cba_ObjSetCopy( p, iObj, 0 );
    Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
        Cba_NtkCollectDfs_rec( p, iFanin, vObjs );
    Vec_IntPush( vObjs, iObj );
}
Vec_Int_t * Cba_NtkCollectDfs( Cba_Ntk_t * p )
{
    int i, k, iObj, iFin, iFanin;
    Vec_Int_t * vObjs = Vec_IntAlloc( Cba_NtkObjNum(p) );
    // collect PIs and seq boxes
    Cba_NtkForEachPi( p, iObj, i )
        Vec_IntPush( vObjs, iObj );
    Cba_NtkForEachBoxSeq( p, iObj, i )
        Vec_IntPush( vObjs, iObj );
    // prepare leaves
    Cba_NtkCleanObjCopies( p );
    Vec_IntForEachEntry( vObjs, iObj, i )
        Cba_ObjSetCopy( p, iObj, 0 );
    // collect internal
    Cba_NtkForEachPo( p, iObj, i )
        Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
            Cba_NtkCollectDfs_rec( p, iFanin, vObjs );
    Cba_NtkForEachBoxSeq( p, iObj, i )
        Cba_ObjForEachFinFaninReal( p, iObj, iFin, iFanin, k )
            Cba_NtkCollectDfs_rec( p, iFanin, vObjs );
    // collect POs
    Cba_NtkForEachPo( p, iObj, i )
        Vec_IntPush( vObjs, iObj );
    assert( Vec_IntSize(vObjs) <= Cba_NtkObjNum(p) );
    if ( Vec_IntSize(vObjs) != Cba_NtkObjNum(p) )
    {
        int iObj = Cba_NtkFindMissing( vObjs, Cba_NtkObjNum(p) );
        printf( "Warning: DSF ordering for module \"%s\" collected %d out of %d objects.\n", Cba_NtkName(p), Vec_IntSize(vObjs), Cba_NtkObjNum(p) );
        printf( "         For example, object %d with name \"%s\" is not reachable from outputs.\n", 
            iObj, iObj > 0 ? Cba_FonNameStr( p, Cba_ObjFon0(p, iObj) ) : "<unknown>" );
    }
    return vObjs;
}


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

  Synopsis    [Count number of objects after collapsing.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_ManGetClpStats_rec( Cba_Ntk_t * p, int * pCountN, int * pCountI, int * pCountO )
{
    int iObj, Id = Cba_NtkId(p);
    if ( pCountN[Id] >= 0 )
        return;
    pCountN[Id] = pCountI[Id] = pCountO[Id] = 0;
    Cba_NtkForEachObj( p, iObj )
        if ( Cba_ObjIsBoxPrim(p, iObj) )
        {
            pCountN[Id] += 1;
            pCountI[Id] += Cba_ObjFinNum(p, iObj);
            pCountO[Id] += Cba_ObjFonNum(p, iObj);
        }
        else if ( Cba_ObjIsBoxUser(p, iObj) )
        {
            int NtkId = Cba_ObjNtkId(p, iObj);
            Cba_ManGetClpStats_rec( Cba_ObjNtk(p, iObj), pCountN, pCountI, pCountO );
            pCountN[Id] += pCountN[NtkId] + Cba_ObjFonNum(p, iObj);
            pCountI[Id] += pCountI[NtkId] + Cba_ObjFonNum(p, iObj);
            pCountO[Id] += pCountO[NtkId] + Cba_ObjFonNum(p, iObj);
        }
}
void Cba_ManGetClpStats( Cba_Man_t * p, int * nObjs, int * nFins, int * nFons )
{
    int * pCountN = ABC_FALLOC( int, Cba_ManNtkNum(p) + 1 );
    int * pCountI = ABC_FALLOC( int, Cba_ManNtkNum(p) + 1 );
    int * pCountO = ABC_FALLOC( int, Cba_ManNtkNum(p) + 1 );
    Cba_Ntk_t * pRoot = Cba_ManRoot(p);
    Cba_ManGetClpStats_rec( pRoot, pCountN, pCountI, pCountO );
    *nObjs = Cba_NtkPioNum(pRoot) + pCountN[Cba_NtkId(pRoot)];
    *nFins = Cba_NtkPoNum(pRoot)  + pCountI[Cba_NtkId(pRoot)];
    *nFons = Cba_NtkPiNum(pRoot)  + pCountO[Cba_NtkId(pRoot)];
    ABC_FREE( pCountN );
    ABC_FREE( pCountI );
    ABC_FREE( pCountO );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_NtkCollapse_rec( Cba_Ntk_t * pNew, Cba_Ntk_t * p, Vec_Int_t * vSigs )
{
    int i, iObj, iObjNew, iFin, iFon;
    Cba_NtkCleanObjCopies( p );
    Cba_NtkCleanFonCopies( p );
    // set PI copies
    assert( Vec_IntSize(vSigs) == Cba_NtkPiNum(p) );
    Cba_NtkForEachPiFon( p, iObj, iFon, i )
        Cba_FonSetCopy( p, iFon, Vec_IntEntry(vSigs, i) );
    // duplicate primitives and create buffers for user instances
    Cba_NtkForEachObj( p, iObj )
        if ( Cba_ObjIsBoxPrim( p, iObj ) )
        {
            iObjNew = Cba_ObjDup( pNew, p, iObj );
            Cba_ObjForEachFon( p, iObj, iFon, i )
                Cba_FonSetCopy( p, iFon, Cba_ObjFon(pNew, iObjNew, i) );
            if ( Cba_ObjAttr(p, iObj) )
                Cba_ObjSetAttrs( pNew, iObjNew, Cba_ObjAttrArray(p, iObj), Cba_ObjAttrSize(p, iObj) );
        }
        else if ( Cba_ObjIsBoxUser( p, iObj ) )
        {
            Cba_ObjForEachFon( p, iObj, iFon, i )
            {
                iObjNew = Cba_ObjAlloc( pNew, CBA_BOX_BUF, 1, 1 );
                Cba_FonSetCopy( p, iFon, Cba_ObjFon0(pNew, iObjNew) );
            }
        }
    // connect primitives and collapse user instances
    Cba_NtkForEachObj( p, iObj )
        if ( Cba_ObjIsBoxPrim( p, iObj ) )
        {
            iObjNew = Cba_ObjCopy( p, iObj );
            Cba_ObjForEachFinFon( p, iObj, iFin, iFon, i )
                Cba_ObjSetFinFon( pNew, iObjNew, i, Cba_FonCopy(p, iFon) );
        }
        else if ( Cba_ObjIsBoxUser( p, iObj ) )
        {
            Vec_IntClear( vSigs );
            Cba_ObjForEachFinFon( p, iObj, iFin, iFon, i )
                Vec_IntPush( vSigs, Cba_FonCopy(p, iFon) );
            assert( Vec_IntSize(vSigs) == Cba_ObjFinNum(p, iObj) );
            Cba_NtkCollapse_rec( pNew, Cba_ObjNtk(p, iObj), vSigs );
            assert( Vec_IntSize(vSigs) == Cba_ObjFonNum(p, iObj) );
            Cba_ObjForEachFon( p, iObj, iFon, i )
            {
                iObjNew = Cba_FonObj( pNew, Cba_FonCopy(p, iFon) ); // buffer
                Cba_ObjSetFinFon( pNew, iObjNew, 0, Vec_IntEntry(vSigs, i) );
            }
        }
    // collect POs
    Vec_IntClear( vSigs );
    Cba_NtkForEachPoDriverFon( p, iObj, iFon, i )
        Vec_IntPush( vSigs, Cba_FonCopy(p, iFon) );
}
Cba_Man_t * Cba_ManCollapse( Cba_Man_t * p )
{
    Cba_Man_t * pNew  = Cba_ManAlloc( p->pSpec, 1, Abc_NamRef(p->pStrs), Abc_NamRef(p->pFuns), Abc_NamStart(100, 24), Hash_IntManRef(p->vHash) );
    Cba_Ntk_t * pRoot = Cba_ManRoot( p ), * pRootNew;
    Vec_Int_t * vSigs = Vec_IntAlloc( 1000 );
    int i, iObj, iObjNew, iFon, nObjs = 0, nFins = 0, nFons = 0;
    Cba_ManDupTypeNames( pNew, p );
    Cba_ManGetClpStats( p, &nObjs, &nFins, &nFons    );
    pRootNew = Cba_NtkAlloc( pNew, Cba_NtkNameId(pRoot), Cba_NtkPiNum(pRoot), Cba_NtkPoNum(pRoot), nObjs, nFins, nFons );
    Cba_NtkAdd( pNew, pRootNew );
    if ( Cba_NtkHasObjNames(pRoot) )
        Cba_NtkCleanObjNames( pRootNew );
    if ( Cba_NtkHasFonNames(pRoot) )
        Cba_NtkCleanFonNames( pRootNew );
    if ( Cba_NtkHasObjAttrs(pRoot) )
        Cba_NtkCleanObjAttrs( pRootNew );
    if ( Cba_ObjAttr(pRoot, 0) )
        Cba_ObjSetAttrs( pRootNew, 0, Cba_ObjAttrArray(pRoot, 0), Cba_ObjAttrSize(pRoot, 0) );
    Cba_NtkCleanObjCopies( pRoot );
    Cba_NtkForEachPiFon( pRoot, iObj, iFon, i )
    {
        iObjNew = Cba_ObjDup( pRootNew, pRoot, iObj );
        Vec_IntPush( vSigs, Cba_ObjFon0(pRootNew, iObjNew) );
        if ( Cba_NtkHasObjNames(pRoot) )
            Cba_ObjSetName( pRootNew, iObjNew, Cba_ObjName(pRoot, iObj) );
        if ( Cba_NtkHasFonNames(pRoot) )
            Cba_FonSetName( pRootNew, Cba_ObjFon0(pRootNew, iObjNew), Cba_FonName(pRoot, iFon) );
        if ( Cba_ObjAttr(pRoot, iObj) )
            Cba_ObjSetAttrs( pRootNew, iObjNew, Cba_ObjAttrArray(pRoot, iObj), Cba_ObjAttrSize(pRoot, iObj) );
    }
    assert( Vec_IntSize(vSigs) == Cba_NtkPiNum(pRoot) );
    Cba_NtkCollapse_rec( pRootNew, pRoot, vSigs );
    assert( Vec_IntSize(vSigs) == Cba_NtkPoNum(pRoot) );
    Cba_NtkForEachPoDriverFon( pRoot, iObj, iFon, i )
    {
        iObjNew = Cba_ObjDup( pRootNew, pRoot, iObj );
        Cba_ObjSetFinFon( pRootNew, iObjNew, 0, Vec_IntEntry(vSigs, i) );
        if ( Cba_NtkHasObjNames(pRoot) )
            Cba_ObjSetName( pRootNew, iObjNew, Cba_ObjName(pRoot, iObj) );
        if ( Cba_NtkHasFonNames(pRoot) )
            Cba_FonSetName( pRootNew, Vec_IntEntry(vSigs, i), Cba_FonName(pRoot, iFon) );
        if ( Cba_ObjAttr(pRoot, iObj) )
            Cba_ObjSetAttrs( pRootNew, iObjNew, Cba_ObjAttrArray(pRoot, iObj), Cba_ObjAttrSize(pRoot, iObj) );
    }
    Vec_IntFree( vSigs );
    assert( Cba_NtkObjNum(pRootNew) == Cba_NtkObjNumAlloc(pRootNew) );
    assert( Cba_NtkFinNum(pRootNew) == Cba_NtkFinNumAlloc(pRootNew) );
    assert( Cba_NtkFonNum(pRootNew) == Cba_NtkFonNumAlloc(pRootNew) );
    // create internal node names
    Cba_NtkMissingFonNames( pRootNew, "m" );
    //Cba_NtkPrepareSeq( pRootNew );
    return pNew;
}



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

  Synopsis    [Performs the reverse of collapsing.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Cba_NtkCollectInFons( Cba_Ntk_t * p, Vec_Int_t * vObjs )
{
    Vec_Int_t * vFons = Vec_IntAlloc( 100 );
    Vec_Bit_t * vVisFons = Vec_BitStart( Cba_NtkFonNum(p) + 1 );
    int i, k, iObj, iFin, iFon, Entry;
    // mark fanin fons
    Vec_IntForEachEntry( vObjs, iObj, i )
        Cba_ObjForEachFinFon( p, iObj, iFin, iFon, k )
            if ( iFon > 0 )
                Vec_BitWriteEntry( vVisFons, iFon, 1 );
    // unmark internal fons
    Vec_IntForEachEntry( vObjs, iObj, i )
        Cba_ObjForEachFon( p, iObj, iFon, k )
            Vec_BitWriteEntry( vVisFons, iFon, 0 );
    // collect fons
    Vec_BitForEachEntry( vVisFons, Entry, iFon )
        if ( Entry )
            Vec_IntPush( vFons, iFon );
    Vec_BitFree( vVisFons );
    return vFons;
}
Vec_Int_t * Cba_NtkCollectOutFons( Cba_Ntk_t * p, Vec_Int_t * vObjs )
{
    Vec_Int_t * vFons = Vec_IntAlloc( 100 );
    Vec_Bit_t * vMapObjs = Vec_BitStart( Cba_NtkObjNum(p) + 1 );
    Vec_Bit_t * vVisFons = Vec_BitStart( Cba_NtkFonNum(p) + 1 );
    int i, k, iObj, iFin, iFon;
    // map objects
    Vec_IntForEachEntry( vObjs, iObj, i )
        Vec_BitWriteEntry( vMapObjs, iObj, 1 );
    // mark those used by non-objects
    Cba_NtkForEachObj( p, iObj )
        if ( !Vec_BitEntry(vMapObjs, iObj) )
            Cba_ObjForEachFinFon( p, iObj, iFin, iFon, k )
                if ( iFon > 0 )
                    Vec_BitWriteEntry( vVisFons, iFon, 1 );
    // collect pointed fons among those in objects
    Vec_IntForEachEntry( vObjs, iObj, i )
        Cba_ObjForEachFon( p, iObj, iFon, k )
            if ( Vec_BitEntry(vVisFons, iFon) )
                Vec_IntPush( vFons, iFon );
    Vec_BitFree( vMapObjs );
    Vec_BitFree( vVisFons );
    return vFons;
}
void Cba_NtkCollectGroupStats( Cba_Ntk_t * p, Vec_Int_t * vObjs, int * pnFins, int * pnFons )
{
    int i, iObj, nFins = 0, nFons = 0;
    Vec_IntForEachEntry( vObjs, iObj, i )
    {
        nFins += Cba_ObjFinNum(p, iObj);
        nFons += Cba_ObjFonNum(p, iObj);
    }
    *pnFins = nFins;
    *pnFons = nFons;
}
void Cba_ManExtractGroupInt( Cba_Ntk_t * pNew, Cba_Ntk_t * p, Vec_Int_t * vObjs, Vec_Int_t * vFonIns, Vec_Int_t * vFonOuts )
{
    int i, k, iObj, iObjNew, iFin, iFon;
    Cba_NtkCleanObjCopies( p );
    Cba_NtkCleanFonCopies( p );
    // create inputs and map fons
    Vec_IntForEachEntry( vFonIns, iFon, i )
    {
        iObjNew = Cba_ObjAlloc( pNew, CBA_OBJ_PI, 0, 1 );
        Cba_FonSetCopy( p, iFon, Cba_ObjFon0(pNew, iObjNew) );
        if ( Cba_NtkHasObjNames(p) )
            Cba_ObjSetName( pNew, iObjNew, Cba_ObjName(p, Cba_FonObj(p, iFon)) );
        if ( Cba_NtkHasFonNames(p) )
            Cba_FonSetName( pNew, Cba_ObjFon0(pNew, iObjNew), Cba_FonName(p, iFon) );

    }
    // create internal
    Vec_IntForEachEntry( vObjs, iObj, i )
    {
        iObjNew = Cba_ObjDup( pNew, p, iObj );
        if ( Cba_NtkHasObjNames(p) )
            Cba_ObjSetName( pNew, iObjNew, Cba_ObjName(p, iObj) );
        Cba_ObjForEachFon( p, iObj, iFon, k )
        {
            Cba_FonSetCopy( p, iFon, Cba_ObjFon(pNew, iObjNew, k) );
            if ( Cba_NtkHasFonNames(p) )
                Cba_FonSetName( pNew, Cba_ObjFon(pNew, iObjNew, k), Cba_FonName(p, iFon) );
        }
    }
    // connect internal
    Vec_IntForEachEntry( vObjs, iObj, i )
    {
        iObjNew = Cba_ObjCopy( p, iObj );
        Cba_ObjForEachFinFon( p, iObj, iFin, iFon, k )
            Cba_ObjSetFinFon( pNew, iObjNew, k, Cba_FonCopy(p, iFon) );
    }
    // create POs
    Vec_IntForEachEntry( vFonOuts, iFon, i )
    {
        iObjNew = Cba_ObjAlloc( pNew, CBA_OBJ_PO, 1, 0 );
        if ( Cba_NtkHasObjNames(p) )
            Cba_ObjSetName( pNew, iObjNew, Cba_FonName(p, iFon) );
        Cba_ObjSetFinFon( pNew, iObjNew, 0, Cba_FonCopy(p, iFon) );
    }
    assert( Cba_NtkObjNum(pNew) == Cba_NtkObjNumAlloc(pNew) );
    assert( Cba_NtkFinNum(pNew) == Cba_NtkFinNumAlloc(pNew) );
    assert( Cba_NtkFonNum(pNew) == Cba_NtkFonNumAlloc(pNew) );
}
Cba_Man_t * Cba_ManExtractGroup( Cba_Man_t * p, Vec_Int_t * vObjs )
{
    Cba_Man_t * pNew  = Cba_ManAlloc( p->pSpec, 1, Abc_NamRef(p->pStrs), Abc_NamRef(p->pFuns), Abc_NamStart(100, 24), Hash_IntManRef(p->vHash) );
    Cba_Ntk_t * pRoot = Cba_ManRoot( p ), * pRootNew;
    Vec_Int_t * vFonIns = Cba_NtkCollectInFons( pRoot, vObjs );
    Vec_Int_t * vFonOuts = Cba_NtkCollectOutFons( pRoot, vObjs );
    int nObjs, nFins, nFons;
    Cba_ManDupTypeNames( pNew, p );
    // collect stats
    Cba_NtkCollectGroupStats( pRoot, vObjs, &nFins, &nFons );
    nObjs  = Vec_IntSize(vObjs) + Vec_IntSize(vFonIns) + Vec_IntSize(vFonOuts);
    nFins += Vec_IntSize(vFonOuts);
    nFons += Vec_IntSize(vFonIns);
    // create network
    pRootNew = Cba_NtkAlloc( pNew, Cba_NtkNameId(pRoot), Vec_IntSize(vFonIns), Vec_IntSize(vFonOuts), nObjs, nFins, nFons );
    Cba_NtkAdd( pNew, pRootNew );
    if ( Cba_NtkHasObjNames(pRoot) )
        Cba_NtkCleanObjNames( pRootNew );
    if ( Cba_NtkHasFonNames(pRoot) )
        Cba_NtkCleanFonNames( pRootNew );
    // add group nodes
    Cba_ManExtractGroupInt( pRootNew, pRoot, vObjs, vFonIns, vFonOuts );
    Cba_NtkMissingFonNames( pRootNew, "b" );
    //Cba_NtkPrepareSeq( pRootNew );
    // cleanup
    Vec_IntFree( vFonIns );
    Vec_IntFree( vFonOuts );
    return pNew;
}


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

  Synopsis    [Derives the design from the GIA manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Cba_NtkInsertGiaLit( Cba_Ntk_t * p, int iLit, Vec_Int_t * vLit2Fon, int fUseXor )
{
    int iObjNew;
    if ( iLit == 0 || iLit == 1 )
        return Cba_FonFromConst(iLit);
    if ( Vec_IntEntry(vLit2Fon, iLit) >= 0 )
        return Vec_IntEntry(vLit2Fon, iLit);
    assert( Abc_LitIsCompl(iLit) );
    assert( Vec_IntEntry(vLit2Fon, Abc_LitNot(iLit)) >= 0 );
    // create inverter
    if ( fUseXor )
    {
        iObjNew = Cba_ObjAlloc( p, CBA_BOX_XOR, 2, 1 );
        Cba_ObjSetFinFon( p, iObjNew, 0, Vec_IntEntry(vLit2Fon, Abc_LitNot(iLit)) );
        Cba_ObjSetFinFon( p, iObjNew, 1, Cba_FonFromConst(1) );
    }
    else
    {
        iObjNew = Cba_ObjAlloc( p, CBA_BOX_INV, 1, 1 );
        Cba_ObjSetFinFon( p, iObjNew, 0, Vec_IntEntry(vLit2Fon, Abc_LitNot(iLit)) );
    }
    // save the result
    Vec_IntWriteEntry( vLit2Fon, iLit, Cba_ObjFon0(p, iObjNew) );
    return Cba_ObjFon0(p, iObjNew);
}
static inline int Cba_NtkInsertGiaObj( Cba_Ntk_t * p, Gia_Man_t * pGia, int iObj, Vec_Int_t * vLit2Fon, int fUseXor )
{
    Gia_Obj_t * pObj = Gia_ManObj( pGia, iObj );
    int iLit0 = Gia_ObjFaninLit0( pObj, iObj );
    int iLit1 = Gia_ObjFaninLit1( pObj, iObj );
    int iFon0 = Cba_NtkInsertGiaLit( p, iLit0, vLit2Fon, fUseXor );
    int iFon1 = Cba_NtkInsertGiaLit( p, iLit1, vLit2Fon, fUseXor );
    int iObjNew;
    if ( Gia_ObjIsMux(pGia, pObj) )
    {
        int iLit2 = Gia_ObjFaninLit2( pGia, iObj );
        int iFon2 = Cba_NtkInsertGiaLit( p, iLit2, vLit2Fon, fUseXor );
        iObjNew = Cba_ObjAlloc( p, CBA_BOX_MUX, 3, 1 );
        Cba_ObjSetFinFon( p, iObjNew, 0, iFon2 );
        Cba_ObjSetFinFon( p, iObjNew, 1, iFon1 );
        Cba_ObjSetFinFon( p, iObjNew, 2, iFon0 );
    }
    else
    {
        assert( Gia_ObjIsAnd(pObj) );
        iObjNew = Cba_ObjAlloc( p, Gia_ObjIsXor(pObj) ? CBA_BOX_XOR : CBA_BOX_AND, 2, 1 );
        Cba_ObjSetFinFon( p, iObjNew, 0, iFon0 );
        Cba_ObjSetFinFon( p, iObjNew, 1, iFon1 );
    }
    Vec_IntWriteEntry( vLit2Fon, Abc_Var2Lit(iObj, 0), Cba_ObjFon0(p, iObjNew) );
    return iObjNew;
}
Cba_Man_t * Cba_ManDeriveFromGia( Gia_Man_t * pGia, int fUseXor )
{
    Cba_Man_t * p = Cba_ManAlloc( pGia->pSpec, 1, NULL, NULL, NULL, NULL );
    Cba_Ntk_t * pNtk = Cba_NtkAlloc( p, Abc_NamStrFindOrAdd(p->pStrs, pGia->pName, NULL), Gia_ManCiNum(pGia), Gia_ManCoNum(pGia), 1000, 2000, 2000 );
    Vec_Int_t * vLit2Fon = Vec_IntStartFull( 2*Gia_ManObjNum(pGia) );
    int i, iObj, iObjNew, NameId, iLit0, iFon0;
    Gia_Obj_t * pObj;
    //Cba_ManPrepareTypeNames( p );
    Cba_NtkAdd( p, pNtk );
    Cba_NtkCleanObjNames( pNtk );
    Gia_ManForEachCiId( pGia, iObj, i )
    {
        NameId = pGia->vNamesIn? Abc_NamStrFindOrAdd(p->pStrs, Vec_PtrEntry(pGia->vNamesIn, i), NULL) : Cba_NtkNewStrId(pNtk, "i%d", i);
        iObjNew = Cba_ObjAlloc( pNtk, CBA_OBJ_PI, 0, 1 );
        Cba_ObjSetName( pNtk, iObjNew, NameId );
        Vec_IntWriteEntry( vLit2Fon, Abc_Var2Lit(iObj, 0), Cba_ObjFon0(pNtk, iObjNew) );
    }
    Gia_ManForEachAndId( pGia, iObj )
        Cba_NtkInsertGiaObj( pNtk, pGia, iObj, vLit2Fon, fUseXor );
    // create inverters if needed
    Gia_ManForEachCoId( pGia, iObj, i )
    {
        pObj = Gia_ManObj( pGia, iObj );
        iLit0 = Gia_ObjFaninLit0( pObj, iObj );
        iFon0 = Cba_NtkInsertGiaLit( pNtk, iLit0, vLit2Fon, fUseXor ); // can be const!
    }
    Gia_ManForEachCoId( pGia, iObj, i )
    {
        pObj = Gia_ManObj( pGia, iObj );
        iLit0 = Gia_ObjFaninLit0( pObj, iObj );
        iFon0 = Cba_NtkInsertGiaLit( pNtk, iLit0, vLit2Fon, fUseXor ); // can be const!
        iObjNew = Cba_ObjAlloc( pNtk, CBA_BOX_BUF, 1, 1 );
        Cba_ObjSetFinFon( pNtk, iObjNew, 0, iFon0 );
        iFon0 = Cba_ObjFon0(pNtk, iObjNew); // non-const fon unique for this output
        NameId = pGia->vNamesOut? Abc_NamStrFindOrAdd(p->pStrs, Vec_PtrEntry(pGia->vNamesOut, i), NULL) : Cba_NtkNewStrId(pNtk, "o%d", i);
        iObjNew = Cba_ObjAlloc( pNtk, CBA_OBJ_PO, 1, 0 );
        Cba_ObjSetName( pNtk, iObjNew, NameId );
        Cba_ObjSetFinFon( pNtk, iObjNew, 0, iFon0 );
    }
    Cba_NtkCleanFonNames( pNtk );
    Cba_NtkCreateFonNames( pNtk, "a" );
    Vec_IntFree( vLit2Fon );
    return p;
}


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

  Synopsis    [Inserts the network into the root module instead of objects.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_NtkInsertGroup( Cba_Ntk_t * p, Vec_Int_t * vObjs, Cba_Ntk_t * pSyn )
{
    Vec_Int_t * vFonIns  = Cba_NtkCollectInFons( p, vObjs );
    Vec_Int_t * vFonOuts = Cba_NtkCollectOutFons( p, vObjs );
    int k, iObj, iObjNew, iFin, iFon;
    assert( Cba_NtkPiNum(pSyn) == Vec_IntSize(vFonIns) );
    assert( Cba_NtkPoNum(pSyn) == Vec_IntSize(vFonOuts) );
    // mark AIG with the input fons
    Cba_NtkCleanFonCopies( pSyn );
    Cba_NtkForEachPiFon( pSyn, iObj, iFon, k )
        Cba_FonSetCopy( pSyn, iFon, Vec_IntEntry(vFonIns, k) );
    Vec_IntFree( vFonIns );
    // build up internal nodes
    Cba_NtkCleanObjCopies( pSyn );
    Cba_NtkForEachBox( pSyn, iObj )
    {
        iObjNew = Cba_ObjDup( p, pSyn, iObj );
        Cba_ObjForEachFon( pSyn, iObj, iFon, k )
            Cba_FonSetCopy( pSyn, iFon, Cba_ObjFon(p, iObjNew, k) );
    }
    // connect internal nodes
    Cba_NtkForEachBox( pSyn, iObj )
    {
        iObjNew = Cba_ObjCopy( pSyn, iObj );
        Cba_ObjForEachFinFon( pSyn, iObj, iFin, iFon, k )
            Cba_ObjSetFinFon( p, iObjNew, k, Cba_FonCopy(pSyn, iFon) );
    }
    // connect output fons
    Cba_NtkCleanFonCopies( p );
    if ( Cba_NtkHasFonNames(p) )
        Vec_IntFillExtra( &p->vFonName, Cba_NtkFonNum(p) + 1, 0 );
    Cba_NtkForEachPoDriverFon( pSyn, iObj, iFon, k )
    {
        assert( Cba_FonIsReal(Cba_FonCopy(pSyn, iFon)) );
        Cba_FonSetCopy( p, Vec_IntEntry(vFonOuts, k), Cba_FonCopy(pSyn, iFon) );
        // transfer names
        if ( Cba_NtkHasFonNames(p) )
        {
            Cba_FonSetName( p, Cba_FonCopy(pSyn, iFon), Cba_FonName(p, Vec_IntEntry(vFonOuts, k)) );
            Cba_FonCleanName( p, Vec_IntEntry(vFonOuts, k) );
        }
    }
    Vec_IntFree( vFonOuts );
    // delete nodes
//    Vec_IntForEachEntry( vObjs, iObj, k )
//        Cba_ObjDelete( p, iObj );
    // update fins pointing to output fons to point to the new fons
    Cba_NtkForEachFinFon( p, iFon, iFin )
        if ( Cba_FonIsReal(iFon) && Cba_FonCopy(p, iFon) )
            Cba_PatchFinFon( p, iFin, Cba_FonCopy(p, iFon) );
    Cba_NtkMissingFonNames( p, "j" );
/*
    // duplicate in DFS order
    pNew = Cba_NtkDupOrder( p->pDesign, p, Cba_NtkCollectDfs );
    Cba_NtkDupAttrs( pNew, p );
    // replace "p" with "pNew"
    Cba_NtkUpdate( Cba_NtkMan(p), pNew ); // removes "p"
    return pNew;
*/
}
Cba_Man_t * Cba_ManInsertGroup( Cba_Man_t * p, Vec_Int_t * vObjs, Cba_Ntk_t * pSyn )
{
    Cba_NtkInsertGroup( Cba_ManRoot(p), vObjs, pSyn );
    Cba_NtkCheckComboLoop( Cba_ManRoot(p) );
    return Cba_ManDup( p, Cba_NtkCollectDfs );
}

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


ABC_NAMESPACE_IMPL_END

