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

  FileName    [abcIfif.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [Experiment with technology mapping.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "src/base/abc/abc.h"
#include "src/map/if/if.h"

ABC_NAMESPACE_IMPL_START

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

#define IFIF_MAX_LEAVES 6


typedef struct Abc_IffObj_t_       Abc_IffObj_t;
struct Abc_IffObj_t_ 
{
    float            Delay[IFIF_MAX_LEAVES+1];     // separate delay
//    int              nLeaves;
//    int              pLeaves[IFIF_MAX_LEAVES];
};

typedef struct Abc_IffMan_t_       Abc_IffMan_t;
struct Abc_IffMan_t_ 
{
    Abc_Ntk_t *      pNtk;
    Ifif_Par_t *     pPars;
    // internal data
    int              nObjs;
    Abc_IffObj_t *   pObjs;
};

static inline Abc_IffObj_t *  Abc_IffObj( Abc_IffMan_t * p, int i )                             { assert( i >= 0 && i < p->nObjs ); return p->pObjs + i;   }
static inline float           Abc_IffDelay( Abc_IffMan_t * p, Abc_Obj_t * pObj, int fDelay1 )   { return Abc_IffObj(p, Abc_ObjId(pObj))->Delay[fDelay1];   }

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Abc_IffMan_t * Abc_NtkIfifStart( Abc_Ntk_t * pNtk, Ifif_Par_t * pPars )
{
    Abc_IffMan_t * p;
    p = ABC_CALLOC( Abc_IffMan_t, 1 );
    p->pNtk       = pNtk;
    p->pPars      = pPars;
    // internal data
    p->nObjs      = Abc_NtkObjNumMax( pNtk );
    p->pObjs      = ABC_CALLOC( Abc_IffObj_t, p->nObjs );
    return p;
}
void Abc_NtkIfifStop( Abc_IffMan_t * p )
{
    // internal data
    ABC_FREE( p->pObjs );
    ABC_FREE( p );
}

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

  Synopsis    [Collects fanins into ppNodes in decreasing order.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_ObjSortByDelay( Abc_IffMan_t * p, Abc_Obj_t * pObj, int fDelay1, Abc_Obj_t ** ppNodes )
{
    Abc_Obj_t * pFanin;
    int i, a, k = 0;
    Abc_ObjForEachFanin( pObj, pFanin, i )
    {
        ppNodes[k++] = pFanin;
        if ( Abc_ObjIsCi(pFanin) )
            continue;
        for ( a = k-1; a > 0; a-- )
            if ( Abc_IffDelay(p, ppNodes[a-1], fDelay1) + p->pPars->pLutDelays[a-1] < Abc_IffDelay(p, ppNodes[a], fDelay1) + p->pPars->pLutDelays[a] )
                ABC_SWAP( Abc_Obj_t *, ppNodes[a-1], ppNodes[a] );
    }
/*
    for ( a = 1; a < k; a++ )
    {
        float D1 = Abc_IffDelay(p, ppNodes[a-1], fDelay1);
        float D2 = Abc_IffDelay(p, ppNodes[a], fDelay1);
        if ( Abc_ObjIsCi(ppNodes[a-1]) || Abc_ObjIsCi(ppNodes[a]) )
            continue;
        assert( Abc_IffDelay(p, ppNodes[a-1], fDelay1) + p->pPars->pLutDelays[a-1] >= Abc_IffDelay(p, ppNodes[a], fDelay1) + p->pPars->pLutDelays[a] - 0.01 );
    }
*/
}

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

  Synopsis    [This is the delay which object has all by itself.]

  Description [This delay is stored in Delay0.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Abc_ObjDelay0( Abc_IffMan_t * p, Abc_Obj_t * pObj )
{
    int i;
    float Delay0 = 0;
    Abc_Obj_t * ppNodes[6];
    Abc_ObjSortByDelay( p, pObj, 1, ppNodes );
    for ( i = 0; i < Abc_ObjFaninNum(pObj); i++ )
        Delay0 = Abc_MaxFloat( Delay0, Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i] );
    return Delay0;
}

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

  Synopsis    [This is the delay object has in the structure.]

  Description [This delay is stored in Delay1.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Abc_ObjDelay1( Abc_IffMan_t * p, Abc_Obj_t * pObj )
{
    int i, fVeryVerbose = 0;
//    Abc_IffObj_t * pIfif = Abc_IffObj( p, Abc_ObjId(pObj) );
    Abc_Obj_t * ppNodes[6];
    float Delay1, DelayNew;

    if ( Abc_ObjFaninNum(pObj) == 0 )
        return 0;

    // sort fanins by delay1+LutDelay
    Abc_ObjSortByDelay( p, pObj, 1, ppNodes );

    // print verbose results
    if ( fVeryVerbose )
    {
        printf( "Object %d   Level %d\n", Abc_ObjId(pObj), Abc_ObjLevel(pObj) );
        for ( i = 0; i < Abc_ObjFaninNum(pObj); i++ )
        {
            printf( "Fanin %d : ", i );
            printf( "D0 %3.2f  ",      Abc_IffDelay(p, ppNodes[i], 0) );
            printf( "D0* %3.2f     ",  Abc_IffDelay(p, ppNodes[i], 0) + p->pPars->pLutDelays[i] - p->pPars->DelayWire );
            printf( "D1 %3.2f",        Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i] );
            printf( "\n" );
        }
        printf( "\n" );
    }
/*
    // for the first nDegree delays, sort them by the minimum Delay1+LutDelay and Delay0-Wire+LutDelay
    Delay1 = 0;
    pIfif->nLeaves = 0;
    for ( i = 0; i < Abc_ObjFaninNum(pObj); i++ )
    {
        if ( Abc_ObjIsNode(ppNodes[i]) && pIfif->nLeaves < p->pPars->nDegree )
        {
            DelayNew = Abc_MinFloat( Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i], 
                                     Abc_IffDelay(p, ppNodes[i], 0) + p->pPars->pLutDelays[i] - p->pPars->DelayWire );
            pIfif->pLeaves[pIfif->nLeaves++] = Abc_ObjId(ppNodes[i]);
        }
        else
            DelayNew = Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i];
        Delay1 = Abc_MaxFloat( Delay1, DelayNew );
    }
*/
    // for the first nDegree delays, sort them by the minimum Delay1+LutDelay and Delay0-Wire+LutDelay
    Delay1 = 0;
    for ( i = 0; i < Abc_ObjFaninNum(pObj); i++ )
    {
        if ( i < p->pPars->nDegree )
            DelayNew = Abc_MinFloat( Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i], 
                                     Abc_IffDelay(p, ppNodes[i], 0) + p->pPars->pLutDelays[i] - p->pPars->DelayWire );
        else
            DelayNew = Abc_IffDelay(p, ppNodes[i], 1) + p->pPars->pLutDelays[i];
        Delay1 = Abc_MaxFloat( Delay1, DelayNew );
    }
    assert( Delay1 > 0 );
    return Delay1;
}


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

  Synopsis    [This is the delay which object has all by itself.]

  Description [This delay is stored in Delay0.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Abc_ObjDelayDegree( Abc_IffMan_t * p, Abc_Obj_t * pObj, int d )
{
    int i;
    float Delay0 = 0, DelayNew;
    Abc_Obj_t * ppNodes[6];
    assert( d >= 0 && d <= p->pPars->nDegree );
    Abc_ObjSortByDelay( p, pObj, p->pPars->nDegree, ppNodes );
    for ( i = 0; i < Abc_ObjFaninNum(pObj); i++ )
    {
        DelayNew = Abc_IffDelay(p, ppNodes[i], p->pPars->nDegree) + p->pPars->pLutDelays[i];
        if ( i == 0 && d > 0 )
            DelayNew = Abc_MinFloat(DelayNew, Abc_IffDelay(p, ppNodes[i], d-1) + p->pPars->pLutDelays[i] - p->pPars->DelayWire );
        Delay0 = Abc_MaxFloat( Delay0, DelayNew );
    }
    return Delay0;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkPerformIfif( Abc_Ntk_t * pNtk, Ifif_Par_t * pPars )
{
    Abc_IffMan_t * p;
    Abc_IffObj_t * pIffObj;
    Vec_Ptr_t * vNodes;
    Abc_Obj_t * pObj;
    float Delay, Delay10, DegreeFinal;
    int i, d, Count10;
    assert( pPars->pLutLib->LutMax >= 0 && pPars->pLutLib->LutMax <= IFIF_MAX_LEAVES );
    assert( pPars->nLutSize >= 0 && pPars->nLutSize <= IFIF_MAX_LEAVES );
    assert( pPars->nDegree >= 0 && pPars->nDegree <= IFIF_MAX_LEAVES );
    // convert to AIGs
    Abc_NtkToAig( pNtk );
    Abc_NtkLevel( pNtk );

    // print parameters
    if ( pPars->fVerbose )
    {
        printf( "Running mapper into LUT structures with the following parameters:\n" );
        printf( "Pin+Wire: {" );
        for ( i = 0; i < pPars->pLutLib->LutMax; i++ )
            printf( " %3.2f", pPars->pLutDelays[i] );
        printf( " }  " );    
        printf( "Wire %3.2f  Degree %d  Type: %s\n", 
            pPars->DelayWire, pPars->nDegree, pPars->fCascade? "Cascade" : "Cluster" );
    }

    // start manager
    p = Abc_NtkIfifStart( pNtk, pPars );
//    printf( "Running experiment with LUT delay %d and degree %d (LUT size is %d).\n", DelayWire, nDegree, nLutSize );

    // compute the delay
    vNodes = Abc_NtkDfs( pNtk, 0 );
    Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pObj, i )
    {
        assert( Abc_ObjIsNode(pObj) );
        pIffObj = Abc_IffObj( p, Abc_ObjId(pObj) );

        if ( pPars->fCascade )
        {
            for ( d = 0; d <= pPars->nDegree; d++ )
                pIffObj->Delay[d] = Abc_ObjDelayDegree( p, pObj, d );
        }
        else
        {
            pIffObj->Delay[0] = Abc_ObjDelay0( p, pObj );
            pIffObj->Delay[1] = Abc_ObjDelay1( p, pObj );
        }
    }

    // get final degree number
    if ( pPars->fCascade )
        DegreeFinal = pPars->nDegree;
    else
        DegreeFinal = 1;

    if ( p->pPars->fVeryVerbose )
    Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pObj, i )
    {
        printf( "Node %3d : Lev =%3d   ",  Abc_ObjId(pObj), Abc_ObjLevel(pObj) );
        for ( d = 0; d <= DegreeFinal; d++ )
            printf( "Del%d =%4.2f  ", d, Abc_IffDelay(p, pObj, d) );
        printf( "\n" );
    }
    Vec_PtrFree( vNodes );


    // consider delay at the outputs
    Delay = 0;
    Abc_NtkForEachCo( pNtk, pObj, i )
        Delay = Abc_MaxFloat( Delay, Abc_IffDelay(p, Abc_ObjFanin0(pObj), DegreeFinal) );
    Delay10 = 0.9 * Delay;

    // consider delay at the outputs
    Count10 = 0;
    Abc_NtkForEachCo( pNtk, pObj, i )
        if ( Abc_IffDelay(p, Abc_ObjFanin0(pObj), DegreeFinal) >= Delay10 )
            Count10++;

    printf( "Critical delay %5.2f. Critical outputs %5.2f %%\n", Delay, 100.0 * Count10 / Abc_NtkCoNum(pNtk) );
//    printf( "%.2f %.2f\n", Delay, 100.0 * Count10 / Abc_NtkCoNum(pNtk) );

    // derive a new network

    // stop manager
    Abc_NtkIfifStop( p );
}

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


ABC_NAMESPACE_IMPL_END