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

  FileName    [giaMap.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Manipulation of mapping associated with the AIG.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "aig/aig/aig.h"
#include "map/if/if.h"
#include "opt/dar/dar.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Load the network into FPGA manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSetIfParsDefault( If_Par_t * pPars )
{
//    extern void * Abc_FrameReadLibLut();
    // set defaults
    memset( pPars, 0, sizeof(If_Par_t) );
    // user-controlable paramters
//    pPars->nLutSize    = -1;
    pPars->nLutSize    =  6;
    pPars->nCutsMax    =  8;
    pPars->nFlowIters  =  1;
    pPars->nAreaIters  =  2;
    pPars->DelayTarget = -1;
    pPars->Epsilon     =  (float)0.005;
    pPars->fPreprocess =  1;
    pPars->fArea       =  0;
    pPars->fFancy      =  0;
    pPars->fExpRed     =  1; ////
    pPars->fLatchPaths =  0;
    pPars->fEdge       =  1;
    pPars->fPower      =  0;
    pPars->fCutMin     =  0;
    pPars->fSeqMap     =  0;
    pPars->fVerbose    =  0;
    // internal parameters
    pPars->fTruth      =  0;
    pPars->nLatchesCi  =  0;
    pPars->nLatchesCo  =  0;
    pPars->fLiftLeaves =  0;
//    pPars->pLutLib     =  Abc_FrameReadLibLut();
    pPars->pLutLib     =  NULL;
    pPars->pTimesArr   =  NULL; 
    pPars->pTimesArr   =  NULL;   
    pPars->pFuncCost   =  NULL;   
}

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

  Synopsis    [Load the network into FPGA manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
If_Man_t * Gia_ManToIf( Gia_Man_t * p, If_Par_t * pPars, Vec_Ptr_t * vAigToIf )
{
//    extern Vec_Int_t * SGia_ManComputeSwitchProbs( Gia_Man_t * p, int nFrames, int nPref, int fProbOne );
//    Vec_Int_t * vSwitching = NULL, * vSwitching2 = NULL;
//    float * pSwitching, * pSwitching2;
    If_Man_t * pIfMan;
    If_Obj_t * pIfObj;
    Gia_Obj_t * pNode;
    int i;//, clk = clock();
    Gia_ManLevelNum( p );
//    assert( p->pReprs == NULL );
/*
    // set the number of registers (switch activity will be combinational)
    Gia_ManSetRegNum( p, 0 );
    if ( pPars->fPower )
    {
        vSwitching  = SGia_ManComputeSwitchProbs( p, 48, 16, 0 );
        if ( pPars->fVerbose )
        {
            ABC_PRT( "Computing switching activity", clock() - clk );
        }
        pSwitching  = (float *)vSwitching->pArray;
        vSwitching2 = Vec_IntStart( Gia_ManObjNumMax(p) );
        pSwitching2 = (float *)vSwitching2->pArray;
    }
*/
    // start the mapping manager and set its parameters
    pIfMan = If_ManStart( pPars );
//    pIfMan->vSwitching = vSwitching2;
    // load the AIG into the mapper
    Gia_ManCreateRefs( p );
    Gia_ManForEachObj( p, pNode, i )
    {
        if ( Gia_ObjIsAnd(pNode) )
            pIfObj = If_ManCreateAnd( pIfMan, 
                If_NotCond( (If_Obj_t *)Vec_PtrEntry(vAigToIf, Gia_ObjFaninId0(pNode, i)), Gia_ObjFaninC0(pNode) ), 
                If_NotCond( (If_Obj_t *)Vec_PtrEntry(vAigToIf, Gia_ObjFaninId1(pNode, i)), Gia_ObjFaninC1(pNode) ) );
        else if ( Gia_ObjIsCi(pNode) )
        {
            pIfObj = If_ManCreateCi( pIfMan );
            If_ObjSetLevel( pIfObj, Gia_ObjLevel(p,pNode) );
//            Abc_Print( 1, "pi=%d ", pIfObj->Level );
            if ( pIfMan->nLevelMax < (int)pIfObj->Level )
                pIfMan->nLevelMax = (int)pIfObj->Level;
        }
        else if ( Gia_ObjIsCo(pNode) )
        {
            pIfObj = If_ManCreateCo( pIfMan, If_NotCond( (If_Obj_t *)Vec_PtrEntry(vAigToIf, Gia_ObjFaninId0(pNode, i)), Gia_ObjFaninC0(pNode) ) );
//            Abc_Print( 1, "po=%d ", pIfObj->Level );
        }
        else if ( Gia_ObjIsConst0(pNode) )
            pIfObj = If_Not(If_ManConst1( pIfMan ));
        else // add the node to the mapper
            assert( 0 );
        // save the result
        assert( Vec_PtrEntry(vAigToIf, i) == NULL );
        Vec_PtrWriteEntry( vAigToIf, i, pIfObj );
//        if ( vSwitching2 )
//            pSwitching2[pIfObj->Id] = pSwitching[pNode->Id];    
        // set up the choice node
/*
//        if ( p->pReprs && p->pNexts && Gia_ObjIsHead( p, i ) )
        if ( p->pNexts && Gia_ObjNext(p, i) && Gia_ObjRefsId(p, i) )
        {
            int iPrev, iFanin;
            pIfMan->nChoices++;
            for ( iPrev = i, iFanin = Gia_ObjNext(p, i); iFanin; iPrev = iFanin, iFanin = Gia_ObjNext(p, iFanin) )
                If_ObjSetChoice( Vec_PtrEntry(vAigToIf,iPrev), Vec_PtrEntry(vAigToIf,iFanin) );
            If_ManCreateChoice( pIfMan, Vec_PtrEntry(vAigToIf,i) );
        }
*/
/*        // set up the choice node
        if ( Gia_ObjIsChoice( p, pNode ) )
        {
            pIfMan->nChoices++;
            for ( pPrev = pNode, pFanin = Gia_ObjEquiv(p, pNode); pFanin; pPrev = pFanin, pFanin = Gia_ObjEquiv(p, pFanin) )
                If_ObjSetChoice( pPrev->pData, pFanin->pData );
            If_ManCreateChoice( pIfMan, pNode->pData );
        }
//        assert( If_ObjLevel(pIfObj) == Gia_ObjLevel(pNode) );
*/
    }
//    if ( vSwitching )
//        Vec_IntFree( vSwitching );
    return pIfMan;
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int * Gia_ManFromIf( If_Man_t * pIfMan, Gia_Man_t * p, Vec_Ptr_t * vAigToIf )
{
    int * pMapping, iOffset;
    Vec_Ptr_t * vIfToAig;
    Gia_Obj_t * pObj, * pObjRepr;
    If_Obj_t * pIfObj;
    If_Cut_t * pCutBest;
    int i, k, j, nLeaves, * ppLeaves;
    int nItems = 0;
    assert( Gia_ManCiNum(p) == If_ManCiNum(pIfMan) );
    assert( Gia_ManCoNum(p) == If_ManCoNum(pIfMan) );
    assert( Gia_ManAndNum(p) == If_ManAndNum(pIfMan) );
    // create mapping of IF to AIG
    vIfToAig = Vec_PtrStart( If_ManObjNum(pIfMan) );
    Gia_ManForEachObj( p, pObj, i )
    {
        pIfObj = (If_Obj_t *)Vec_PtrEntry( vAigToIf, i );
        Vec_PtrWriteEntry( vIfToAig, pIfObj->Id, pObj );
        if ( !Gia_ObjIsAnd(pObj) || pIfObj->nRefs == 0 )
            continue;
        nItems += 2 + If_CutLeaveNum( If_ObjCutBest(pIfObj) );
    }
    // construct the network
    pMapping = ABC_CALLOC( int, Gia_ManObjNum(p) + nItems );
    iOffset = Gia_ManObjNum(p);
    Gia_ManForEachObj( p, pObj, i )
    {
        pIfObj = (If_Obj_t *)Vec_PtrEntry( vAigToIf, i );
        if ( !Gia_ObjIsAnd(pObj) || pIfObj->nRefs == 0 )
            continue;
        pCutBest = If_ObjCutBest( pIfObj );
        nLeaves  = If_CutLeaveNum( pCutBest ); 
        ppLeaves = If_CutLeaves( pCutBest );
        // create node
        k = iOffset;
        pMapping[k++] = nLeaves;
        for ( j = 0; j < nLeaves; j++ )
        {
            pObjRepr = (Gia_Obj_t *)Vec_PtrEntry( vIfToAig, ppLeaves[j] );
            pMapping[k++] = Gia_ObjId( p, pObjRepr );
        }
        pMapping[k++] = i;
        pMapping[i] = iOffset;
        iOffset = k;
    }
    assert( iOffset <= Gia_ManObjNum(p) + nItems );
    Vec_PtrFree( vIfToAig );
//    pNtk->pManTime = Tim_ManDup( pIfMan->pManTim, 0 );
    return pMapping;
}

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

  Synopsis    [Interface with the FPGA mapping package.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_MappingIf( Gia_Man_t * p, If_Par_t * pPars )
{
    If_Man_t * pIfMan;
    Vec_Ptr_t * vAigToIf;
    // set the arrival times
    pPars->pTimesArr = ABC_ALLOC( float, Gia_ManCiNum(p) );
    memset( pPars->pTimesArr, 0, sizeof(float) * Gia_ManCiNum(p) );
    // translate into the mapper
    vAigToIf = Vec_PtrStart( Gia_ManObjNum(p) );
    pIfMan = Gia_ManToIf( p, pPars, vAigToIf );    
    if ( pIfMan == NULL )
    {
        Vec_PtrFree( vAigToIf );
        return 0;
    }
//    pIfMan->pManTim = Tim_ManDup( pManTime, 0 );
    if ( !If_ManPerformMapping( pIfMan ) )
    {
        Vec_PtrFree( vAigToIf );
        If_ManStop( pIfMan );
        return 0;
    }
    // transform the result of mapping into the new network
    ABC_FREE( p->pMapping );
    p->pMapping = Gia_ManFromIf( pIfMan, p, vAigToIf );
//    if ( pPars->fBidec && pPars->nLutSize <= 8 )
//        Gia_ManBidecResyn( pNtk, 0 );
    If_ManStop( pIfMan );
    Vec_PtrFree( vAigToIf );
    return 1;
}


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

  Synopsis    [Prints mapping statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManPrintMappingStats( Gia_Man_t * p )
{
    int * pLevels;
    int i, k, iFan, nLutSize = 0, nLuts = 0, nFanins = 0, LevelMax = 0;
    if ( !p->pMapping )
        return;
    pLevels = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManForEachLut( p, i )
    {
        nLuts++;
        nFanins += Gia_ObjLutSize(p, i);
        nLutSize = Abc_MaxInt( nLutSize, Gia_ObjLutSize(p, i) );
        Gia_LutForEachFanin( p, i, iFan, k )
            pLevels[i] = Abc_MaxInt( pLevels[i], pLevels[iFan] );
        pLevels[i]++;
        LevelMax = Abc_MaxInt( LevelMax, pLevels[i] );
    }
    ABC_FREE( pLevels );
    Abc_Print( 1, "mapping (K=%d)  :  ", nLutSize );
    Abc_Print( 1, "lut =%7d  ", nLuts );
    Abc_Print( 1, "edge =%8d  ", nFanins );
    Abc_Print( 1, "lev =%5d  ", LevelMax );
    Abc_Print( 1, "mem =%5.2f MB", 4.0*(Gia_ManObjNum(p) + 2*nLuts + nFanins)/(1<<20) );
    Abc_Print( 1, "\n" );
}

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

  Synopsis    [Prints mapping statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManLutFaninCount( Gia_Man_t * p )
{
    int i, Counter = 0;
    Gia_ManForEachLut( p, i )
        Counter += Gia_ObjLutSize(p, i);
    return Counter;
}

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

  Synopsis    [Prints mapping statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManLutSizeMax( Gia_Man_t * p )
{
    int i, nSizeMax = -1;
    Gia_ManForEachLut( p, i )
        nSizeMax = Abc_MaxInt( nSizeMax, Gia_ObjLutSize(p, i) );
    return nSizeMax;
}

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

  Synopsis    [Prints mapping statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManLutNum( Gia_Man_t * p )
{
    int i, Counter = 0;
    Gia_ManForEachLut( p, i )
        Counter ++;
    return Counter;
}

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

  Synopsis    [Prints mapping statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManLutLevel( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, k, iFan, Level;
    int * pLevels = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManForEachLut( p, i )
    {
        Level = 0;
        Gia_LutForEachFanin( p, i, iFan, k )
            if ( Level < pLevels[iFan] )
                Level = pLevels[iFan];
        pLevels[i] = Level + 1;
    }
    Level = 0;
    Gia_ManForEachCo( p, pObj, k )
        if ( Level < pLevels[Gia_ObjFaninId0p(p, pObj)] )
            Level = pLevels[Gia_ObjFaninId0p(p, pObj)];
    ABC_FREE( pLevels );
    return Level;
}

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

  Synopsis    [Assigns levels.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSetRefsMapped( Gia_Man_t * p )  
{
    Gia_Obj_t * pObj;
    int i, k, iFan;
    ABC_FREE( p->pRefs );
    p->pRefs = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManForEachCo( p, pObj, i )
        Gia_ObjRefInc( p, Gia_ObjFanin0(pObj) );
    Gia_ManForEachLut( p, i )
        Gia_LutForEachFanin( p, i, iFan, k )
            Gia_ObjRefInc( p, Gia_ManObj(p, iFan) );
}

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

  Synopsis    [Prints NPN class statistics.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManPrintNpnClasses( Gia_Man_t * p )
{
    extern char ** Kit_DsdNpn4ClassNames();
    char ** pNames = Kit_DsdNpn4ClassNames();
    Vec_Int_t * vLeaves, * vTruth, * vVisited;
    int * pLutClass, ClassCounts[222] = {0};
    int i, k, iFan, Class, OtherClasses, OtherClasses2, nTotal, Counter, Counter2;
    unsigned * pTruth;
    assert( p->pMapping != NULL );
    assert(  Gia_ManLutSizeMax( p ) <= 4 );
    vLeaves   = Vec_IntAlloc( 100 );
    vVisited  = Vec_IntAlloc( 100 );
    vTruth    = Vec_IntAlloc( (1<<16) );
    pLutClass = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManCleanTruth( p );
    Gia_ManForEachLut( p, i )
    {
        if ( Gia_ObjLutSize(p,i) > 4 )
            continue;
        Vec_IntClear( vLeaves );
        Gia_LutForEachFanin( p, i, iFan, k )
            Vec_IntPush( vLeaves, iFan );
        for ( ; k < 4; k++ )
            Vec_IntPush( vLeaves, 0 );
        pTruth = Gia_ManConvertAigToTruth( p, Gia_ManObj(p, i), vLeaves, vTruth, vVisited );
        Class = Dar_LibReturnClass( *pTruth );
        ClassCounts[ Class ]++;
        pLutClass[i] = Class;
    }
    Vec_IntFree( vLeaves );
    Vec_IntFree( vTruth );
    Vec_IntFree( vVisited );
    Vec_IntFreeP( &p->vTruths );
    nTotal = 0;
    for ( i = 0; i < 222; i++ )
        nTotal += ClassCounts[i];
    Abc_Print( 1, "NPN CLASS STATISTICS (for %d LUT4 present in the current mapping):\n", nTotal );
    OtherClasses = 0;
    for ( i = 0; i < 222; i++ )
    {
        if ( ClassCounts[i] == 0 )
            continue;
        if ( 100.0 * ClassCounts[i] / (nTotal+1) < 0.1 ) // do not show anything below 0.1 percent
            continue;
        OtherClasses += ClassCounts[i];
        Abc_Print( 1, "Class %3d :  Count = %6d   (%7.2f %%)   %s\n", 
            i, ClassCounts[i], 100.0 * ClassCounts[i] / (nTotal+1), pNames[i] );
    }
    OtherClasses = nTotal - OtherClasses;
    Abc_Print( 1, "Other     :  Count = %6d   (%7.2f %%)\n", 
        OtherClasses, 100.0 * OtherClasses / (nTotal+1) );
    // count the number of LUTs that have MUX function and two fanins with MUX functions
    OtherClasses = OtherClasses2 = 0;
    ABC_FREE( p->pRefs );
    Gia_ManSetRefsMapped( p );
    Gia_ManForEachLut( p, i )
    {
        if ( pLutClass[i] != 109 )
            continue;
        Counter = Counter2 = 0;
        Gia_LutForEachFanin( p, i, iFan, k )
        {
            Counter  += (pLutClass[iFan] == 109);
            Counter2 += (pLutClass[iFan] == 109) && (Gia_ObjRefsId(p, iFan) == 1);
        }
        OtherClasses  += (Counter > 1);
        OtherClasses2 += (Counter2 > 1);
//            Abc_Print( 1, "%d -- ", pLutClass[i] );
//            Gia_LutForEachFanin( p, i, iFan, k )
//                Abc_Print( 1, "%d ", pLutClass[iFan] );
//            Abc_Print( 1, "\n" );
    }
    ABC_FREE( p->pRefs );
    Abc_Print( 1, "Approximate number of 4:1 MUX structures: All = %6d  (%7.2f %%)  MFFC = %6d  (%7.2f %%)\n", 
        OtherClasses,  100.0 * OtherClasses  / (nTotal+1),
        OtherClasses2, 100.0 * OtherClasses2 / (nTotal+1) );
    ABC_FREE( pLutClass );
}

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


ABC_NAMESPACE_IMPL_END