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

  FileName    [abcDsd.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [Technology dependent sweep.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "base/abc/abc.h"
#include "base/main/main.h"
#include "proof/fraig/fraig.h"

#ifdef ABC_USE_CUDD
#include "bdd/extrab/extraBdd.h"
#endif

ABC_NAMESPACE_IMPL_START

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

static void           Abc_NtkFraigSweepUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk );
static stmm_table *   Abc_NtkFraigEquiv( Abc_Ntk_t * pNtk, int fUseInv, int fVerbose, int fVeryVerbose );
static void           Abc_NtkFraigTransform( Abc_Ntk_t * pNtk, stmm_table * tEquiv, int fUseInv, int fVerbose );
static void           Abc_NtkFraigMergeClassMapped( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose );
static void           Abc_NtkFraigMergeClass( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose );
static int            Abc_NodeDroppingCost( Abc_Obj_t * pNode );

static int            Abc_NtkReduceNodes( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes );
static void           Abc_NodeSweep( Abc_Obj_t * pNode, int fVerbose );

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

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

  Synopsis    [Sweping functionally equivalence nodes.]

  Description [Removes gates with equivalent functionality. Works for 
  both technology-independent and mapped networks. If the flag is set, 
  allows adding inverters at the gate outputs.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkFraigSweep( Abc_Ntk_t * pNtk, int fUseInv, int fExdc, int fVerbose, int fVeryVerbose )
{
    Fraig_Params_t Params;
    Abc_Ntk_t * pNtkAig;
    Fraig_Man_t * pMan;
    stmm_table * tEquiv;
    Abc_Obj_t * pObj;
    int i, fUseTrick;

    assert( !Abc_NtkIsStrash(pNtk) );

    // save gate assignments
    fUseTrick = 0;
    if ( Abc_NtkIsMappedLogic(pNtk) )
    {
        fUseTrick = 1;
        Abc_NtkForEachNode( pNtk, pObj, i )
            pObj->pNext = (Abc_Obj_t *)pObj->pData;
    }
    // derive the AIG
    pNtkAig = Abc_NtkStrash( pNtk, 0, 1, 0 );
    // reconstruct gate assignments
    if ( fUseTrick )
    {
//        extern void * Abc_FrameReadLibGen(); 
        Hop_ManStop( (Hop_Man_t *)pNtk->pManFunc );
        pNtk->pManFunc = Abc_FrameReadLibGen();
        pNtk->ntkFunc = ABC_FUNC_MAP;
        Abc_NtkForEachNode( pNtk, pObj, i )
            pObj->pData = pObj->pNext, pObj->pNext = NULL;
    }

    // perform fraiging of the AIG
    Fraig_ParamsSetDefault( &Params );
    Params.fInternal = 1;
    pMan = (Fraig_Man_t *)Abc_NtkToFraig( pNtkAig, &Params, 0, 0 );   
    // cannot use EXDC with FRAIG because it can create classes of equivalent FRAIG nodes
    // with representative nodes that do not correspond to the nodes with the current network

    // update FRAIG using EXDC
    if ( fExdc )
    {
        if ( pNtk->pExdc == NULL )
            printf( "Warning: Networks has no EXDC.\n" );
        else
            Abc_NtkFraigSweepUsingExdc( pMan, pNtk );
    }
    // assign levels to the nodes of the network
    Abc_NtkLevel( pNtk );

    // collect the classes of equivalent nets
    tEquiv = Abc_NtkFraigEquiv( pNtk, fUseInv, fVerbose, fVeryVerbose );

    // transform the network into the equivalent one
    Abc_NtkFraigTransform( pNtk, tEquiv, fUseInv, fVerbose );
    stmm_free_table( tEquiv );

    // free the manager
    Fraig_ManFree( pMan );
    Abc_NtkDelete( pNtkAig );

    // cleanup the dangling nodes
    if ( Abc_NtkHasMapping(pNtk) )
        Abc_NtkCleanup( pNtk, fVerbose );
    else
        Abc_NtkSweep( pNtk, fVerbose );

    // check
    if ( !Abc_NtkCheck( pNtk ) )
    {
        printf( "Abc_NtkFraigSweep: The network check has failed.\n" );
        return 0;
    }
    return 1;
}

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

  Synopsis    [Sweep the network using EXDC.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkFraigSweepUsingExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk )
{
    Fraig_Node_t * gNodeExdc, * gNode, * gNodeRes;
    Abc_Obj_t * pNode, * pNodeAig;
    int i;
    extern Fraig_Node_t * Abc_NtkToFraigExdc( Fraig_Man_t * pMan, Abc_Ntk_t * pNtk, Abc_Ntk_t * pNtkExdc );

    assert( pNtk->pExdc );
    // derive FRAIG node representing don't-cares in the EXDC network
    gNodeExdc = Abc_NtkToFraigExdc( pMan, pNtk, pNtk->pExdc );
    // update the node pointers
    Abc_NtkForEachNode( pNtk, pNode, i )
    {
        // skip the constant input nodes
        if ( Abc_ObjFaninNum(pNode) == 0 )
            continue;
        // get the strashed node
        pNodeAig = pNode->pCopy;
        // skip the dangling nodes
        if ( pNodeAig == NULL )
            continue;
        // get the FRAIG node
        gNode = Fraig_NotCond( Abc_ObjRegular(pNodeAig)->pCopy, (int)Abc_ObjIsComplement(pNodeAig) );
        // perform ANDing with EXDC
        gNodeRes = Fraig_NodeAnd( pMan, gNode, Fraig_Not(gNodeExdc) );
        // write the node back
        Abc_ObjRegular(pNodeAig)->pCopy = (Abc_Obj_t *)Fraig_NotCond( gNodeRes, (int)Abc_ObjIsComplement(pNodeAig) );
    }
}

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

  Synopsis    [Collects equivalence classes of node in the network.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
stmm_table * Abc_NtkFraigEquiv( Abc_Ntk_t * pNtk, int fUseInv, int fVerbose, int fVeryVerbose )
{
    Abc_Obj_t * pList, * pNode, * pNodeAig;
    Fraig_Node_t * gNode;
    Abc_Obj_t ** ppSlot;
    stmm_table * tStrash2Net;
    stmm_table * tResult;
    stmm_generator * gen;
    int c, Counter;

    // create mapping of strashed nodes into the corresponding network nodes
    tStrash2Net = stmm_init_table(stmm_ptrcmp,stmm_ptrhash);
    Abc_NtkForEachNode( pNtk, pNode, c )
    {
        // skip the constant input nodes
        if ( Abc_ObjFaninNum(pNode) == 0 )
            continue;
        // get the strashed node
        pNodeAig = pNode->pCopy;
        // skip the dangling nodes
        if ( pNodeAig == NULL )
            continue;
        // skip the nodes that fanout into COs
        if ( Abc_NodeFindCoFanout(pNode) )
            continue;
        // get the FRAIG node
        gNode = Fraig_NotCond( Abc_ObjRegular(pNodeAig)->pCopy, (int)Abc_ObjIsComplement(pNodeAig) );
        if ( !stmm_find_or_add( tStrash2Net, (char *)Fraig_Regular(gNode), (char ***)&ppSlot ) )
            *ppSlot = NULL;
        // add the node to the list
        pNode->pNext = *ppSlot;
        *ppSlot = pNode;
        // mark the node if it is complemented
        pNode->fPhase = Fraig_IsComplement(gNode);
    }

    // print the classes
    c = 0;
    Counter = 0;
    tResult = stmm_init_table(stmm_ptrcmp,stmm_ptrhash);
    stmm_foreach_item( tStrash2Net, gen, (char **)&gNode, (char **)&pList )
    {
        // skip the trival classes
        if ( pList == NULL || pList->pNext == NULL )
            continue;
        // add the non-trival class
        stmm_insert( tResult, (char *)pList, NULL );
        // count nodes in the non-trival classes
        for ( pNode = pList; pNode; pNode = pNode->pNext )
            Counter++;

        if ( fVeryVerbose )
        {
            printf( "Class %2d : {", c );
            for ( pNode = pList; pNode; pNode = pNode->pNext )
            {
                pNode->pCopy = NULL;
                printf( " %s", Abc_ObjName(pNode) );
                printf( "(%c)", pNode->fPhase? '-' : '+' );
                printf( "(%d)", pNode->Level );
            }
            printf( " }\n" );
            c++;
        }
    }
    if ( fVerbose || fVeryVerbose )
    {
        printf( "Sweeping stats for network \"%s\":\n", pNtk->pName );
        printf( "Internal nodes = %d. Different functions (up to compl) = %d.\n", Abc_NtkNodeNum(pNtk), stmm_count(tStrash2Net) );
        printf( "Non-trivial classes = %d. Nodes in non-trivial classes = %d.\n", stmm_count(tResult), Counter );
    }
    stmm_free_table( tStrash2Net );
    return tResult;
}


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

  Synopsis    [Transforms the network using the equivalence relation on nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkFraigTransform( Abc_Ntk_t * pNtk, stmm_table * tEquiv, int fUseInv, int fVerbose )
{
    stmm_generator * gen;
    Abc_Obj_t * pList;
    if ( stmm_count(tEquiv) == 0 )
        return;
    // merge nodes in the classes
    if ( Abc_NtkHasMapping( pNtk ) )
    {
        Abc_NtkDelayTrace( pNtk, NULL, NULL, 0 );
        stmm_foreach_item( tEquiv, gen, (char **)&pList, NULL )
            Abc_NtkFraigMergeClassMapped( pNtk, pList, fUseInv, fVerbose );
    }
    else 
    {
        stmm_foreach_item( tEquiv, gen, (char **)&pList, NULL )
            Abc_NtkFraigMergeClass( pNtk, pList, fUseInv, fVerbose );
    }
}


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

  Synopsis    [Transforms the list of one-phase equivalent nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkFraigMergeClassMapped( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose )
{
    Abc_Obj_t * pListDir, * pListInv;
    Abc_Obj_t * pNodeMin, * pNode, * pNext;
    float Arrival1, Arrival2;

    assert( pChain );
    assert( pChain->pNext );

    // divide the nodes into two parts: 
    // those that need the invertor and those that don't need
    pListDir = pListInv = NULL;
    for ( pNode = pChain, pNext = pChain->pNext; 
          pNode; 
          pNode = pNext, pNext = pNode? pNode->pNext : NULL )
    {
        // check to which class the node belongs
        if ( pNode->fPhase == 1 )
        {
            pNode->pNext = pListDir;
            pListDir = pNode;
        }
        else
        {
            pNode->pNext = pListInv;
            pListInv = pNode;
        }
    }

    // find the node with the smallest number of logic levels
    pNodeMin = pListDir;
    for ( pNode = pListDir; pNode; pNode = pNode->pNext )
    {
        Arrival1 = Abc_NodeReadArrivalWorst(pNodeMin);
        Arrival2 = Abc_NodeReadArrivalWorst(pNode   );
//        assert( Abc_ObjIsCi(pNodeMin) || Arrival1 > 0 );
//        assert( Abc_ObjIsCi(pNode)    || Arrival2 > 0 );
        if (  Arrival1 > Arrival2 ||
              (Arrival1 == Arrival2 && pNodeMin->Level >  pNode->Level) ||
              (Arrival1 == Arrival2 && pNodeMin->Level == pNode->Level &&
              Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode))  )
            pNodeMin = pNode;
    }

    // move the fanouts of the direct nodes
    for ( pNode = pListDir; pNode; pNode = pNode->pNext )
        if ( pNode != pNodeMin )
            Abc_ObjTransferFanout( pNode, pNodeMin );

    // find the node with the smallest number of logic levels
    pNodeMin = pListInv;
    for ( pNode = pListInv; pNode; pNode = pNode->pNext )
    {
        Arrival1 = Abc_NodeReadArrivalWorst(pNodeMin);
        Arrival2 = Abc_NodeReadArrivalWorst(pNode   );
//        assert( Abc_ObjIsCi(pNodeMin) || Arrival1 > 0 );
//        assert( Abc_ObjIsCi(pNode)    || Arrival2 > 0 );
        if (  Arrival1 > Arrival2 ||
              (Arrival1 == Arrival2 && pNodeMin->Level >  pNode->Level) ||
              (Arrival1 == Arrival2 && pNodeMin->Level == pNode->Level &&
              Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode))  )
            pNodeMin = pNode;
    }

    // move the fanouts of the direct nodes
    for ( pNode = pListInv; pNode; pNode = pNode->pNext )
        if ( pNode != pNodeMin )
            Abc_ObjTransferFanout( pNode, pNodeMin );
}

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

  Synopsis    [Process one equivalence class of nodes.]

  Description [This function does not remove the nodes. It only switches 
  around the connections.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkFraigMergeClass( Abc_Ntk_t * pNtk, Abc_Obj_t * pChain, int fUseInv, int fVerbose )
{
    Abc_Obj_t * pListDir, * pListInv;
    Abc_Obj_t * pNodeMin, * pNodeMinInv;
    Abc_Obj_t * pNode, * pNext;

    assert( pChain );
    assert( pChain->pNext );

    // find the node with the smallest number of logic levels
    pNodeMin = pChain;
    for ( pNode = pChain->pNext; pNode; pNode = pNode->pNext )
        if (  pNodeMin->Level >  pNode->Level || 
            ( pNodeMin->Level == pNode->Level && 
              Abc_NodeDroppingCost(pNodeMin) < Abc_NodeDroppingCost(pNode) ) )
            pNodeMin = pNode;

    // divide the nodes into two parts: 
    // those that need the invertor and those that don't need
    pListDir = pListInv = NULL;
    for ( pNode = pChain, pNext = pChain->pNext; 
          pNode; 
          pNode = pNext, pNext = pNode? pNode->pNext : NULL )
    {
        if ( pNode == pNodeMin )
            continue;
        // check to which class the node belongs
        if ( pNodeMin->fPhase == pNode->fPhase )
        {
            pNode->pNext = pListDir;
            pListDir = pNode;
        }
        else
        {
            pNode->pNext = pListInv;
            pListInv = pNode;
        }
    }

    // move the fanouts of the direct nodes
    for ( pNode = pListDir; pNode; pNode = pNode->pNext )
        Abc_ObjTransferFanout( pNode, pNodeMin );

    // skip if there are no inverted nodes
    if ( pListInv == NULL )
        return;

    // add the invertor
    pNodeMinInv = Abc_NtkCreateNodeInv( pNtk, pNodeMin );
   
    // move the fanouts of the inverted nodes
    for ( pNode = pListInv; pNode; pNode = pNode->pNext )
        Abc_ObjTransferFanout( pNode, pNodeMinInv );
}


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

  Synopsis    [Returns the number of literals saved if this node becomes useless.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeDroppingCost( Abc_Obj_t * pNode )
{ 
    return 1;
}





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

  Synopsis    [Removes dangling nodes.]

  Description [Returns the number of nodes removed.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkCleanup( Abc_Ntk_t * pNtk, int fVerbose )
{
    Vec_Ptr_t * vNodes;
    int Counter;
    assert( Abc_NtkIsLogic(pNtk) );
    // mark the nodes reachable from the POs
    vNodes = Abc_NtkDfs( pNtk, 0 );
    Counter = Abc_NtkReduceNodes( pNtk, vNodes );
    if ( fVerbose )
        printf( "Cleanup removed %d dangling nodes.\n", Counter );
    Vec_PtrFree( vNodes );
    return Counter;
}

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

  Synopsis    [Removes dangling nodes.]

  Description [Returns the number of nodes removed.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkCleanupNodes( Abc_Ntk_t * pNtk, Vec_Ptr_t * vRoots, int fVerbose )
{
    Vec_Ptr_t * vNodes, * vStarts;
    Abc_Obj_t * pObj;
    int i, Counter;
    assert( Abc_NtkIsLogic(pNtk) );
    // collect starting nodes into one array
    vStarts = Vec_PtrAlloc( 1000 );
    Abc_NtkForEachCo( pNtk, pObj, i )
        Vec_PtrPush( vStarts, pObj );
    Vec_PtrForEachEntry( Abc_Obj_t *, vRoots, pObj, i )
        if ( pObj )
            Vec_PtrPush( vStarts, pObj );
    // mark the nodes reachable from the POs
    vNodes = Abc_NtkDfsNodes( pNtk, (Abc_Obj_t **)Vec_PtrArray(vStarts), Vec_PtrSize(vStarts) );
    Vec_PtrFree( vStarts );
    Counter = Abc_NtkReduceNodes( pNtk, vNodes );
    if ( fVerbose )
        printf( "Cleanup removed %d dangling nodes.\n", Counter );
    Vec_PtrFree( vNodes );
    return Counter;
}

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

  Synopsis    [Preserves the nodes collected in the array.]

  Description [Returns the number of nodes removed.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkReduceNodes( Abc_Ntk_t * pNtk, Vec_Ptr_t * vNodes )
{
    Abc_Obj_t * pNode;
    int i, Counter;
    assert( Abc_NtkIsLogic(pNtk) );
    // mark the nodes reachable from the POs
    Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pNode, i )
        pNode->fMarkA = 1;
    // remove the non-marked nodes
    Counter = 0;
    Abc_NtkForEachNode( pNtk, pNode, i )
        if ( pNode->fMarkA == 0 )
        {
            Abc_NtkDeleteObj( pNode );
            Counter++;
        }
    // unmark the remaining nodes
    Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pNode, i )
        pNode->fMarkA = 0;
    // check
    if ( !Abc_NtkCheck( pNtk ) )
        printf( "Abc_NtkCleanup: The network check has failed.\n" );
    return Counter;
}




#ifdef ABC_USE_CUDD

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

  Synopsis    [Replaces the local function by its cofactor.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NodeConstantInput( Abc_Obj_t * pNode, Abc_Obj_t * pFanin, int fConst0 )
{
    DdManager * dd = (DdManager *)pNode->pNtk->pManFunc;
    DdNode * bVar, * bTemp;
    int iFanin;
    assert( Abc_NtkIsBddLogic(pNode->pNtk) ); 
    if ( (iFanin = Vec_IntFind( &pNode->vFanins, pFanin->Id )) == -1 )
    {
        printf( "Node %s should be among", Abc_ObjName(pFanin) );
        printf( " the fanins of node %s...\n", Abc_ObjName(pNode) );
        return;
    }
    bVar = Cudd_NotCond( Cudd_bddIthVar(dd, iFanin), fConst0 );
    pNode->pData = Cudd_Cofactor( dd, bTemp = (DdNode *)pNode->pData, bVar );   Cudd_Ref( (DdNode *)pNode->pData );
    Cudd_RecursiveDeref( dd, bTemp );
}

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

  Synopsis    [Tranditional sweep of the network.]

  Description [Propagates constant and single-input node, removes dangling nodes.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkSweep( Abc_Ntk_t * pNtk, int fVerbose )
{
    Vec_Ptr_t * vNodes;
    Abc_Obj_t * pNode, * pFanout, * pDriver;
    int i, nNodesOld;
    assert( Abc_NtkIsLogic(pNtk) ); 
    // convert network to BDD representation
    if ( !Abc_NtkToBdd(pNtk) )
    {
        fprintf( stdout, "Converting to BDD has failed.\n" );
        return 1;
    }
    // perform cleanup
    nNodesOld = Abc_NtkNodeNum(pNtk);
    Abc_NtkCleanup( pNtk, 0 );
    // prepare nodes for sweeping
    Abc_NtkRemoveDupFanins(pNtk);
    Abc_NtkMinimumBase(pNtk);
    // collect sweepable nodes
    vNodes = Vec_PtrAlloc( 100 );
    Abc_NtkForEachNode( pNtk, pNode, i )
        if ( Abc_ObjFaninNum(pNode) < 2 )
            Vec_PtrPush( vNodes, pNode );
    // sweep the nodes
    while ( Vec_PtrSize(vNodes) > 0 )
    {
        // get any sweepable node
        pNode = (Abc_Obj_t *)Vec_PtrPop(vNodes);
        if ( !Abc_ObjIsNode(pNode) )
            continue;
        // get any non-CO fanout of this node
        pFanout = Abc_NodeFindNonCoFanout(pNode);
        if ( pFanout == NULL )
            continue;
        assert( Abc_ObjIsNode(pFanout) );
        // transform the function of the fanout
        if ( Abc_ObjFaninNum(pNode) == 0 )
            Abc_NodeConstantInput( pFanout, pNode, Abc_NodeIsConst0(pNode) );
        else 
        {
            assert( Abc_ObjFaninNum(pNode) == 1 );
            pDriver = Abc_ObjFanin0(pNode);
            if ( Abc_NodeIsInv(pNode) )
                Abc_NodeComplementInput( pFanout, pNode );
            Abc_ObjPatchFanin( pFanout, pNode, pDriver );
        }
        Abc_NodeRemoveDupFanins( pFanout );
        Abc_NodeMinimumBase( pFanout );
        // check if the fanout should be added
        if ( Abc_ObjFaninNum(pFanout) < 2 )
            Vec_PtrPush( vNodes, pFanout );
        // check if the node has other fanouts
        if ( Abc_ObjFanoutNum(pNode) > 0 )
            Vec_PtrPush( vNodes, pNode );
        else
            Abc_NtkDeleteObj_rec( pNode, 1 );
    }
    Vec_PtrFree( vNodes );
    // sweep a node into its CO fanout if all of this is true:
    // (a) this node is a single-input node
    // (b) the driver of the node has only one fanout (this node)
    // (c) the driver is a node
    Abc_NtkForEachCo( pNtk, pFanout, i )
    {
        pNode = Abc_ObjFanin0(pFanout);
        if ( Abc_ObjFaninNum(pNode) != 1 )
            continue;
        pDriver = Abc_ObjFanin0(pNode);
        if ( !(Abc_ObjFanoutNum(pDriver) == 1 && Abc_ObjIsNode(pDriver)) )
            continue;
        // trasform this CO
        if ( Abc_NodeIsInv(pNode) )
            pDriver->pData = Cudd_Not(pDriver->pData);
        Abc_ObjPatchFanin( pFanout, pNode, pDriver );
    }
    // perform cleanup
    Abc_NtkCleanup( pNtk, 0 );
    // report
    if ( fVerbose )
        printf( "Sweep removed %d nodes.\n", nNodesOld - Abc_NtkNodeNum(pNtk) );
    return nNodesOld - Abc_NtkNodeNum(pNtk);
}


#else

int Abc_NtkSweep( Abc_Ntk_t * pNtk, int fVerbose ) { return 1; }

#endif


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

  Synopsis    [Removes all objects whose trav ID is not current.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeRemoveNonCurrentObjects( Abc_Ntk_t * pNtk )
{
    Abc_Obj_t * pObj;
    int Counter, i;
    int fVerbose = 0;

    // report on the nodes to be deleted
    if ( fVerbose )
    {
        printf( "These nodes will be deleted: \n" );
        Abc_NtkForEachObj( pNtk, pObj, i )
            if ( !Abc_NodeIsTravIdCurrent( pObj ) )
            {
                printf( "    " );
                Abc_ObjPrint( stdout, pObj );
            }
    }
    
    // delete the nodes    
    Counter = 0;
    Abc_NtkForEachObj( pNtk, pObj, i )
        if ( !Abc_NodeIsTravIdCurrent( pObj ) )
        {
            Abc_NtkDeleteObj( pObj );
            Counter++;
        }
    return Counter;
}

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

  Synopsis    [Check if the fanin of this latch is a constant.]

  Description [Returns 0/1 if constant; -1 if not a constant.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkSetTravId_rec( Abc_Obj_t * pObj )
{
    Abc_NodeSetTravIdCurrent(pObj);
    if ( Abc_ObjFaninNum(pObj) == 0 )
        return;
    assert( Abc_ObjFaninNum(pObj) == 1 );
    Abc_NtkSetTravId_rec( Abc_ObjFanin0(pObj) );    
}

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

  Synopsis    [Check if the fanin of this latch is a constant.]

  Description [Returns 0/1 if constant; -1 if not a constant.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkCheckConstant_rec( Abc_Obj_t * pObj )
{
    if ( Abc_ObjFaninNum(pObj) == 0 )
    {
        if ( !Abc_ObjIsNode(pObj) )
            return -1;
        if ( Abc_NodeIsConst0(pObj) )
            return 0;
        if ( Abc_NodeIsConst1(pObj) )
            return 1;
        assert( 0 );
        return -1;
    }
    if ( Abc_ObjIsLatch(pObj) || Abc_ObjFaninNum(pObj) > 1 )
        return -1;
    if ( !Abc_ObjIsNode(pObj) || Abc_NodeIsBuf(pObj) )
        return Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pObj) );
    if ( Abc_NodeIsInv(pObj) )
    {
        int RetValue = Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pObj) );
        if ( RetValue == 0 )
            return 1;
        if ( RetValue == 1 )
            return 0;
        return RetValue;
    }
    assert( 0 );
    return -1;
}

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

  Synopsis    [Removes redundant latches.]

  Description [The redundant latches are of two types:
  - Latches fed by a constant which matches the init value of the latch.
  - Latches fed by a constant which does not match the init value of the latch
  can be all replaced by one latch.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkLatchSweep( Abc_Ntk_t * pNtk )
{
    Abc_Obj_t * pFanin, * pLatch, * pLatchPivot = NULL;
    int Counter, RetValue, i;
    Counter = 0;
    // go through the latches
    Abc_NtkForEachLatch( pNtk, pLatch, i )
    {
        // check if the latch has constant input
        RetValue = Abc_NtkCheckConstant_rec( Abc_ObjFanin0(pLatch) );
        if ( RetValue == -1 )
            continue;
        // found a latch with constant fanin
        if ( (RetValue == 1 && Abc_LatchIsInit0(pLatch)) ||
             (RetValue == 0 && Abc_LatchIsInit1(pLatch)) )
        {
            // fanin constant differs from the latch init value
            if ( pLatchPivot == NULL )
            {
                pLatchPivot = pLatch;
                continue;
            }
            if ( Abc_LatchInit(pLatch) != Abc_LatchInit(pLatchPivot) ) // add inverter
                pFanin = Abc_NtkCreateNodeInv( pNtk, Abc_ObjFanout0(pLatchPivot) );
            else
                pFanin = Abc_ObjFanout0(pLatchPivot);
        }
        else
            pFanin = Abc_ObjFanin0(Abc_ObjFanin0(pLatch));
        // replace latch
        Abc_ObjTransferFanout( Abc_ObjFanout0(pLatch), pFanin );
        // delete the extra nodes
        Abc_NtkDeleteObj_rec( Abc_ObjFanout0(pLatch), 0 );
        Counter++;
    }
    return Counter;
}

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

  Synopsis    [Replaces autonumnous logic by free inputs.]

  Description [Assumes that non-autonomous logic is marked with
  the current ID.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkReplaceAutonomousLogic( Abc_Ntk_t * pNtk )
{
    Abc_Obj_t * pNode, * pFanin;
    Vec_Ptr_t * vNodes;
    int i, k, Counter;
    // collect the nodes that feed into the reachable logic
    vNodes = Vec_PtrAlloc( 100 );
    Abc_NtkForEachObj( pNtk, pNode, i )
    {
        // skip non-visited fanins
        if ( !Abc_NodeIsTravIdCurrent(pNode) )
            continue;
        // look for non-visited fanins
        Abc_ObjForEachFanin( pNode, pFanin, k )
        {
            // skip visited fanins
            if ( Abc_NodeIsTravIdCurrent(pFanin) )
                continue;
            // skip constants and latches fed by constants
            if ( Abc_NtkCheckConstant_rec(pFanin) != -1 ||
                 (Abc_ObjIsBo(pFanin) && Abc_NtkCheckConstant_rec(Abc_ObjFanin0(Abc_ObjFanin0(pFanin))) != -1) )
            {
                Abc_NtkSetTravId_rec( pFanin );
                continue;
            }
            assert( !Abc_ObjIsLatch(pFanin) );
            Vec_PtrPush( vNodes, pFanin );
        }
    }
    Vec_PtrUniqify( vNodes, (int (*)(void))Abc_ObjPointerCompare );
    // replace these nodes by the PIs
    Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pNode, i )
    {
        pFanin = Abc_NtkCreatePi(pNtk);
        Abc_ObjAssignName( pFanin, Abc_ObjName(pFanin), NULL );
        Abc_NodeSetTravIdCurrent( pFanin );
        Abc_ObjTransferFanout( pNode, pFanin );
    }
    Counter = Vec_PtrSize(vNodes);
    Vec_PtrFree( vNodes );
    return Counter;
}

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

  Synopsis    [Sequential cleanup.]

  Description [Performs three tasks:
  - Removes logic that does not feed into POs.
  - Removes latches driven by constant values equal to the initial state.
  - Replaces the autonomous components by additional PI variables.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkCleanupSeq( Abc_Ntk_t * pNtk, int fLatchSweep, int fAutoSweep, int fVerbose )
{
    Vec_Ptr_t * vNodes;
    int Counter;
    assert( Abc_NtkIsLogic(pNtk) );
    // mark the nodes reachable from the POs
    vNodes = Abc_NtkDfsSeq( pNtk );
    Vec_PtrFree( vNodes );
    // remove the non-marked nodes
    Counter = Abc_NodeRemoveNonCurrentObjects( pNtk );
    if ( fVerbose )
        printf( "Cleanup removed %4d dangling objects.\n", Counter );
    // check if some of the latches can be removed
    if ( fLatchSweep )
    {
        Counter = Abc_NtkLatchSweep( pNtk );
        if ( fVerbose )
            printf( "Cleanup removed %4d redundant latches.\n", Counter );
    }
    // detect the autonomous components
    if ( fAutoSweep )
    {
        vNodes = Abc_NtkDfsSeqReverse( pNtk );
        Vec_PtrFree( vNodes );
        // replace them by PIs
        Counter = Abc_NtkReplaceAutonomousLogic( pNtk );
        if ( fVerbose )
            printf( "Cleanup added   %4d additional PIs.\n", Counter );
        // remove the non-marked nodes
        Counter = Abc_NodeRemoveNonCurrentObjects( pNtk );
        if ( fVerbose )
            printf( "Cleanup removed %4d autonomous objects.\n", Counter );
    }
    // check
    if ( !Abc_NtkCheck( pNtk ) )
        printf( "Abc_NtkCleanupSeq: The network check has failed.\n" );
    return 1;
}

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

  Synopsis    [Sweep to remove buffers and inverters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkSweepBufsInvs( Abc_Ntk_t * pNtk, int fVerbose )
{
    Hop_Man_t * pMan;
    Abc_Obj_t * pObj, * pFanin;
    int i, k, fChanges = 1, Counter = 0;
    assert( Abc_NtkIsLogic(pNtk) ); 
    // convert network to BDD representation
    if ( !Abc_NtkToAig(pNtk) )
    {
        fprintf( stdout, "Converting to SOP has failed.\n" );
        return 1;
    }
    // get AIG manager
    pMan = (Hop_Man_t *)pNtk->pManFunc;
    // label selected nodes
    Abc_NtkIncrementTravId( pNtk );
    // iterate till no improvement
    while ( fChanges )
    {
        fChanges = 0;
        Abc_NtkForEachObj( pNtk, pObj, i )
        {
            Abc_ObjForEachFanin( pObj, pFanin, k )
            {
                // do not eliminate marked fanins
                if ( Abc_NodeIsTravIdCurrent(pFanin) )
                    continue;
                // do not eliminate constant nodes
                if ( !Abc_ObjIsNode(pFanin) || Abc_ObjFaninNum(pFanin) != 1 )
                    continue;
                // do not eliminate inverters into COs
                if ( Abc_ObjIsCo(pObj) && Abc_NodeIsInv(pFanin) )
                    continue;
                // do not eliminate buffers connecting PIs and POs 
//                if ( Abc_ObjIsCo(pObj) && Abc_ObjIsCi(Abc_ObjFanin0(pFanin)) )
//                    continue;
                fChanges = 1;
                Counter++;
                // update function of the node
                if ( Abc_NodeIsInv(pFanin) )
                    pObj->pData = Hop_Compose( pMan, (Hop_Obj_t *)pObj->pData, Hop_Not(Hop_IthVar(pMan, k)), k );
                // update the fanin
                Abc_ObjPatchFanin( pObj, pFanin, Abc_ObjFanin0(pFanin) );
                if ( Abc_ObjFanoutNum(pFanin) == 0 )
                    Abc_NtkDeleteObj(pFanin);            
            }
        }
    }
    if ( fVerbose )
        printf( "Removed %d single input nodes.\n", Counter );
    return Counter;
}



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


ABC_NAMESPACE_IMPL_END

