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

  FileName    [abcMerge.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [LUT merging algorithm.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "base/abc/abc.h"
#include "aig/aig/aig.h"
#include "aig/nwk/nwkMerge.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Marks the fanins of the node with the current trav ID.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkMarkFanins_rec( Abc_Obj_t * pLut, int nLevMin )
{
    Abc_Obj_t * pNext;
    int i;
    if ( !Abc_ObjIsNode(pLut) )
        return;
    if ( Abc_NodeIsTravIdCurrent( pLut ) )
        return;
    Abc_NodeSetTravIdCurrent( pLut );
    if ( Abc_ObjLevel(pLut) < nLevMin )
        return;
    Abc_ObjForEachFanin( pLut, pNext, i )
        Abc_NtkMarkFanins_rec( pNext, nLevMin );
}

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

  Synopsis    [Marks the fanouts of the node with the current trav ID.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkMarkFanouts_rec( Abc_Obj_t * pLut, int nLevMax, int nFanMax )
{
    Abc_Obj_t * pNext;
    int i;
    if ( !Abc_ObjIsNode(pLut) )
        return;
    if ( Abc_NodeIsTravIdCurrent( pLut ) )
        return;
    Abc_NodeSetTravIdCurrent( pLut );
    if ( Abc_ObjLevel(pLut) > nLevMax )
        return;
    if ( Abc_ObjFanoutNum(pLut) > nFanMax )
        return;
    Abc_ObjForEachFanout( pLut, pNext, i )
        Abc_NtkMarkFanouts_rec( pNext, nLevMax, nFanMax );
}

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

  Synopsis    [Collects the circle of nodes around the given set.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkCollectCircle( Vec_Ptr_t * vStart, Vec_Ptr_t * vNext, int nFanMax )
{
    Abc_Obj_t * pObj, * pNext;
    int i, k;
    Vec_PtrClear( vNext );
    Vec_PtrForEachEntry( Vec_Int_t *, vStart, pObj, i )
    {
        Abc_ObjForEachFanin( pObj, pNext, k )
        {
            if ( !Abc_ObjIsNode(pNext) )
                continue;
            if ( Abc_NodeIsTravIdCurrent( pNext ) )
                continue;
            Abc_NodeSetTravIdCurrent( pNext );
            Vec_PtrPush( vNext, pNext );
        }
        Abc_ObjForEachFanout( pObj, pNext, k )
        {
            if ( !Abc_ObjIsNode(pNext) )
                continue;
            if ( Abc_NodeIsTravIdCurrent( pNext ) )
                continue;
            Abc_NodeSetTravIdCurrent( pNext );
            if ( Abc_ObjFanoutNum(pNext) > nFanMax )
                continue;
            Vec_PtrPush( vNext, pNext );
        }
    }
}

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

  Synopsis    [Collects the circle of nodes removes from the given one.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkCollectNonOverlapCands( Abc_Obj_t * pLut, Vec_Ptr_t * vStart, Vec_Ptr_t * vNext, Vec_Ptr_t * vCands, Nwk_LMPars_t * pPars )
{
    Vec_Ptr_t * vTemp;
    Abc_Obj_t * pObj;
    int i, k;
    Vec_PtrClear( vCands );
    if ( pPars->nMaxSuppSize - Abc_ObjFaninNum(pLut) <= 1 )
        return;

    // collect nodes removed by this distance
    assert( pPars->nMaxDistance > 0 );
    Vec_PtrClear( vStart );
    Vec_PtrPush( vStart, pLut );
    Abc_NtkIncrementTravId( pLut->pNtk );
    Abc_NodeSetTravIdCurrent( pLut );
    for ( i = 1; i <= pPars->nMaxDistance; i++ )
    {
        Abc_NtkCollectCircle( vStart, vNext, pPars->nMaxFanout );
        vTemp  = vStart;
        vStart = vNext;
        vNext  = vTemp;
        // collect the nodes in vStart
        Vec_PtrForEachEntry( Vec_Int_t *, vStart, pObj, k )
            Vec_PtrPush( vCands, pObj );
    }

    // mark the TFI/TFO nodes
    Abc_NtkIncrementTravId( pLut->pNtk );
    if ( pPars->fUseTfiTfo )
        Abc_NodeSetTravIdCurrent( pLut );
    else
    {
        Abc_NodeSetTravIdPrevious( pLut );
        Abc_NtkMarkFanins_rec( pLut, Abc_ObjLevel(pLut) - pPars->nMaxDistance );
        Abc_NodeSetTravIdPrevious( pLut );
        Abc_NtkMarkFanouts_rec( pLut, Abc_ObjLevel(pLut) + pPars->nMaxDistance, pPars->nMaxFanout );
    }

    // collect nodes satisfying the following conditions:
    // - they are close enough in terms of distance
    // - they are not in the TFI/TFO of the LUT
    // - they have no more than the given number of fanins
    // - they have no more than the given diff in delay
    k = 0;
    Vec_PtrForEachEntry( Vec_Int_t *, vCands, pObj, i )
    {
        if ( Abc_NodeIsTravIdCurrent(pObj) )
            continue;
        if ( Abc_ObjFaninNum(pLut) + Abc_ObjFaninNum(pObj) > pPars->nMaxSuppSize )
            continue;
        if ( Abc_ObjLevel(pLut) - Abc_ObjLevel(pObj) > pPars->nMaxLevelDiff || 
             Abc_ObjLevel(pObj) - Abc_ObjLevel(pLut) > pPars->nMaxLevelDiff )
             continue;
        Vec_PtrWriteEntry( vCands, k++, pObj );
    }
    Vec_PtrShrink( vCands, k );
}


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

  Synopsis    [Count the total number of fanins.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkCountTotalFanins( Abc_Obj_t * pLut, Abc_Obj_t * pCand )
{
    Abc_Obj_t * pFanin;
    int i, nCounter = Abc_ObjFaninNum(pLut);
    Abc_ObjForEachFanin( pCand, pFanin, i )
        nCounter += !pFanin->fMarkC;
    return nCounter;
}

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

  Synopsis    [Collects overlapping candidates.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkCollectOverlapCands( Abc_Obj_t * pLut, Vec_Ptr_t * vCands, Nwk_LMPars_t * pPars )
{
    Abc_Obj_t * pFanin, * pObj;
    int i, k;
    // mark fanins of pLut
    Abc_ObjForEachFanin( pLut, pFanin, i )
        pFanin->fMarkC = 1;
    // collect the matching fanouts of each fanin of the node
    Vec_PtrClear( vCands );
    Abc_NtkIncrementTravId( pLut->pNtk );
    Abc_NodeSetTravIdCurrent( pLut );
    Abc_ObjForEachFanin( pLut, pFanin, i )
    {
        if ( !Abc_ObjIsNode(pFanin) )
            continue;
        if ( Abc_ObjFanoutNum(pFanin) > pPars->nMaxFanout )
            continue;
        Abc_ObjForEachFanout( pFanin, pObj, k )
        {
            if ( !Abc_ObjIsNode(pObj) )
                continue;
            if ( Abc_NodeIsTravIdCurrent( pObj ) )
                continue;
            Abc_NodeSetTravIdCurrent( pObj );
            // check the difference in delay
            if ( Abc_ObjLevel(pLut) - Abc_ObjLevel(pObj) > pPars->nMaxLevelDiff || 
                 Abc_ObjLevel(pObj) - Abc_ObjLevel(pLut) > pPars->nMaxLevelDiff )
                 continue;
            // check the total number of fanins of the node
            if ( Abc_NtkCountTotalFanins(pLut, pObj) > pPars->nMaxSuppSize )
                continue;
            Vec_PtrPush( vCands, pObj );
        }
    }
    // unmark fanins of pLut
    Abc_ObjForEachFanin( pLut, pFanin, i )
        pFanin->fMarkC = 0;
}

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

  Synopsis    [Performs LUT merging with parameters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Abc_NtkLutMerge( Abc_Ntk_t * pNtk, Nwk_LMPars_t * pPars )
{
    Nwk_Grf_t * p;
    Vec_Int_t * vResult;
    Vec_Ptr_t * vStart, * vNext, * vCands1, * vCands2;
    Abc_Obj_t * pLut, * pCand;
    int i, k, nVertsMax, nCands;
    clock_t clk = clock();
    // count the number of vertices
    nVertsMax = 0;
    Abc_NtkForEachNode( pNtk, pLut, i )
        nVertsMax += (int)(Abc_ObjFaninNum(pLut) <= pPars->nMaxLutSize);
    p = Nwk_ManGraphAlloc( nVertsMax );
    // create graph
    vStart  = Vec_PtrAlloc( 1000 );
    vNext   = Vec_PtrAlloc( 1000 );
    vCands1 = Vec_PtrAlloc( 1000 );
    vCands2 = Vec_PtrAlloc( 1000 );
    nCands  = 0;
    Abc_NtkForEachNode( pNtk, pLut, i )
    {
        if ( Abc_ObjFaninNum(pLut) > pPars->nMaxLutSize )
            continue;
        Abc_NtkCollectOverlapCands( pLut, vCands1, pPars );
        if ( pPars->fUseDiffSupp )
            Abc_NtkCollectNonOverlapCands( pLut, vStart, vNext, vCands2, pPars );
        if ( Vec_PtrSize(vCands1) == 0 && Vec_PtrSize(vCands2) == 0 )
            continue;
        nCands += Vec_PtrSize(vCands1) + Vec_PtrSize(vCands2);
        // save candidates
        Vec_PtrForEachEntry( Vec_Int_t *, vCands1, pCand, k )
            Nwk_ManGraphHashEdge( p, Abc_ObjId(pLut), Abc_ObjId(pCand) );
        Vec_PtrForEachEntry( Vec_Int_t *, vCands2, pCand, k )
            Nwk_ManGraphHashEdge( p, Abc_ObjId(pLut), Abc_ObjId(pCand) );
        // print statistics about this node
        if ( pPars->fVeryVerbose )
        printf( "Node %6d : Fanins = %d. Fanouts = %3d.  Cand1 = %3d. Cand2 = %3d.\n",
            Abc_ObjId(pLut), Abc_ObjFaninNum(pLut), Abc_ObjFaninNum(pLut), 
            Vec_PtrSize(vCands1), Vec_PtrSize(vCands2) );
    }
    Vec_PtrFree( vStart );
    Vec_PtrFree( vNext );
    Vec_PtrFree( vCands1 );
    Vec_PtrFree( vCands2 );
    if ( pPars->fVerbose )
    {
        printf( "Mergable LUTs = %6d. Total cands = %6d. ", p->nVertsMax, nCands );
        ABC_PRT( "Deriving graph", clock() - clk );
    }
    // solve the graph problem
    clk = clock();
    Nwk_ManGraphSolve( p );
    if ( pPars->fVerbose )
    {
        printf( "GRAPH: Nodes = %6d. Edges = %6d.  Pairs = %6d.  ", 
            p->nVerts, p->nEdges, Vec_IntSize(p->vPairs)/2 );
        ABC_PRT( "Solving", clock() - clk );
        Nwk_ManGraphReportMemoryUsage( p );
    }
    vResult = p->vPairs; p->vPairs = NULL;
/*
    for ( i = 0; i < vResult->nSize; i += 2 )
        printf( "(%d,%d) ", vResult->pArray[i], vResult->pArray[i+1] );
    printf( "\n" );
*/
    Nwk_ManGraphFree( p );
    return vResult;
}


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


ABC_NAMESPACE_IMPL_END

