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

  FileName    [amapMatch.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Technology mapper for standard cells.]

  Synopsis    []

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "amapInt.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Duplicates the cut using new memory manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Amap_Cut_t * Amap_ManDupCut( Amap_Man_t * p, Amap_Cut_t * pCut )
{
    Amap_Cut_t * pNew;
    int nBytes = sizeof(Amap_Cut_t) + sizeof(int) * pCut->nFans;
    pNew = (Amap_Cut_t *)Aig_MmFlexEntryFetch( p->pMemCutBest, nBytes );
    memcpy( pNew, pCut, (size_t)nBytes );
    return pNew;
}

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

  Synopsis    [Starts the match with cut and set.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Amap_ManMatchStart( Amap_Mat_t * p, Amap_Cut_t * pCut, Amap_Set_t * pSet )
{
    memset( p, 0, sizeof(Amap_Mat_t) );
    p->pCut = pCut;
    p->pSet = pSet;
}

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

  Synopsis    [Cleans reference counters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_ManCleanRefs( Amap_Man_t * p )
{
    Amap_Obj_t * pObj;
    int i;
    Amap_ManForEachObj( p, pObj, i )
        pObj->nFouts[0] = pObj->nFouts[1] = 0;
}

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

  Synopsis    [Computes delay.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Amap_ManMaxDelay( Amap_Man_t * p )
{
    Amap_Obj_t * pObj;
    float Delay = 0.0;
    int i;
    Amap_ManForEachPo( p, pObj, i )
        Delay = Abc_MaxInt( Delay, Amap_ObjFanin0(p,pObj)->Best.Delay );
    return Delay;
}

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

  Synopsis    [Cleans reference counters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_ManCleanData( Amap_Man_t * p )
{
    Amap_Obj_t * pObj;
    int i;
//    Amap_ManForEachNode( p, pObj, i )
//        ABC_FREE( pObj->pData );
    Amap_ManForEachObj( p, pObj, i )
        pObj->pData = NULL;
}

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

  Synopsis    [Compute nodes used in the mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Amap_ManComputeMapping_rec( Amap_Man_t * p, Amap_Obj_t * pObj, int fCompl )
{
    Amap_Mat_t * pM = &pObj->Best;
    Amap_Obj_t * pFanin;
    Amap_Gat_t * pGate;
    int i, iFanin, fComplFanin;
    float Area;
    if ( pObj->nFouts[fCompl]++ + pObj->nFouts[!fCompl] > 0 )
        return 0.0;
    if ( Amap_ObjIsPi(pObj) || Amap_ObjIsConst1(pObj) )
        return 0.0;
    pGate = Amap_LibGate( p->pLib, pM->pSet->iGate );
    assert( pGate->nPins == pM->pCut->nFans );
    Area = pGate->dArea;
    for ( i = 0; i < (int)pGate->nPins; i++ )
    {
        iFanin = Abc_Lit2Var( pM->pSet->Ins[i] );
        pFanin = Amap_ManObj( p, Abc_Lit2Var(pM->pCut->Fans[iFanin]) );
        fComplFanin = Abc_LitIsCompl( pM->pSet->Ins[i] ) ^ Abc_LitIsCompl( pM->pCut->Fans[iFanin] );
        Area += Amap_ManComputeMapping_rec( p, pFanin, fComplFanin );
    }
    return Area;
}

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

  Synopsis    [Compute nodes used in the mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Amap_ManComputeMapping( Amap_Man_t * p )
{
    Amap_Obj_t * pObj;
    float Area = 0.0;
    int i;
    Amap_ManCleanRefs( p );
    Amap_ManForEachPo( p, pObj, i )
        Area += Amap_ManComputeMapping_rec( p, Amap_ObjFanin0(p, pObj), Amap_ObjFaninC0(pObj) );
    return Area;
}

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

  Synopsis    [Counts the number of inverters to be added.]

  Description [Should be called after mapping has been set.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Amap_ManCountInverters( Amap_Man_t * p )
{
    Amap_Obj_t * pObj;
    int i, Counter = 0;
    Amap_ManForEachObj( p, pObj, i )
        Counter += (int)(pObj->nFouts[!pObj->fPolar] > 0);
    return Counter;
}

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

  Synopsis    [Compare two matches.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Amap_CutCompareDelay( Amap_Man_t * p, Amap_Mat_t * pM0, Amap_Mat_t * pM1 )
{
    // compare delay
    if ( pM0->Delay < pM1->Delay - p->pPars->fEpsilon )
        return -1;
    if ( pM0->Delay > pM1->Delay + p->pPars->fEpsilon )
        return 1;

    // compare area flows
    if ( pM0->Area < pM1->Area - p->pPars->fEpsilon )
        return -1;
    if ( pM0->Area > pM1->Area + p->pPars->fEpsilon )
        return 1;

    // compare average fanouts
    if ( pM0->AveFan > pM1->AveFan - p->pPars->fEpsilon )
        return -1;
    if ( pM0->AveFan < pM1->AveFan + p->pPars->fEpsilon )
        return 1;
    return 1;
}
static inline int Amap_CutCompareArea( Amap_Man_t * p, Amap_Mat_t * pM0, Amap_Mat_t * pM1 )
{
    // compare area flows
    if ( pM0->Area < pM1->Area - p->pPars->fEpsilon )
        return -1;
    if ( pM0->Area > pM1->Area + p->pPars->fEpsilon )
        return 1;

    // compare average fanouts
    if ( pM0->AveFan > pM1->AveFan - p->pPars->fEpsilon )
        return -1;
    if ( pM0->AveFan < pM1->AveFan + p->pPars->fEpsilon )
        return 1;

    // compare delay
    if ( pM0->Delay < pM1->Delay - p->pPars->fEpsilon )
        return -1;
    if ( pM0->Delay > pM1->Delay + p->pPars->fEpsilon )
        return 1;
    return 1;
}

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

  Synopsis    [Counts area while dereferencing the match.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline float Amap_CutAreaDeref( Amap_Man_t * p, Amap_Mat_t * pM )
{
    Amap_Obj_t * pFanin;
    int i, fCompl;
    float Area = Amap_LibGate( p->pLib, pM->pSet->iGate )->dArea;
    Amap_MatchForEachFaninCompl( p, pM, pFanin, fCompl, i )
    {
        assert( Amap_ObjRefsTotal(pFanin) > 0 );
        if ( (int)pFanin->fPolar != fCompl && pFanin->nFouts[fCompl] == 1 )
            Area += p->fAreaInv;
        if ( --pFanin->nFouts[fCompl] + pFanin->nFouts[!fCompl] == 0 && Amap_ObjIsNode(pFanin) )
            Area += Amap_CutAreaDeref( p, &pFanin->Best );
    }
    return Area;
}

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

  Synopsis    [Counts area while referencing the match.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline float Amap_CutAreaRef2( Amap_Man_t * p, Amap_Mat_t * pM, Vec_Ptr_t * vTemp, int Limit )
{
    Amap_Obj_t * pFanin;
    int i, fCompl;
    float Area = Amap_LibGate( p->pLib, pM->pSet->iGate )->dArea;
    if ( Limit == 0 ) return Area;
    Amap_MatchForEachFaninCompl( p, pM, pFanin, fCompl, i )
    {
        Vec_PtrPush( vTemp, pFanin->nFouts + fCompl );
        assert( Amap_ObjRefsTotal(pFanin) >= 0 );
        if ( (int)pFanin->fPolar != fCompl && pFanin->nFouts[fCompl] == 0 )
            Area += p->fAreaInv;
        if ( pFanin->nFouts[fCompl]++ + pFanin->nFouts[!fCompl] == 0 && Amap_ObjIsNode(pFanin) )
            Area += Amap_CutAreaRef2( p, &pFanin->Best, vTemp, Limit-1 );
    }
    return Area;
}

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

  Synopsis    [Derives area of the match for a non-referenced node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline float Amap_CutAreaDerefed2( Amap_Man_t * p, Amap_Obj_t * pNode, Amap_Mat_t * pM )
{
    int nRecurLevels = 8;
    int fComplNew, i, * pInt;
    float aResult;
    Vec_PtrClear( p->vTempP );
    aResult = Amap_CutAreaRef2( p, pM, p->vTempP, nRecurLevels );
    //Amap_CutAreaDeref( p, pM );
    Vec_PtrForEachEntry( int *, p->vTempP, pInt, i )
        (*pInt)--;
    // if node is needed in another polarity, add inverter
    fComplNew = pM->pCut->fInv ^ pM->pSet->fInv;
    if ( pNode->nFouts[fComplNew] == 0 && pNode->nFouts[!fComplNew] > 0 )
        aResult += p->fAreaInv;
    return aResult;
}

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

  Synopsis    [Counts area while referencing the match.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline float Amap_CutAreaRef( Amap_Man_t * p, Amap_Mat_t * pM )
{
    Amap_Obj_t * pFanin;
    int i, fCompl;
    float Area = Amap_LibGate( p->pLib, pM->pSet->iGate )->dArea;
    Amap_MatchForEachFaninCompl( p, pM, pFanin, fCompl, i )
    {
        assert( Amap_ObjRefsTotal(pFanin) >= 0 );
        if ( (int)pFanin->fPolar != fCompl && pFanin->nFouts[fCompl] == 0 )
            Area += p->fAreaInv;
        if ( pFanin->nFouts[fCompl]++ + pFanin->nFouts[!fCompl] == 0 && Amap_ObjIsNode(pFanin) )
            Area += Amap_CutAreaRef( p, &pFanin->Best );
    }
    return Area;
}

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

  Synopsis    [Derives area of the match for a non-referenced node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline float Amap_CutAreaDerefed( Amap_Man_t * p, Amap_Obj_t * pNode, Amap_Mat_t * pM )
{
    float aResult, aResult2;
    int fComplNew;
    aResult2 = Amap_CutAreaRef( p, pM );
    aResult  = Amap_CutAreaDeref( p, pM );
    assert( aResult > aResult2 - p->fEpsilonInternal );
    assert( aResult < aResult2 + p->fEpsilonInternal );
    // if node is needed in another polarity, add inverter
    fComplNew = pM->pCut->fInv ^ pM->pSet->fInv;
    if ( pNode->nFouts[fComplNew] == 0 && pNode->nFouts[!fComplNew] > 0 )
        aResult += p->fAreaInv;
    return aResult;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Amap_CutAreaTest( Amap_Man_t * p, Amap_Obj_t * pNode )
{
    float aResult, aResult2;
    if ( Amap_ObjRefsTotal(pNode) == 0 )
    {
        aResult2 = Amap_CutAreaRef( p, &pNode->Best );
        aResult  = Amap_CutAreaDeref( p, &pNode->Best );
        assert( aResult > aResult2 - p->fEpsilonInternal );
        assert( aResult < aResult2 + p->fEpsilonInternal );
    }
    else
    {
        aResult  = Amap_CutAreaDeref( p, &pNode->Best );
        aResult2 = Amap_CutAreaRef( p, &pNode->Best );
        assert( aResult > aResult2 - p->fEpsilonInternal );
        assert( aResult < aResult2 + p->fEpsilonInternal );
    }
}

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

  Synopsis    [Derives parameters for the match.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Amap_ManMatchGetFlows( Amap_Man_t * p, Amap_Mat_t * pM )
{
    Amap_Mat_t * pMFanin;
    Amap_Obj_t * pFanin;
    Amap_Gat_t * pGate;
    float AddOn; 
    int i;
    pGate = Amap_LibGate( p->pLib, pM->pSet->iGate );
    assert( pGate->nPins == pM->pCut->nFans );
    assert( pM->Area == 0.0 );
    pM->Area = pGate->dArea;
    pM->AveFan = 0.0;
    pM->Delay = 0.0;
    Amap_MatchForEachFanin( p, pM, pFanin, i )
    {
        pMFanin = &pFanin->Best;
        pM->Delay = Abc_MaxInt( pM->Delay, pMFanin->Delay );
        pM->AveFan += Amap_ObjRefsTotal(pFanin);
//        if ( Amap_ObjRefsTotal(pFanin) == 0 )
//            pM->Area += pMFanin->Area;
//        else
//            pM->Area += pMFanin->Area / pFanin->EstRefs;
        AddOn = Amap_ObjRefsTotal(pFanin) == 0 ? pMFanin->Area : pMFanin->Area / pFanin->EstRefs; 
        if ( pM->Area >= (float)1e32 || AddOn >= (float)1e32 )
            pM->Area = (float)1e32;
        else 
            pM->Area += AddOn;
    }
    pM->AveFan /= pGate->nPins;
    pM->Delay += 1.0;
}

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

  Synopsis    [Derives parameters for the match.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Amap_ManMatchGetExacts( Amap_Man_t * p, Amap_Obj_t * pNode, Amap_Mat_t * pM )
{
    Amap_Mat_t * pMFanin;
    Amap_Obj_t * pFanin;
    Amap_Gat_t * pGate;
    int i;
    pGate = Amap_LibGate( p->pLib, pM->pSet->iGate );
    assert( pGate->nPins == pM->pCut->nFans );
    assert( pM->Area == 0.0 );
    pM->AveFan = 0.0;
    pM->Delay = 0.0;
    Amap_MatchForEachFanin( p, pM, pFanin, i )
    {
        pMFanin = &pFanin->Best;
        pM->Delay = Abc_MaxInt( pM->Delay, pMFanin->Delay );
        pM->AveFan += Amap_ObjRefsTotal(pFanin);
    }
    pM->AveFan /= pGate->nPins;
    pM->Delay += 1.0;
    //pM->Area = Amap_CutAreaDerefed( p, pNode, pM );
    pM->Area = Amap_CutAreaDerefed2( p, pNode, pM );
}

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

  Synopsis    [Computes the best match at each node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_ManMatchNode( Amap_Man_t * p, Amap_Obj_t * pNode, int fFlow, int fRefs )
{
    int fVerbose = 0; //(pNode->Level == 2 || pNode->Level == 4);
    int fVeryVerbose = fVerbose;

    Amap_Mat_t MA = {0}, MD = {0}, M = {0};
    Amap_Mat_t * pMBestA = &MA, * pMBestD = &MD, * pMThis = &M, * pMBest;
    Amap_Cut_t * pCut;
    Amap_Set_t * pSet;
    Amap_Nod_t * pNod;
    int i;

    if ( fRefs )
        pNode->EstRefs = (float)((2.0 * pNode->EstRefs + Amap_ObjRefsTotal(pNode)) / 3.0);
    else
        pNode->EstRefs = (float)pNode->nRefs;
    if ( fRefs && Amap_ObjRefsTotal(pNode) > 0 )
        Amap_CutAreaDeref( p, &pNode->Best );

    if ( fVerbose )
        printf( "\nNode %d (%d)\n", pNode->Id, pNode->Level );

    pMBestA->pCut = pMBestD->pCut = NULL;
    Amap_NodeForEachCut( pNode, pCut, i )
    {
        if ( pCut->iMat == 0 )
            continue;
        pNod = Amap_LibNod( p->pLib, pCut->iMat );
        Amap_LibNodeForEachSet( pNod, pSet )
        {
            Amap_ManMatchStart( pMThis, pCut, pSet );
            if ( fFlow )
                Amap_ManMatchGetFlows( p, pMThis );
            else
                Amap_ManMatchGetExacts( p, pNode, pMThis );
            if ( pMBestD->pCut == NULL || Amap_CutCompareDelay(p, pMBestD, pMThis) == 1 )
                *pMBestD = *pMThis;
            if ( pMBestA->pCut == NULL || Amap_CutCompareArea(p, pMBestA, pMThis) == 1 )
                *pMBestA = *pMThis;

            if ( fVeryVerbose ) 
            {
                printf( "Cut %2d (%d) :  ", i, pCut->nFans );
                printf( "Gate %10s  ",      Amap_LibGate(p->pLib, pMThis->pSet->iGate)->pName );
                printf( "%s  ",             pMThis->pSet->fInv ? "inv" : "   " );
                printf( "Delay %5.2f  ",    pMThis->Delay );
                printf( "Area %5.2f  ",     pMThis->Area );
                printf( "\n" );
            }
        }
    }

    if ( Abc_AbsFloat(pMBestA->Area - pMBestD->Area) / pMBestD->Area >= p->pPars->fADratio * Abc_AbsFloat(pMBestA->Delay - pMBestD->Delay) / pMBestA->Delay )
        pMBest = pMBestA;
    else
        pMBest = pMBestD;

    if ( fVerbose )
    {
        printf( "BEST MATCHA:  " );
        printf( "Gate %10s  ",    Amap_LibGate(p->pLib, pMBestA->pSet->iGate)->pName );
        printf( "%s  ",           pMBestA->pSet->fInv ? "inv" : "   " );
        printf( "Delay %5.2f  ",  pMBestA->Delay );
        printf( "Area %5.2f  ",   pMBestA->Area );
        printf( "\n" ); 

        printf( "BEST MATCHD:  " );
        printf( "Gate %10s  ",    Amap_LibGate(p->pLib, pMBestD->pSet->iGate)->pName );
        printf( "%s  ",           pMBestD->pSet->fInv ? "inv" : "   " );
        printf( "Delay %5.2f  ",  pMBestD->Delay );
        printf( "Area %5.2f  ",   pMBestD->Area );
        printf( "\n" ); 

        printf( "BEST MATCH :  " );
        printf( "Gate %10s  ",    Amap_LibGate(p->pLib, pMBest->pSet->iGate)->pName );
        printf( "%s  ",           pMBest->pSet->fInv ? "inv" : "   " );
        printf( "Delay %5.2f  ",  pMBest->Delay );
        printf( "Area %5.2f  ",   pMBest->Area );
        printf( "\n" ); 
    }

    pNode->fPolar = pMBest->pCut->fInv ^ pMBest->pSet->fInv;
    pNode->Best = *pMBest;
    pNode->Best.pCut = Amap_ManDupCut( p, pNode->Best.pCut );
    if ( fRefs && Amap_ObjRefsTotal(pNode) > 0 )
        Amap_CutAreaRef( p, &pNode->Best );
}

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

  Synopsis    [Performs one round of mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_ManMatch( Amap_Man_t * p, int fFlow, int fRefs )
{
    Aig_MmFlex_t * pMemOld;
    Amap_Obj_t * pObj;
    float Area;
    int i, nInvs;
    abctime clk = Abc_Clock();
    pMemOld = p->pMemCutBest;
    p->pMemCutBest = Aig_MmFlexStart();
    Amap_ManForEachNode( p, pObj, i )
        if ( pObj->pData )
            Amap_ManMatchNode( p, pObj, fFlow, fRefs );
    Aig_MmFlexStop( pMemOld, 0 );
    Area = Amap_ManComputeMapping( p );
    nInvs = Amap_ManCountInverters( p );
if ( p->pPars->fVerbose )
{
    printf( "Area =%9.2f. Gate =%9.2f. Inv =%9.2f. (%6d.) Delay =%6.2f. ", 
        Area + nInvs * p->fAreaInv, 
        Area, nInvs * p->fAreaInv, nInvs,
        Amap_ManMaxDelay(p) );
ABC_PRT( "Time ", Abc_Clock() - clk );
}
    // test procedures
//    Amap_ManForEachNode( p, pObj, i )
//        Amap_CutAreaTest( p, pObj );
}

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

  Synopsis    [Performs mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_ManMap( Amap_Man_t * p )
{
    int i;
    Amap_ManMerge( p );
    for ( i = 0; i < p->pPars->nIterFlow; i++ )
        Amap_ManMatch( p, 1, i>0 );
    for ( i = 0; i < p->pPars->nIterArea; i++ )
        Amap_ManMatch( p, 0, p->pPars->nIterFlow>0||i>0 );
/*
    for ( i = 0; i < p->pPars->nIterFlow; i++ )
        Amap_ManMatch( p, 1, 1 );
    for ( i = 0; i < p->pPars->nIterArea; i++ )
        Amap_ManMatch( p, 0, 1 );
*/
    Amap_ManCleanData( p );
}

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


ABC_NAMESPACE_IMPL_END

