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

  FileName    [giaJf.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    []

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "misc/vec/vecSet.h"
#include "misc/vec/vecMem.h"
#include "misc/extra/extra.h"
#include "bool/kit/kit.h"
#include "misc/util/utilTruth.h"
#include "opt/dau/dau.h"

ABC_NAMESPACE_IMPL_START

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

#define JF_LEAF_MAX   6
#define JF_CUT_MAX   16

typedef struct Jf_Cut_t_ Jf_Cut_t; 
struct Jf_Cut_t_
{
    word             Sign;        // signature
    float            Flow;        // flow
    int              Time;        // arrival time
    int              iFunc;       // function 
    int              Cost;        // cut cost
    int              pCut[JF_LEAF_MAX+2]; // cut
};

typedef struct Jf_Man_t_ Jf_Man_t; 
struct Jf_Man_t_
{
    Gia_Man_t *      pGia;        // user's manager
    Jf_Par_t *       pPars;       // users parameter
    Sdm_Man_t *      pDsd;        // extern DSD manager
    Vec_Int_t *      vCnfs;       // costs of elementary CNFs
    Vec_Mem_t *      vTtMem;      // truth table memory and hash table
    Vec_Int_t        vCuts;       // cuts for each node
    Vec_Int_t        vArr;        // arrival time
    Vec_Int_t        vDep;        // departure time
    Vec_Flt_t        vFlow;       // area flow
    Vec_Flt_t        vRefs;       // ref counters
    Vec_Set_t        pMem;        // cut storage
    Vec_Int_t *      vTemp;       // temporary
    float (*pCutCmp) (Jf_Cut_t *, Jf_Cut_t *);// procedure to compare cuts
    abctime          clkStart;    // starting time
    word             CutCount[4]; // statistics
    int              nCoarse;     // coarse nodes
};

static inline int    Jf_ObjIsUnit( Gia_Obj_t * p )          { return !p->fMark0;                                       }
static inline void   Jf_ObjCleanUnit( Gia_Obj_t * p )       { assert(Jf_ObjIsUnit(p)); p->fMark0 = 1;                  }
static inline void   Jf_ObjSetUnit( Gia_Obj_t * p )         { p->fMark0 = 0;                                           }

static inline int    Jf_ObjCutH( Jf_Man_t * p, int i )      { return Vec_IntEntry(&p->vCuts, i);                       }
static inline int *  Jf_ObjCuts( Jf_Man_t * p, int i )      { return (int *)Vec_SetEntry(&p->pMem, Jf_ObjCutH(p, i));  }
static inline int *  Jf_ObjCutBest( Jf_Man_t * p, int i )   { return Jf_ObjCuts(p, i) + 1;                             }
static inline int    Jf_ObjArr( Jf_Man_t * p, int i )       { return Vec_IntEntry(&p->vArr, i);                        }
static inline int    Jf_ObjDep( Jf_Man_t * p, int i )       { return Vec_IntEntry(&p->vDep, i);                        }
static inline float  Jf_ObjFlow( Jf_Man_t * p, int i )      { return Vec_FltEntry(&p->vFlow, i);                       }
static inline float  Jf_ObjRefs( Jf_Man_t * p, int i )      { return Vec_FltEntry(&p->vRefs, i);                       }
//static inline int    Jf_ObjLit( int i, int c )            { return i;                                                }
static inline int    Jf_ObjLit( int i, int c )              { return Abc_Var2Lit( i, c );                              }

static inline int    Jf_CutSize( int * pCut )               { return pCut[0] & 0xF;                                    }  //  4 bits
static inline int    Jf_CutCost( int * pCut )               { return (pCut[0] >> 4) & 0xF;                             }  //  4 bits
static inline int    Jf_CutFunc( int * pCut )               { return ((unsigned)pCut[0] >> 8);                         }  // 24 bits
static inline int    Jf_CutSetAll( int f, int c, int s )    { return (f << 8) | (c << 4) | s;                          }
static inline void   Jf_CutSetSize( int * pCut, int s )     { assert(s>=0 && s<16); pCut[0] ^= (Jf_CutSize(pCut) ^ s); }
static inline void   Jf_CutSetCost( int * pCut, int c )     { assert(c>=0 && c<16); pCut[0] ^=((Jf_CutCost(pCut) ^ c) << 4); }
static inline void   Jf_CutSetFunc( int * pCut, int f )     { assert(f>=0); pCut[0] ^=((Jf_CutFunc(pCut) ^ f) << 8);   }

static inline int    Jf_CutFuncClass( int * pCut )          { return Abc_Lit2Var(Jf_CutFunc(pCut));                    }
static inline int    Jf_CutFuncCompl( int * pCut )          { return Abc_LitIsCompl(Jf_CutFunc(pCut));                 }
static inline int *  Jf_CutLits( int * pCut )               { return pCut + 1;                                         }
static inline int    Jf_CutLit( int * pCut, int i )         { assert(i);return pCut[i];                                }
//static inline int    Jf_CutVar( int * pCut, int i )         { assert(i); return pCut[i];                               }
static inline int    Jf_CutVar( int * pCut, int i )         {  assert(i);return Abc_Lit2Var(pCut[i]);                  }
static inline int    Jf_CutIsTriv( int * pCut, int i )      { return Jf_CutSize(pCut) == 1 && Jf_CutVar(pCut, 1) == i; } 
static inline int    Jf_CutCnfSizeF( Jf_Man_t * p, int f )  { return Vec_IntEntry( p->vCnfs, f );                      }
static inline int    Jf_CutCnfSize( Jf_Man_t * p, int * c ) { return Jf_CutCnfSizeF( p, Jf_CutFuncClass(c) );          }

static inline int    Jf_ObjFunc0( Gia_Obj_t * p, int * c )  { return Abc_LitNotCond(Jf_CutFunc(c), Gia_ObjFaninC0(p)); } 
static inline int    Jf_ObjFunc1( Gia_Obj_t * p, int * c )  { return Abc_LitNotCond(Jf_CutFunc(c), Gia_ObjFaninC1(p)); } 

#define Jf_ObjForEachCut( pList, pCut, i )   for ( i = 0, pCut = pList + 1; i < pList[0]; i++, pCut += Jf_CutSize(pCut) + 1 )
#define Jf_CutForEachLit( pCut, Lit, i )     for ( i = 1; i <= Jf_CutSize(pCut) && (Lit = Jf_CutLit(pCut, i)); i++ )
#define Jf_CutForEachVar( pCut, Var, i )     for ( i = 1; i <= Jf_CutSize(pCut) && (Var = Jf_CutVar(pCut, i)); i++ )

extern int Kit_TruthToGia( Gia_Man_t * pMan, unsigned * pTruth, int nVars, Vec_Int_t * vMemory, Vec_Int_t * vLeaves, int fHash );

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

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

  Synopsis    [Computing references while discounting XOR/MUX.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float * Jf_ManInitRefs( Jf_Man_t * pMan )
{
    Gia_Man_t * p = pMan->pGia;
    Gia_Obj_t * pObj, * pCtrl, * pData0, * pData1;
    float * pRes; int i;
    assert( p->pRefs == NULL );
    p->pRefs = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManForEachAnd( p, pObj, i )
    {
        Gia_ObjRefFanin0Inc( p, pObj );
        if ( Gia_ObjIsBuf(pObj) )
            continue;
        Gia_ObjRefFanin1Inc( p, pObj );
        if ( !Gia_ObjIsMuxType(pObj) )
            continue;
        // discount XOR/MUX
        pCtrl = Gia_ObjRecognizeMux( pObj, &pData1, &pData0 );
        Gia_ObjRefDec( p, Gia_Regular(pCtrl) );
        if ( Gia_Regular(pData1) == Gia_Regular(pData0) )
            Gia_ObjRefDec( p, Gia_Regular(pData1) );
    }
    Gia_ManForEachCo( p, pObj, i )
        Gia_ObjRefFanin0Inc( p, pObj );
    // mark XOR/MUX internal nodes, which are not used elsewhere
    if ( pMan->pPars->fCoarsen )
    {
        pMan->nCoarse = 0;
        Gia_ManForEachAnd( p, pObj, i )
        {
            if ( !Gia_ObjIsMuxType(pObj) )
                continue;
            if ( Gia_ObjRefNum(p, Gia_ObjFanin0(pObj)) == 1 )
            {
                Jf_ObjSetUnit(Gia_ObjFanin0(Gia_ObjFanin0(pObj)));
                Jf_ObjSetUnit(Gia_ObjFanin0(Gia_ObjFanin1(pObj)));
                Jf_ObjCleanUnit(Gia_ObjFanin0(pObj)), pMan->nCoarse++;
            }
            if ( Gia_ObjRefNum(p, Gia_ObjFanin1(pObj)) == 1 )
            {
                Jf_ObjSetUnit(Gia_ObjFanin1(Gia_ObjFanin0(pObj)));
                Jf_ObjSetUnit(Gia_ObjFanin1(Gia_ObjFanin1(pObj)));
                Jf_ObjCleanUnit(Gia_ObjFanin1(pObj)), pMan->nCoarse++;
            }
        }
    }
    // multiply by factor
    pRes = ABC_ALLOC( float, Gia_ManObjNum(p) );
    for ( i = 0; i < Gia_ManObjNum(p); i++ )
        pRes[i] = Abc_MaxInt( 1, p->pRefs[i] );
    return pRes;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Jf_ManProfileClasses( Jf_Man_t * p )
{
    Gia_Obj_t * pObj; 
    int Counts[595] = {0}, Costs[595] = {0};
    int i, iFunc, Total = 0, CostTotal = 0, Other = 0, CostOther = 0;
    printf( "DSD classes that appear in more than %.1f %% of mapped nodes:\n", 0.1 * p->pPars->nVerbLimit );
    Gia_ManForEachAnd( p->pGia, pObj, i )
        if ( !Gia_ObjIsBuf(pObj) && Gia_ObjRefNumId(p->pGia, i) )
        {
            iFunc = Jf_CutFuncClass( Jf_ObjCutBest(p, i) );
            assert( iFunc < 595 );
            if ( p->pPars->fGenCnf )
            {
                Costs[iFunc] += Jf_CutCnfSizeF(p, iFunc);
                CostTotal += Jf_CutCnfSizeF(p, iFunc);
            }
            Counts[iFunc]++;
            Total++;
        }
    CostTotal = Abc_MaxInt(CostTotal, 1);
    Total = Abc_MaxInt(Total, 1);
    for ( i = 0; i < 595; i++ )
        if ( Counts[i] && 100.0 * Counts[i] / Total >= 0.1 * p->pPars->nVerbLimit )
        {
            printf( "%5d  :  ", i );
            printf( "%-20s   ", Sdm_ManReadDsdStr(p->pDsd, i) );
            printf( "%8d  ",    Counts[i] );
            printf( "%5.1f %%   ", 100.0 * Counts[i] / Total );
            printf( "%8d  ",    Costs[i] );
            printf( "%5.1f %%", 100.0 * Costs[i] / CostTotal );
            printf( "\n" );
        }
        else
        {
            Other += Counts[i];
            CostOther += Costs[i];
        }
    printf( "Other  :  " );
    printf( "%-20s   ",   "" );
    printf( "%8d  ",    Other );
    printf( "%5.1f %%   ", 100.0 * Other / Total );
    printf( "%8d  ",    CostOther );
    printf( "%5.1f %%", 100.0 * CostOther / CostTotal );
    printf( "\n" );
}

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

  Synopsis    [Manager manipulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Jf_Man_t * Jf_ManAlloc( Gia_Man_t * pGia, Jf_Par_t * pPars )
{
    Jf_Man_t * p;
    assert( pPars->nLutSize <= JF_LEAF_MAX );
    assert( pPars->nCutNum <= JF_CUT_MAX );
    Vec_IntFreeP( &pGia->vMapping );
    p = ABC_CALLOC( Jf_Man_t, 1 );
    p->pGia      = pGia;
    p->pPars     = pPars;
    if ( pPars->fCutMin && pPars->fUseTts )
    {
        word uTruth; int Value;
        p->vTtMem    = Vec_MemAlloc( 1, 12 ); // 32 KB/page for 6-var functions
        Vec_MemHashAlloc( p->vTtMem, 10000 );
        uTruth = ABC_CONST(0x0000000000000000); Value = Vec_MemHashInsert( p->vTtMem, &uTruth ); assert( Value == 0 );
        uTruth = ABC_CONST(0xAAAAAAAAAAAAAAAA); Value = Vec_MemHashInsert( p->vTtMem, &uTruth ); assert( Value == 1 );
    }
    else if ( pPars->fCutMin && !pPars->fUseTts )
    {
        p->pDsd = Sdm_ManRead();
        if ( pPars->fGenCnf )
        {
            p->vCnfs = Vec_IntStart( 595 );
            Sdm_ManReadCnfCosts( p->pDsd, Vec_IntArray(p->vCnfs), Vec_IntSize(p->vCnfs) );
        }
    }
    Vec_IntFill( &p->vCuts, Gia_ManObjNum(pGia), 0 );
    Vec_IntFill( &p->vArr,  Gia_ManObjNum(pGia), 0 );
    Vec_IntFill( &p->vDep,  Gia_ManObjNum(pGia), 0 );
    Vec_FltFill( &p->vFlow, Gia_ManObjNum(pGia), 0 );
    p->vRefs.nCap = p->vRefs.nSize = Gia_ManObjNum(pGia);
    p->vRefs.pArray = Jf_ManInitRefs( p );
    Vec_SetAlloc_( &p->pMem, 20 );
    p->vTemp     = Vec_IntAlloc( 1000 );
    p->clkStart  = Abc_Clock();
    return p;
}
void Jf_ManFree( Jf_Man_t * p )
{
    if ( p->pPars->fVerbose && p->pDsd )
        Sdm_ManPrintDsdStats( p->pDsd, 0 );
    if ( p->pPars->fVerbose && p->vTtMem )
        printf( "Unique truth tables = %d. Memory = %.2f MB\n", Vec_MemEntryNum(p->vTtMem), Vec_MemMemory(p->vTtMem) / (1<<20) ); 
    if ( p->pPars->fVeryVerbose && p->pPars->fUseTts )
    {
        char * pFileName = "truths.txt";
        FILE * pFile = fopen( pFileName, "wb" );
        Vec_MemDump( pFile, p->vTtMem );
        fclose( pFile );
        printf( "Dumped %d %d-var truth tables into file \"%s\" (%.2f MB).\n", 
            Vec_MemEntryNum(p->vTtMem), p->pPars->nLutSize, pFileName,
            17.0 * Vec_MemEntryNum(p->vTtMem) / (1 << 20) );
    }
    if ( p->pPars->fVeryVerbose && !p->pPars->fUseTts && p->pPars->fCutMin )
        Jf_ManProfileClasses( p );
    if ( p->pPars->fCoarsen )
        Gia_ManCleanMark0( p->pGia );
    ABC_FREE( p->pGia->pRefs );
    ABC_FREE( p->vCuts.pArray );
    ABC_FREE( p->vArr.pArray );
    ABC_FREE( p->vDep.pArray );
    ABC_FREE( p->vFlow.pArray );
    ABC_FREE( p->vRefs.pArray );
    if ( p->pPars->fCutMin && p->pPars->fUseTts )
    {
        Vec_MemHashFree( p->vTtMem );
        Vec_MemFree( p->vTtMem );
    }
    Vec_IntFreeP( &p->vCnfs );
    Vec_SetFree_( &p->pMem );
    Vec_IntFreeP( &p->vTemp );
    ABC_FREE( p );
}

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

  Synopsis    [Cut functions.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Jf_CutPrint( int * pCut )
{
    int i; 
    printf( "%d {", Jf_CutSize(pCut) );
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        printf( " %d", Jf_CutLit(pCut, i) );
    printf( " } Func = %d\n", Jf_CutFunc(pCut) );
}
static inline void Jf_ObjCutPrint( int * pCuts )
{
    int i, * pCut; 
    Jf_ObjForEachCut( pCuts, pCut, i )
        Jf_CutPrint( pCut );
    printf( "\n" );
}
static inline void Jf_ObjBestCutConePrint( Jf_Man_t * p, Gia_Obj_t * pObj )
{
    int * pCut = Jf_ObjCutBest( p, Gia_ObjId(p->pGia, pObj) );
    printf( "Best cut of node %d : ", Gia_ObjId(p->pGia, pObj) );
    Jf_CutPrint( pCut );
    Gia_ManPrintCone( p->pGia, pObj, Jf_CutLits(pCut), Jf_CutSize(pCut), p->vTemp );
}
static inline void Jf_CutCheck( int * pCut )
{
    int i, k;
    for ( i = 2; i <= Jf_CutSize(pCut); i++ )
        for ( k = 1; k < i; k++ )
            assert( Jf_CutLit(pCut, i) != Jf_CutLit(pCut, k) );
}
static inline int Jf_CountBitsSimple( unsigned n )
{
    int i, Count = 0;
    for ( i = 0; i < 32; i++ )
        Count += ((n >> i) & 1);
    return Count;
}
static inline int Jf_CountBits32( unsigned i )
{
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = ((i + (i >> 4)) & 0x0F0F0F0F);
    return (i*(0x01010101))>>24;
}
static inline int Jf_CountBits( word i )
{
    i = i - ((i >> 1) & 0x5555555555555555);
    i = (i & 0x3333333333333333) + ((i >> 2) & 0x3333333333333333);
    i = ((i + (i >> 4)) & 0x0F0F0F0F0F0F0F0F);
    return (i*(0x0101010101010101))>>56;
}
static inline unsigned Jf_CutGetSign32( int * pCut )
{
    unsigned Sign = 0; int i; 
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Sign |= 1 << (Jf_CutVar(pCut, i) & 0x1F);
    return Sign;
}
static inline word Jf_CutGetSign( int * pCut )
{
    word Sign = 0; int i; 
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Sign |= ((word)1) << (Jf_CutVar(pCut, i) & 0x3F);
    return Sign;
}
static inline int Jf_CutArr( Jf_Man_t * p, int * pCut )
{
    int i, Time = 0;
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Time = Abc_MaxInt( Time, Jf_ObjArr(p, Jf_CutVar(pCut, i)) );
    return Time + 1; 
}

static inline void Jf_ObjSetBestCut( int * pCuts, int * pCut, Vec_Int_t * vTemp )
{
    assert( pCuts < pCut );
    if ( ++pCuts < pCut )
    {
        int nBlock = pCut - pCuts;
        int nSize = Jf_CutSize(pCut) + 1;
        Vec_IntGrow( vTemp, nBlock );
        memmove( Vec_IntArray(vTemp), pCuts, sizeof(int) * nBlock );
        memmove( pCuts, pCut, sizeof(int) * nSize );
        memmove( pCuts + nSize, Vec_IntArray(vTemp), sizeof(int) * nBlock );
    }
}
static inline void Jf_CutRef( Jf_Man_t * p, int * pCut )
{
    int i;
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Gia_ObjRefIncId( p->pGia, Jf_CutVar(pCut, i) );
}
static inline void Jf_CutDeref( Jf_Man_t * p, int * pCut )
{
    int i;
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Gia_ObjRefDecId( p->pGia, Jf_CutVar(pCut, i) );
}
static inline float Jf_CutFlow( Jf_Man_t * p, int * pCut )
{
    float Flow = 0; int i; 
    for ( i = 1; i <= Jf_CutSize(pCut); i++ )
        Flow += Jf_ObjFlow( p, Jf_CutVar(pCut, i) );
    assert( Flow >= 0 );
    return Flow; 
}

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

  Synopsis    [Cut merging.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Jf_CutIsContainedOrder( int * pBase, int * pCut ) // check if pCut is contained pBase
{
    int nSizeB = Jf_CutSize(pBase);
    int nSizeC = Jf_CutSize(pCut);
    int i, k;
    if ( nSizeB == nSizeC )
    {
        for ( i = 1; i <= nSizeB; i++ )
            if ( pBase[i] != pCut[i] )
                return 0;
        return 1;
    }
    assert( nSizeB > nSizeC ); 
    for ( i = k = 1; i <= nSizeB; i++ )
    {
        if ( pBase[i] > pCut[k] )
            return 0;
        if ( pBase[i] == pCut[k] )
        {
            if ( k++ == nSizeC )
                return 1;
        }
    }
    return 0;
}
static inline int Jf_CutMergeOrder( int * pCut0, int * pCut1, int * pCut, int LutSize )
{ 
    int nSize0 = Jf_CutSize(pCut0);
    int nSize1 = Jf_CutSize(pCut1);
    int * pC0 = pCut0 + 1;
    int * pC1 = pCut1 + 1;
    int * pC = pCut + 1;
    int i, k, c, s;
    // the case of the largest cut sizes
    if ( nSize0 == LutSize && nSize1 == LutSize )
    {
        for ( i = 0; i < nSize0; i++ )
        {
            if ( pC0[i] != pC1[i] )
                return 0;
            pC[i] = pC0[i];
        }
        pCut[0] = LutSize;
        return 1;
    }
    // compare two cuts with different numbers
    i = k = c = s = 0;
    while ( 1 )
    {
        if ( c == LutSize ) return 0;
        if ( pC0[i] < pC1[k] )
        {
            pC[c++] = pC0[i++];
            if ( i >= nSize0 ) goto FlushCut1;
        }
        else if ( pC0[i] > pC1[k] )
        {
            pC[c++] = pC1[k++];
            if ( k >= nSize1 ) goto FlushCut0;
        }
        else
        {
            pC[c++] = pC0[i++]; k++;
            if ( i >= nSize0 ) goto FlushCut1;
            if ( k >= nSize1 ) goto FlushCut0;
        }
    }

FlushCut0:
    if ( c + nSize0 > LutSize + i ) return 0;
    while ( i < nSize0 )
        pC[c++] = pC0[i++];
    pCut[0] = c;
    return 1;

FlushCut1:
    if ( c + nSize1 > LutSize + k ) return 0;
    while ( k < nSize1 )
        pC[c++] = pC1[k++];
    pCut[0] = c;
    return 1;
}

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

  Synopsis    [Cut merging.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Jf_CutFindLeaf0( int * pCut, int iObj )
{
    int i, nLits = Jf_CutSize(pCut);
    for ( i = 1; i <= nLits; i++ )
        if ( pCut[i] == iObj )
            return i;
    return i;
}
static inline int Jf_CutIsContained0( int * pBase, int * pCut ) // check if pCut is contained pBase
{
    int i, nLits = Jf_CutSize(pCut);
    for ( i = 1; i <= nLits; i++ )
        if ( Jf_CutFindLeaf0(pBase, pCut[i]) > pBase[0] )
            return 0;
    return 1;
}
static inline int Jf_CutMerge0( int * pCut0, int * pCut1, int * pCut, int LutSize )
{
    int nSize0 = Jf_CutSize(pCut0);
    int nSize1 = Jf_CutSize(pCut1), i;
    pCut[0] = nSize0;
    for ( i = 1; i <= nSize1; i++ )
        if ( Jf_CutFindLeaf0(pCut0, pCut1[i]) > nSize0 )
        {
            if ( pCut[0] == LutSize )
                return 0;
            pCut[++pCut[0]] = pCut1[i];
        }
    memcpy( pCut + 1, pCut0 + 1, sizeof(int) * nSize0 );
    return 1;
}

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

  Synopsis    [Cut merging.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Jf_CutFindLeaf1( int * pCut, int iLit )
{
    int i, nLits = Jf_CutSize(pCut);
    for ( i = 1; i <= nLits; i++ )
        if ( Abc_Lit2Var(pCut[i]) == iLit )
            return i;
    return i;
}
static inline int Jf_CutIsContained1( int * pBase, int * pCut ) // check if pCut is contained pBase
{
    int i, nLits = Jf_CutSize(pCut);
    for ( i = 1; i <= nLits; i++ )
        if ( Jf_CutFindLeaf1(pBase, Abc_Lit2Var(pCut[i])) > pBase[0] )
            return 0;
    return 1;
}
static inline int Jf_CutMerge1( int * pCut0, int * pCut1, int * pCut, int LutSize )
{
    int nSize0 = Jf_CutSize(pCut0);
    int nSize1 = Jf_CutSize(pCut1), i;
    pCut[0] = nSize0;
    for ( i = 1; i <= nSize1; i++ )
        if ( Jf_CutFindLeaf1(pCut0, Abc_Lit2Var(pCut1[i])) > nSize0 )
        {
            if ( pCut[0] == LutSize )
                return 0;
            pCut[++pCut[0]] = pCut1[i];
        }
    memcpy( pCut + 1, pCut0 + 1, sizeof(int) * nSize0 );
    return 1;
}
static inline int Jf_CutMerge2( int * pCut0, int * pCut1, int * pCut, int LutSize )
{
    int ConfigMask = 0x3FFFF; // 18 bits
    int nSize0 = Jf_CutSize(pCut0);
    int nSize1 = Jf_CutSize(pCut1);
    int i, iPlace;
    pCut[0] = nSize0;
    for ( i = 1; i <= nSize1; i++ )
    {
        iPlace = Jf_CutFindLeaf1(pCut0, Abc_Lit2Var(pCut1[i]));
        if ( iPlace > nSize0 )
        {
            if ( pCut[0] == LutSize )
                return 0;
            pCut[(iPlace = ++pCut[0])] = pCut1[i];
        }
        else if ( pCut0[iPlace] != pCut1[i] )
            ConfigMask |= (1 << (iPlace+17));
        ConfigMask ^= (((i-1) ^ 7) << (3*(iPlace-1)));
    }
    memcpy( pCut + 1, pCut0 + 1, sizeof(int) * nSize0 );
    return ConfigMask;
}


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

  Synopsis    [Cut filtering.]

  Description [Returns the number of cuts after filtering and the last
  cut in the last entry.  If the cut is filtered, its size is set to -1.]
               
  SideEffects [This was found to be 15% slower.]

  SeeAlso     []

***********************************************************************/
int Jf_ObjCutFilterBoth( Jf_Man_t * p, Jf_Cut_t ** pSto, int c )
{
    int k, last;
    // filter this cut using other cuts
    for ( k = 0; k < c; k++ )
        if ( pSto[c]->pCut[0] >= pSto[k]->pCut[0] && 
            (pSto[c]->Sign & pSto[k]->Sign) == pSto[k]->Sign && 
             Jf_CutIsContained1(pSto[c]->pCut, pSto[k]->pCut) )
        {
                pSto[c]->pCut[0] = -1;
                return c;
        }
    // filter other cuts using this cut
    for ( k = last = 0; k < c; k++ )
        if ( !(pSto[c]->pCut[0] < pSto[k]->pCut[0] && 
              (pSto[c]->Sign & pSto[k]->Sign) == pSto[c]->Sign && 
               Jf_CutIsContained1(pSto[k]->pCut, pSto[c]->pCut)) )
        {
            if ( last++ == k )
                continue;
            ABC_SWAP( Jf_Cut_t *, pSto[last-1], pSto[k] );
        }
    assert( last <= c );
    if ( last < c )
        ABC_SWAP( Jf_Cut_t *, pSto[last], pSto[c] );
    return last;
}
int Jf_ObjCutFilter( Jf_Man_t * p, Jf_Cut_t ** pSto, int c )
{
    int k;
    if ( p->pPars->fCutMin )
    {
        for ( k = 0; k < c; k++ )
            if ( pSto[c]->pCut[0] >= pSto[k]->pCut[0] && 
                (pSto[c]->Sign & pSto[k]->Sign) == pSto[k]->Sign && 
                 Jf_CutIsContained1(pSto[c]->pCut, pSto[k]->pCut) )
                 return 0;
    }
    else
    {
        for ( k = 0; k < c; k++ )
            if ( pSto[c]->pCut[0] >= pSto[k]->pCut[0] && 
                (pSto[c]->Sign & pSto[k]->Sign) == pSto[k]->Sign && 
                 Jf_CutIsContainedOrder(pSto[c]->pCut, pSto[k]->pCut) )
                 return 0;
    }
    return 1;
}

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

  Synopsis    [Sorting cuts by size.]

  Description []
               
  SideEffects [Did not really help.]

  SeeAlso     []

***********************************************************************/
static inline void Jf_ObjSortCuts( Jf_Cut_t ** pSto, int nSize )
{
    int i, j, best_i;
    for ( i = 0; i < nSize-1; i++ )
    {
        best_i = i;
        for ( j = i+1; j < nSize; j++ )
            if ( pSto[j]->pCut[0] < pSto[best_i]->pCut[0] )
                best_i = j;
        ABC_SWAP( Jf_Cut_t *, pSto[i], pSto[best_i] );
    }
}

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

  Synopsis    [Reference counting.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Jf_CutRef_rec( Jf_Man_t * p, int * pCut )
{
    int i, Var, Count = Jf_CutCost(pCut);
    Jf_CutForEachVar( pCut, Var, i )
        if ( !Gia_ObjRefIncId(p->pGia, Var) && !Jf_CutIsTriv(Jf_ObjCutBest(p, Var), Var) )
            Count += Jf_CutRef_rec( p, Jf_ObjCutBest(p, Var) );
    return Count;
}
int Jf_CutDeref_rec( Jf_Man_t * p, int * pCut )
{
    int i, Var, Count = Jf_CutCost(pCut);
    Jf_CutForEachVar( pCut, Var, i )
        if ( !Gia_ObjRefDecId(p->pGia, Var) && !Jf_CutIsTriv(Jf_ObjCutBest(p, Var), Var) )
            Count += Jf_CutDeref_rec( p, Jf_ObjCutBest(p, Var) );
    return Count;
}
static inline int Jf_CutAreaOld( Jf_Man_t * p, int * pCut )
{
    int Ela1, Ela2;
    Ela1 = Jf_CutRef_rec( p, pCut );
    Ela2 = Jf_CutDeref_rec( p, pCut );
    assert( Ela1 == Ela2 );
    return Ela1;
}

int Jf_CutAreaRef_rec( Jf_Man_t * p, int * pCut )
{
    int i, Var, Count = Jf_CutCost(pCut);
    Jf_CutForEachVar( pCut, Var, i )
    {
        if ( !Gia_ObjRefIncId(p->pGia, Var) && !Jf_CutIsTriv(Jf_ObjCutBest(p, Var), Var) )
            Count += Jf_CutAreaRef_rec( p, Jf_ObjCutBest(p, Var) );
        Vec_IntPush( p->vTemp, Var );
    }
    return Count;
}
int Jf_CutAreaRefEdge_rec( Jf_Man_t * p, int * pCut )
{
    int i, Var, Count = (Jf_CutCost(pCut) << 4) | Jf_CutSize(pCut);
    Jf_CutForEachVar( pCut, Var, i )
    {
        if ( !Gia_ObjRefIncId(p->pGia, Var) && !Jf_CutIsTriv(Jf_ObjCutBest(p, Var), Var) )
            Count += Jf_CutAreaRefEdge_rec( p, Jf_ObjCutBest(p, Var) );
        Vec_IntPush( p->vTemp, Var );
    }
    return Count;
}
static inline int Jf_CutArea( Jf_Man_t * p, int * pCut, int fEdge )
{
    int Ela, Entry, i;
    Vec_IntClear( p->vTemp );
    if ( fEdge )
        Ela = Jf_CutAreaRefEdge_rec( p, pCut );
    else
        Ela = Jf_CutAreaRef_rec( p, pCut );
    Vec_IntForEachEntry( p->vTemp, Entry, i )
        Gia_ObjRefDecId( p->pGia, Entry );
    return Ela;
}
// returns 1 if MFFC size is less than limit
int Jf_CutCheckMffc_rec( Jf_Man_t * p, int * pCut, int Limit )
{
    int i, Var;
    Jf_CutForEachVar( pCut, Var, i )
    {
        int fRecur = (!Gia_ObjRefDecId(p->pGia, Var) && !Jf_CutIsTriv(Jf_ObjCutBest(p, Var), Var));
        Vec_IntPush( p->vTemp, Var );
        if ( Vec_IntSize(p->vTemp) >= Limit )
            return 0;
        if ( fRecur && !Jf_CutCheckMffc_rec( p, Jf_ObjCutBest(p, Var), Limit ) )
            return 0;
    }
    return 1;
}
static inline int Jf_CutCheckMffc( Jf_Man_t * p, int * pCut, int Limit )
{
    int RetValue, Entry, i;
    Vec_IntClear( p->vTemp );
    RetValue = Jf_CutCheckMffc_rec( p, pCut, Limit );
    Vec_IntForEachEntry( p->vTemp, Entry, i )
        Gia_ObjRefIncId( p->pGia, Entry );
    return RetValue;
}

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

  Synopsis    [Comparison procedures.]

  Description [Return positive value if the new cut is better than the old cut.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Jf_CutCompareDelay( Jf_Cut_t * pOld, Jf_Cut_t * pNew )
{
    if ( pOld->Time    != pNew->Time    ) return pOld->Time    - pNew->Time;
    if ( pOld->pCut[0] != pNew->pCut[0] ) return pOld->pCut[0] - pNew->pCut[0];
    if ( pOld->Flow    != pNew->Flow    ) return pOld->Flow    - pNew->Flow;
    return 0;
}
float Jf_CutCompareArea( Jf_Cut_t * pOld, Jf_Cut_t * pNew )
{
//    float Epsilon = (float)0.001;
//    if ( pOld->Flow > pNew->Flow + Epsilon ) return 1;
//    if ( pOld->Flow < pNew->Flow - Epsilon ) return -1;
    if ( pOld->Flow    != pNew->Flow    ) return pOld->Flow    - pNew->Flow;
    if ( pOld->pCut[0] != pNew->pCut[0] ) return pOld->pCut[0] - pNew->pCut[0];
    if ( pOld->Time    != pNew->Time    ) return pOld->Time    - pNew->Time;
    return 0;
}
static inline int Jf_ObjAddCutToStore( Jf_Man_t * p, Jf_Cut_t ** pSto, int c, int cMax )
{
    Jf_Cut_t * pTemp;
    int k, last, iPivot;
    // if the store is empty, add anything
    if ( c == 0 )
        return 1;
    // special case when the cut store is full and last cut is better than new cut
    if ( c == cMax && p->pCutCmp(pSto[c-1], pSto[c]) <= 0 )
        return c;
    // find place of the given cut in the store
    assert( c <= cMax );
    for ( iPivot = c-1; iPivot >= 0; iPivot-- )
        if ( p->pCutCmp(pSto[iPivot], pSto[c]) < 0 ) // iPivot-th cut is better than new cut
            break;
    // filter this cut using other cuts
    if ( p->pPars->fCutMin )
    {
        for ( k = 0; k <= iPivot; k++ )
            if ( pSto[c]->pCut[0] >= pSto[k]->pCut[0] && 
                (pSto[c]->Sign & pSto[k]->Sign) == pSto[k]->Sign && 
                 Jf_CutIsContained1(pSto[c]->pCut, pSto[k]->pCut) )
                    return c;
    }
    else
    {
        for ( k = 0; k <= iPivot; k++ )
            if ( pSto[c]->pCut[0] >= pSto[k]->pCut[0] && 
                (pSto[c]->Sign & pSto[k]->Sign) == pSto[k]->Sign && 
                 Jf_CutIsContainedOrder(pSto[c]->pCut, pSto[k]->pCut) )
                    return c;
    }
    // insert this cut after iPivot
    pTemp = pSto[c];
    for ( ++iPivot, k = c++; k > iPivot; k-- )
        pSto[k] = pSto[k-1];
    pSto[iPivot] = pTemp;
    // filter other cuts using this cut
    if ( p->pPars->fCutMin )
    {
        for ( k = last = iPivot+1; k < c; k++ )
            if ( !(pSto[iPivot]->pCut[0] <= pSto[k]->pCut[0] && 
                  (pSto[iPivot]->Sign & pSto[k]->Sign) == pSto[iPivot]->Sign && 
                   Jf_CutIsContained1(pSto[k]->pCut, pSto[iPivot]->pCut)) )
            {
                if ( last++ == k )
                    continue;
                ABC_SWAP( Jf_Cut_t *, pSto[last-1], pSto[k] );
            }
    }
    else
    {
        for ( k = last = iPivot+1; k < c; k++ )
            if ( !(pSto[iPivot]->pCut[0] <= pSto[k]->pCut[0] && 
                  (pSto[iPivot]->Sign & pSto[k]->Sign) == pSto[iPivot]->Sign && 
                   Jf_CutIsContainedOrder(pSto[k]->pCut, pSto[iPivot]->pCut)) )
            {
                if ( last++ == k )
                    continue;
                ABC_SWAP( Jf_Cut_t *, pSto[last-1], pSto[k] );
            }
    }
    c = last;
    // remove the last cut if too many
    if ( c == cMax + 1 )
        return c - 1;
    return c;
}
static inline void Jf_ObjPrintStore( Jf_Man_t * p, Jf_Cut_t ** pSto, int c )
{
    int i;
    for ( i = 0; i < c; i++ )
    {
        printf( "Flow =%9.5f  ", pSto[i]->Flow );
        printf( "Time = %5d   ", pSto[i]->Time );
        printf( "Func = %5d   ", pSto[i]->iFunc );
        printf( "  " );
        Jf_CutPrint( pSto[i]->pCut );
    }
    printf( "\n" );
}
static inline void Jf_ObjCheckPtrs( Jf_Cut_t ** pSto, int c )
{
    int i, k;
    for ( i = 1; i < c; i++ )
        for ( k = 0; k < i; k++ )
            assert( pSto[k] != pSto[i] );
}
static inline void Jf_ObjCheckStore( Jf_Man_t * p, Jf_Cut_t ** pSto, int c, int iObj )
{
    int i, k;
    for ( i = 1; i < c; i++ )
        assert( p->pCutCmp(pSto[i-1], pSto[i]) <= 0 );
    for ( i = 1; i < c; i++ )
        for ( k = 0; k < i; k++ )
        {
            assert( !Jf_CutIsContained1(pSto[k]->pCut, pSto[i]->pCut) );
            assert( !Jf_CutIsContained1(pSto[i]->pCut, pSto[k]->pCut) );
        }
}

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

  Synopsis    [Cut minimization.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Jf_TtComputeForCut( Jf_Man_t * p, int iFuncLit0, int iFuncLit1, int * pCut0, int * pCut1, int * pCutOut )
{
    word uTruth; int fCompl, truthId;
    int LutSize    = p->pPars->nLutSize;
    word * pTruth0 = Vec_MemReadEntry(p->vTtMem, Abc_Lit2Var(iFuncLit0));
    word * pTruth1 = Vec_MemReadEntry(p->vTtMem, Abc_Lit2Var(iFuncLit1));
    word uTruth0   = Abc_LitIsCompl(iFuncLit0) ? ~*pTruth0 : *pTruth0;
    word uTruth1   = Abc_LitIsCompl(iFuncLit1) ? ~*pTruth1 : *pTruth1;
    Abc_TtStretch( &uTruth0, LutSize, pCut0 + 1, Jf_CutSize(pCut0), pCutOut + 1, Jf_CutSize(pCutOut) );
    Abc_TtStretch( &uTruth1, LutSize, pCut1 + 1, Jf_CutSize(pCut1), pCutOut + 1, Jf_CutSize(pCutOut) );
    Abc_TtAnd( &uTruth, &uTruth0, &uTruth1, Abc_TtWordNum(LutSize), 0 );
    pCutOut[0]     = Abc_TtMinBase( &uTruth, pCutOut + 1, pCutOut[0] );
    fCompl         = (uTruth & 1);
    uTruth         = fCompl ? ~uTruth : uTruth;
    truthId        = Vec_MemHashInsert(p->vTtMem, &uTruth);
    return Abc_Var2Lit( truthId, fCompl );
}

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

  Synopsis    [Cut enumeration.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Jf_ObjAssignCut( Jf_Man_t * p, Gia_Obj_t * pObj )
{
    int iObj = Gia_ObjId(p->pGia, pObj);
    int pClause[3] = { 1, Jf_CutSetAll(2, 0, 1), Jf_ObjLit(iObj, 0) }; // set function
    assert( Gia_ObjIsCi(pObj) || Gia_ObjIsBuf(pObj) );
    Vec_IntWriteEntry( &p->vCuts, iObj, Vec_SetAppend( &p->pMem, pClause, 3 ) );
}
static inline void Jf_ObjPropagateBuf( Jf_Man_t * p, Gia_Obj_t * pObj, int fReverse )
{
    int iObj = Gia_ObjId( p->pGia, pObj );
    int iFanin = Gia_ObjFaninId0( pObj, iObj );
    assert( 0 );
    assert( Gia_ObjIsBuf(pObj) );
    if ( fReverse )
        ABC_SWAP( int, iObj, iFanin );
    Vec_IntWriteEntry( &p->vArr,  iObj, Jf_ObjArr(p, iFanin) );
    Vec_FltWriteEntry( &p->vFlow, iObj, Jf_ObjFlow(p, iFanin) );
}
static inline int Jf_ObjHasCutWithSize( Jf_Cut_t ** pSto, int c, int nSize )
{
    int i;
    for ( i = 0; i < c; i++ )
        if ( pSto[i]->pCut[0] <= nSize )
            return 1;
    return 0;
}
void Jf_ObjComputeCuts( Jf_Man_t * p, Gia_Obj_t * pObj, int fEdge )
{
    int        LutSize = p->pPars->nLutSize;
    int        CutNum = p->pPars->nCutNum;
    int        iObj = Gia_ObjId(p->pGia, pObj);
    word       Sign0[JF_CUT_MAX+2]; // signatures of the first cut
    word       Sign1[JF_CUT_MAX+2]; // signatures of the second cut
    Jf_Cut_t   Sto[JF_CUT_MAX+2];   // cut storage
    Jf_Cut_t * pSto[JF_CUT_MAX+2];  // pointers to cut storage
    int *      pCut0, * pCut1, * pCuts0, * pCuts1;
    int        nOldSupp, Config, i, k, c = 0;
    // prepare cuts
    for ( i = 0; i <= CutNum+1; i++ )
        pSto[i] = Sto + i, pSto[i]->Cost = 0, pSto[i]->iFunc = ~0;
    // compute signatures
    pCuts0 = Jf_ObjCuts( p, Gia_ObjFaninId0(pObj, iObj) );
    Jf_ObjForEachCut( pCuts0, pCut0, i )
        Sign0[i] = Jf_CutGetSign( pCut0 );
    // compute signatures
    pCuts1 = Jf_ObjCuts( p, Gia_ObjFaninId1(pObj, iObj) );
    Jf_ObjForEachCut( pCuts1, pCut1, i )
        Sign1[i] = Jf_CutGetSign( pCut1 );
    // merge cuts
    p->CutCount[0] += pCuts0[0] * pCuts1[0];
    Jf_ObjForEachCut( pCuts0, pCut0, i )
    Jf_ObjForEachCut( pCuts1, pCut1, k )
    {
        if ( Jf_CountBits(Sign0[i] | Sign1[k]) > LutSize )
            continue;
        p->CutCount[1]++;        
        if ( !p->pPars->fCutMin )
        {
            if ( !Jf_CutMergeOrder(pCut0, pCut1, pSto[c]->pCut, LutSize) )
                continue;
            pSto[c]->Sign = Sign0[i] | Sign1[k];
        }
        else if ( p->pPars->fUseTts )
        {
            if ( !Jf_CutMergeOrder(pCut0, pCut1, pSto[c]->pCut, LutSize) )
                continue;
            pSto[c]->Sign = Sign0[i] | Sign1[k];
            nOldSupp = pSto[c]->pCut[0];
            pSto[c]->iFunc = Jf_TtComputeForCut( p, Jf_ObjFunc0(pObj, pCut0), Jf_ObjFunc1(pObj, pCut1), pCut0, pCut1, pSto[c]->pCut );
            assert( pSto[c]->pCut[0] <= nOldSupp );
            if ( pSto[c]->pCut[0] < nOldSupp )
                pSto[c]->Sign = Jf_CutGetSign( pSto[c]->pCut );
            if ( pSto[c]->iFunc >= (1 << 24) )
                printf( "Hard limit on the number of different Boolean functions (2^23) is reached. Quitting...\n" ), exit(1);
        }
        else
        {
            if ( !(Config = Jf_CutMerge2(pCut0, pCut1, pSto[c]->pCut, LutSize)) )
                continue;
            pSto[c]->Sign = Sign0[i] | Sign1[k];
            nOldSupp = pSto[c]->pCut[0];
            pSto[c]->iFunc = Sdm_ManComputeFunc( p->pDsd, Jf_ObjFunc0(pObj, pCut0), Jf_ObjFunc1(pObj, pCut1), pSto[c]->pCut, Config, 0 );
            if ( pSto[c]->iFunc == -1 )
                continue;
            if ( p->pPars->fGenCnf && Jf_CutCnfSizeF(p, Abc_Lit2Var(pSto[c]->iFunc)) >= 12 ) // no more than 15
                continue;
            assert( pSto[c]->pCut[0] <= nOldSupp );
            if ( pSto[c]->pCut[0] < nOldSupp )
                pSto[c]->Sign = Jf_CutGetSign( pSto[c]->pCut );
        }
        p->CutCount[2]++;
        pSto[c]->Time = p->pPars->fAreaOnly ? 0 : Jf_CutArr(p, pSto[c]->pCut);
        pSto[c]->Flow = Jf_CutFlow(p, pSto[c]->pCut);
        c = Jf_ObjAddCutToStore( p, pSto, c, CutNum );
        assert( c <= CutNum );
    }
//    Jf_ObjPrintStore( p, pSto, c );
//    Jf_ObjCheckStore( p, pSto, c, iObj );
    // add two variable cut
    if ( !Jf_ObjIsUnit(pObj) && !Jf_ObjHasCutWithSize(pSto, c, 2) )
    {
        assert( Jf_ObjIsUnit(Gia_ObjFanin0(pObj)) && Jf_ObjIsUnit(Gia_ObjFanin1(pObj)) );
        if ( p->pPars->fCutMin ) pSto[c]->iFunc = 4;  // set function (DSD only!)
        pSto[c]->pCut[0] = 2; 
        pSto[c]->pCut[1] = Jf_ObjLit(Gia_ObjFaninId0(pObj, iObj), Gia_ObjFaninC0(pObj)); 
        pSto[c]->pCut[2] = Jf_ObjLit(Gia_ObjFaninId1(pObj, iObj), Gia_ObjFaninC1(pObj)); 
        c++;
    }
    // add elementary cut
    if ( Jf_ObjIsUnit(pObj) && !(p->pPars->fCutMin && Jf_ObjHasCutWithSize(pSto, c, 1)) )
    {
        if ( p->pPars->fCutMin ) pSto[c]->iFunc = 2;  // set function
        pSto[c]->pCut[0] = 1;
        pSto[c]->pCut[1] = Jf_ObjLit(iObj, 0);
        c++;
    }
    // reorder cuts
//    Jf_ObjSortCuts( pSto + 1, c - 1 );
//    Jf_ObjCheckPtrs( pSto, CutNum );
    // find cost of the best cut
    pSto[0]->Cost = p->pPars->fGenCnf ? Jf_CutCnfSizeF(p, Abc_Lit2Var(pSto[0]->iFunc)) : 1;
    assert( pSto[0]->Cost >= 0 );
    // save best info
    assert( pSto[0]->Flow >= 0 );
    Vec_IntWriteEntry( &p->vArr,  iObj, pSto[0]->Time );
    Vec_FltWriteEntry( &p->vFlow, iObj, (pSto[0]->Flow + (fEdge ? pSto[0]->pCut[0] : pSto[0]->Cost)) / Jf_ObjRefs(p, iObj) );
    // add cuts to storage cuts
    Vec_IntClear( p->vTemp );
    Vec_IntPush( p->vTemp, c );
    for ( i = 0; i < c; i++ )
    {
        assert( pSto[i]->pCut[0] <= 6 );
        pSto[i]->Cost = p->pPars->fGenCnf ? Jf_CutCnfSizeF(p, Abc_Lit2Var(pSto[i]->iFunc)) : 1;
        Vec_IntPush( p->vTemp, Jf_CutSetAll(pSto[i]->iFunc, pSto[i]->Cost, pSto[i]->pCut[0]) );
        for ( k = 1; k <= pSto[i]->pCut[0]; k++ )
            Vec_IntPush( p->vTemp, pSto[i]->pCut[k] );
    }
    Vec_IntWriteEntry( &p->vCuts, iObj, Vec_SetAppend(&p->pMem, Vec_IntArray(p->vTemp), Vec_IntSize(p->vTemp)) );
    p->CutCount[3] += c;
}
void Jf_ManComputeCuts( Jf_Man_t * p, int fEdge )
{
    Gia_Obj_t * pObj; int i;
    if ( p->pPars->fVerbose )
    {
        printf( "Aig: CI = %d  CO = %d  AND = %d    ", Gia_ManCiNum(p->pGia), Gia_ManCoNum(p->pGia), Gia_ManAndNum(p->pGia) );
        printf( "LutSize = %d  CutMax = %d  Rounds = %d\n", p->pPars->nLutSize, p->pPars->nCutNum, p->pPars->nRounds );
        printf( "Computing cuts...\r" );
        fflush( stdout );
    }
    Gia_ManForEachObj( p->pGia, pObj, i )
    {
        if ( Gia_ObjIsCi(pObj) || Gia_ObjIsBuf(pObj) )
            Jf_ObjAssignCut( p, pObj );
        if ( Gia_ObjIsBuf(pObj) )
            Jf_ObjPropagateBuf( p, pObj, 0 );
        else if ( Gia_ObjIsAnd(pObj) )
            Jf_ObjComputeCuts( p, pObj, fEdge );
    }
    if ( p->pPars->fVerbose )
    {
        printf( "CutPair = %lu  ", p->CutCount[0] );
        printf( "Merge = %lu  ",   p->CutCount[1] );
        printf( "Eval = %lu  ",    p->CutCount[2] );
        printf( "Cut = %lu  ",     p->CutCount[3] );
        Abc_PrintTime( 1, "Time",  Abc_Clock() - p->clkStart );
        printf( "Memory:  " );
        printf( "Gia = %.2f MB  ", Gia_ManMemory(p->pGia) / (1<<20) );
        printf( "Man = %.2f MB  ", 6.0 * sizeof(int) * Gia_ManObjNum(p->pGia) / (1<<20) );
        printf( "Cuts = %.2f MB",  Vec_ReportMemory(&p->pMem) / (1<<20) );
        if ( p->nCoarse )
        printf( "   Coarse = %d (%.1f %%)",  p->nCoarse, 100.0 * p->nCoarse / Gia_ManObjNum(p->pGia) );
        printf( "\n" );
        fflush( stdout );
    }
}


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

  Synopsis    [Computing delay/area.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Jf_ManComputeDelay( Jf_Man_t * p, int fEval )
{
    Gia_Obj_t * pObj; 
    int i, Delay = 0;
    if ( fEval )
    {
        Gia_ManForEachObj( p->pGia, pObj, i )
            if ( Gia_ObjIsBuf(pObj) )
                Jf_ObjPropagateBuf( p, pObj, 0 );
            else if ( Gia_ObjIsAnd(pObj) && Gia_ObjRefNum(p->pGia, pObj) > 0 )
                Vec_IntWriteEntry( &p->vArr, i, Jf_CutArr(p, Jf_ObjCutBest(p, i)) );
    }
    Gia_ManForEachCoDriver( p->pGia, pObj, i )
    {
        assert( Gia_ObjRefNum(p->pGia, pObj) > 0 );
        Delay = Abc_MaxInt( Delay, Jf_ObjArr(p, Gia_ObjId(p->pGia, pObj)) );
    }
    return Delay;
}
int Jf_ManComputeRefs( Jf_Man_t * p )
{
    Gia_Obj_t * pObj; 
    float nRefsNew; int i, * pCut;
    float * pRefs = Vec_FltArray(&p->vRefs);
    float * pFlow = Vec_FltArray(&p->vFlow);
    assert( p->pGia->pRefs != NULL );
    memset( p->pGia->pRefs, 0, sizeof(int) * Gia_ManObjNum(p->pGia) );
    p->pPars->Area = p->pPars->Edge = 0;
    Gia_ManForEachObjReverse( p->pGia, pObj, i )
    {
        if ( Gia_ObjIsCo(pObj) || Gia_ObjIsBuf(pObj) )
            Gia_ObjRefInc( p->pGia, Gia_ObjFanin0(pObj) );
        else if ( Gia_ObjIsAnd(pObj) && Gia_ObjRefNum(p->pGia, pObj) > 0 )
        {
            assert( Jf_ObjIsUnit(pObj) );
            pCut = Jf_ObjCutBest(p, i);
            Jf_CutRef( p, pCut );
            if ( p->pPars->fGenCnf )
            p->pPars->Clause += Jf_CutCnfSize(p, pCut);
            p->pPars->Edge += Jf_CutSize(pCut);
            p->pPars->Area++;
        }
    }
    // blend references and normalize flow
    for ( i = 0; i < Gia_ManObjNum(p->pGia); i++ )
    {
        if ( p->pPars->fOptEdge )
            nRefsNew = Abc_MaxFloat( 1, 0.8 * pRefs[i] + 0.2 * p->pGia->pRefs[i] );
        else
            nRefsNew = Abc_MaxFloat( 1, 0.2 * pRefs[i] + 0.8 * p->pGia->pRefs[i] );
        pFlow[i] = pFlow[i] * pRefs[i] / nRefsNew;
        pRefs[i] = nRefsNew;
        assert( pFlow[i] >= 0 );
    }
    // compute delay
    p->pPars->Delay = Jf_ManComputeDelay( p, 1 );
    return p->pPars->Area;
}

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

  Synopsis    [Mapping rounds.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Jf_ObjComputeBestCut( Jf_Man_t * p, Gia_Obj_t * pObj, int fEdge, int fEla )
{
    int i, iObj = Gia_ObjId( p->pGia, pObj );
    int * pCuts = Jf_ObjCuts( p, iObj );
    int * pCut, * pCutBest = NULL;
    int Time = ABC_INFINITY, TimeBest = ABC_INFINITY;
    float Area, AreaBest = ABC_INFINITY;
    Jf_ObjForEachCut( pCuts, pCut, i )
    {
        if ( Jf_CutIsTriv(pCut, iObj) ) continue;
        if ( fEdge && !fEla ) 
            Jf_CutSetCost(pCut, Jf_CutSize(pCut));
        Area = fEla ? Jf_CutArea(p, pCut, fEdge) : Jf_CutFlow(p, pCut) + Jf_CutCost(pCut);
        if ( pCutBest == NULL || AreaBest > Area || (AreaBest == Area && TimeBest > (Time = Jf_CutArr(p, pCut))) )
            pCutBest = pCut, AreaBest = Area, TimeBest = Time;
    }
    Vec_IntWriteEntry( &p->vArr,  iObj, Jf_CutArr(p, pCutBest) );
    if ( !fEla )
    Vec_FltWriteEntry( &p->vFlow, iObj, AreaBest / Jf_ObjRefs(p, iObj) );
    Jf_ObjSetBestCut( pCuts, pCutBest, p->vTemp );
//    Jf_CutPrint( Jf_ObjCutBest(p, iObj) ); printf( "\n" );
}
void Jf_ManPropagateFlow( Jf_Man_t * p, int fEdge )
{
    Gia_Obj_t * pObj; 
    int i;
    Gia_ManForEachObj( p->pGia, pObj, i )
        if ( Gia_ObjIsBuf(pObj) )
            Jf_ObjPropagateBuf( p, pObj, 0 );
        else if ( Gia_ObjIsAnd(pObj) && Jf_ObjIsUnit(pObj) )
            Jf_ObjComputeBestCut( p, pObj, fEdge, 0 );
    Jf_ManComputeRefs( p );
}
void Jf_ManPropagateEla( Jf_Man_t * p, int fEdge )
{
    Gia_Obj_t * pObj; 
    int i, CostBef, CostAft;
    p->pPars->Area = p->pPars->Edge = p->pPars->Clause = 0;
    Gia_ManForEachObjReverse( p->pGia, pObj, i )
        if ( Gia_ObjIsBuf(pObj) )
            Jf_ObjPropagateBuf( p, pObj, 1 );
        else if ( Gia_ObjIsAnd(pObj) && Gia_ObjRefNum(p->pGia, pObj) > 0 )
        {
            assert( Jf_ObjIsUnit(pObj) );
            if ( Jf_CutCheckMffc(p, Jf_ObjCutBest(p, i), 50) )
            {
                CostBef = Jf_CutDeref_rec( p, Jf_ObjCutBest(p, i) );
                Jf_ObjComputeBestCut( p, pObj, fEdge, 1 );
                CostAft = Jf_CutRef_rec( p, Jf_ObjCutBest(p, i) );
    //            if ( CostBef != CostAft )  printf( "%d -> %d   ", CostBef, CostAft );
                assert( CostBef >= CostAft ); // does not hold because of JF_EDGE_LIM
            }
            if ( p->pPars->fGenCnf )
            p->pPars->Clause += Jf_CutCnfSize(p, Jf_ObjCutBest(p, i));
            p->pPars->Edge += Jf_CutSize(Jf_ObjCutBest(p, i));
            p->pPars->Area++;
        }
    p->pPars->Delay = Jf_ManComputeDelay( p, 1 );
//    printf( "\n" );
}

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

  Synopsis    [Derives the result of mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Jf_ManDeriveMappingGia( Jf_Man_t * p )
{
    Gia_Man_t * pNew;
    Gia_Obj_t * pObj; 
    Vec_Int_t * vCopies   = Vec_IntStartFull( Gia_ManObjNum(p->pGia) );
    Vec_Int_t * vMapping  = Vec_IntStart( 2 * Gia_ManObjNum(p->pGia) + (int)p->pPars->Edge + 2 * (int)p->pPars->Area );
    Vec_Int_t * vMapping2 = Vec_IntStart( (int)p->pPars->Edge + 2 * (int)p->pPars->Area + 1000 );
    Vec_Int_t * vCover    = Vec_IntAlloc( 1 << 16 );
    Vec_Int_t * vLeaves   = Vec_IntAlloc( 16 );
    int i, k, iLit, Class, * pCut;
    word uTruth;
    assert( p->pPars->fCutMin );
    // create new manager
    pNew = Gia_ManStart( Gia_ManObjNum(p->pGia) );
    pNew->pName = Abc_UtilStrsav( p->pGia->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pGia->pSpec );
    // map primary inputs
    Vec_IntWriteEntry( vCopies, 0, 0 );
    Gia_ManForEachCi( p->pGia, pObj, i )
        Vec_IntWriteEntry( vCopies, Gia_ObjId(p->pGia, pObj), Gia_ManAppendCi(pNew) );
    // iterate through nodes used in the mapping
    Gia_ManForEachAnd( p->pGia, pObj, i )
    {
       if ( Gia_ObjIsBuf(pObj) || Gia_ObjRefNum(p->pGia, pObj) == 0 )
            continue;
        pCut = Jf_ObjCutBest( p, i );
//        printf( "Best cut of node %d:  ", i );  Jf_CutPrint(pCut);
        Class = Jf_CutFuncClass( pCut );
        uTruth = p->pPars->fUseTts ? *Vec_MemReadEntry(p->vTtMem, Class) : Sdm_ManReadDsdTruth(p->pDsd, Class);
        assert( p->pDsd == NULL || Sdm_ManReadDsdVarNum(p->pDsd, Class) == Jf_CutSize(pCut) );
        if ( Jf_CutSize(pCut) == 0 )
        {
            assert( Class == 0 );
            Vec_IntWriteEntry( vCopies, i, Jf_CutFunc(pCut) );
            continue;
        }
        if ( Jf_CutSize(pCut) == 1 )
        {
            assert( Class == 1 );
            iLit = Abc_LitNotCond( Jf_CutLit(pCut, 1) , Jf_CutFuncCompl(pCut) );
            iLit = Abc_Lit2LitL( Vec_IntArray(vCopies), iLit );
            Vec_IntWriteEntry( vCopies, i, iLit );
            continue;
        }
        // collect leaves
        Vec_IntClear( vLeaves );
        Jf_CutForEachLit( pCut, iLit, k )
            Vec_IntPush( vLeaves, Abc_Lit2LitL(Vec_IntArray(vCopies), iLit) );
        // create GIA
        iLit = Kit_TruthToGia( pNew, (unsigned *)&uTruth, Vec_IntSize(vLeaves), vCover, vLeaves, 0 );
        iLit = Abc_LitNotCond( iLit, Jf_CutFuncCompl(pCut) );
        Vec_IntWriteEntry( vCopies, i, iLit );
        // create mapping
        Vec_IntSetEntry( vMapping, Abc_Lit2Var(iLit), Vec_IntSize(vMapping2) );
        Vec_IntPush( vMapping2, Vec_IntSize(vLeaves) );
        Vec_IntForEachEntry( vLeaves, iLit, k )
            Vec_IntPush( vMapping2, Abc_Lit2Var(iLit) );
        Vec_IntPush( vMapping2, Abc_Lit2Var(Vec_IntEntry(vCopies, i)) );
    }
    Gia_ManForEachCo( p->pGia, pObj, i )
    {
        iLit = Vec_IntEntry( vCopies, Gia_ObjFaninId0p(p->pGia, pObj) );
        Gia_ManAppendCo( pNew, Abc_LitNotCond(iLit, Gia_ObjFaninC0(pObj)) );
    }
    Vec_IntFree( vCopies );
    Vec_IntFree( vCover );
    Vec_IntFree( vLeaves );
    // finish mapping 
    if ( Vec_IntSize(vMapping) > Gia_ManObjNum(pNew) )
        Vec_IntShrink( vMapping, Gia_ManObjNum(pNew) );
    else
        Vec_IntFillExtra( vMapping, Gia_ManObjNum(pNew), 0 );
    assert( Vec_IntSize(vMapping) == Gia_ManObjNum(pNew) );
    Vec_IntForEachEntry( vMapping, iLit, i )
        if ( iLit > 0 )
            Vec_IntAddToEntry( vMapping, i, Gia_ManObjNum(pNew) );
    Vec_IntAppend( vMapping, vMapping2 );
    Vec_IntFree( vMapping2 );
    // attach mapping and packing
    assert( pNew->vMapping == NULL );
    pNew->vMapping = vMapping;
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p->pGia) );
    return pNew;
}
void Jf_ManDeriveMapping( Jf_Man_t * p )
{
    Vec_Int_t * vMapping;
    Gia_Obj_t * pObj; 
    int i, k, * pCut;
    assert( !p->pPars->fCutMin );
    vMapping = Vec_IntAlloc( Gia_ManObjNum(p->pGia) + (int)p->pPars->Edge + (int)p->pPars->Area * 2 );
    Vec_IntFill( vMapping, Gia_ManObjNum(p->pGia), 0 );
    Gia_ManForEachAnd( p->pGia, pObj, i )
    {
        if ( Gia_ObjIsBuf(pObj) || Gia_ObjRefNum(p->pGia, pObj) == 0 )
            continue;
        pCut = Jf_ObjCutBest( p, i );
        Vec_IntWriteEntry( vMapping, i, Vec_IntSize(vMapping) );
        assert( Jf_CutSize(pCut) <= 6 );
        Vec_IntPush( vMapping, Jf_CutSize(pCut) );
        for ( k = 1; k <= Jf_CutSize(pCut); k++ )
            Vec_IntPush( vMapping, Jf_CutVar(pCut, k) );
        Vec_IntPush( vMapping, i );
    }
    assert( Vec_IntCap(vMapping) == 16 || Vec_IntSize(vMapping) == Vec_IntCap(vMapping) );
    p->pGia->vMapping = vMapping;
//    Gia_ManMappingVerify( p->pGia );
}

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

  Synopsis    [Derive GIA without mapping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Jf_ManDeriveGia( Jf_Man_t * p )
{
    Gia_Man_t * pNew;
    Gia_Obj_t * pObj; 
    Vec_Int_t * vCopies   = Vec_IntStartFull( Gia_ManObjNum(p->pGia) );
    Vec_Int_t * vCover    = Vec_IntAlloc( 1 << 16 );
    Vec_Int_t * vLeaves   = Vec_IntAlloc( 16 );
    int i, k, iLit, Class, * pCut;
    word uTruth;
    assert( p->pPars->fCutMin );
    // create new manager
    pNew = Gia_ManStart( Gia_ManObjNum(p->pGia) );
    pNew->pName = Abc_UtilStrsav( p->pGia->pName );
    pNew->pSpec = Abc_UtilStrsav( p->pGia->pSpec );
    // map primary inputs
    Vec_IntWriteEntry( vCopies, 0, 0 );
    Gia_ManForEachCi( p->pGia, pObj, i )
        Vec_IntWriteEntry( vCopies, Gia_ObjId(p->pGia, pObj), Gia_ManAppendCi(pNew) );
    // iterate through nodes used in the mapping
    Gia_ManHashStart( pNew );
    Gia_ManForEachAnd( p->pGia, pObj, i )
    {
       if ( Gia_ObjIsBuf(pObj) || Gia_ObjRefNum(p->pGia, pObj) == 0 )
            continue;
        pCut = Jf_ObjCutBest( p, i );
//        printf( "Best cut of node %d:  ", i );  Jf_CutPrint(pCut);
        Class = Jf_CutFuncClass( pCut );
        uTruth = p->pPars->fUseTts ? *Vec_MemReadEntry(p->vTtMem, Class) : Sdm_ManReadDsdTruth(p->pDsd, Class);
        assert( p->pDsd == NULL || Sdm_ManReadDsdVarNum(p->pDsd, Class) == Jf_CutSize(pCut) );
        if ( Jf_CutSize(pCut) == 0 )
        {
            assert( Class == 0 );
            Vec_IntWriteEntry( vCopies, i, Jf_CutFunc(pCut) );
            continue;
        }
        if ( Jf_CutSize(pCut) == 1 )
        {
            assert( Class == 1 );
            iLit = Abc_LitNotCond( Jf_CutLit(pCut, 1) , Jf_CutFuncCompl(pCut) );
            iLit = Abc_Lit2LitL( Vec_IntArray(vCopies), iLit );
            Vec_IntWriteEntry( vCopies, i, iLit );
            continue;
        }
        // collect leaves
        Vec_IntClear( vLeaves );
        Jf_CutForEachLit( pCut, iLit, k )
            Vec_IntPush( vLeaves, Abc_Lit2LitL(Vec_IntArray(vCopies), iLit) );
        // create GIA
        iLit = Dsm_ManDeriveGia( pNew, uTruth, vLeaves, vCover );
        iLit = Abc_LitNotCond( iLit, Jf_CutFuncCompl(pCut) );
        Vec_IntWriteEntry( vCopies, i, iLit );
    }
    Gia_ManForEachCo( p->pGia, pObj, i )
    {
        iLit = Vec_IntEntry( vCopies, Gia_ObjFaninId0p(p->pGia, pObj) );
        Gia_ManAppendCo( pNew, Abc_LitNotCond(iLit, Gia_ObjFaninC0(pObj)) );
    }
    Vec_IntFree( vCopies );
    Vec_IntFree( vLeaves );
    Vec_IntFree( vCover );
    Gia_ManHashStop( pNew );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(p->pGia) );
//    Dsm_ManReportStats();
    return pNew;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Jf_ManSetDefaultPars( Jf_Par_t * pPars )
{
    memset( pPars, 0, sizeof(Jf_Par_t) );
    pPars->nLutSize     =  6;
    pPars->nCutNum      =  8;
    pPars->nRounds      =  1;
    pPars->nVerbLimit   =  5;
    pPars->DelayTarget  = -1;
    pPars->fAreaOnly    =  1;
    pPars->fOptEdge     =  1; 
    pPars->fCoarsen     =  0;
    pPars->fCutMin      =  0;
    pPars->fUseTts      =  0;
    pPars->fGenCnf      =  0;
    pPars->fPureAig     =  0;
    pPars->fVerbose     =  0;
    pPars->fVeryVerbose =  0;
    pPars->nLutSizeMax  =  JF_LEAF_MAX;
    pPars->nCutNumMax   =  JF_CUT_MAX;
}
void Jf_ManPrintStats( Jf_Man_t * p, char * pTitle )
{
    if ( !p->pPars->fVerbose )
        return;
    printf( "%s :  ", pTitle );
    printf( "Level =%6lu   ", p->pPars->Delay );
    printf( "Area =%9lu   ",  p->pPars->Area );
    printf( "Edge =%9lu   ",  p->pPars->Edge );
    if ( p->pPars->fGenCnf )
    printf( "Cnf =%9lu   ",   p->pPars->Clause );
    Abc_PrintTime( 1, "Time", Abc_Clock() - p->clkStart );
    fflush( stdout );
}
Gia_Man_t * Jf_ManPerformMapping( Gia_Man_t * pGia, Jf_Par_t * pPars )
{
    Gia_Man_t * pNew = pGia;
    Jf_Man_t * p; int i;
    assert( !pPars->fCutMin || pPars->nLutSize <= 6 );
    if ( pPars->fGenCnf )
        pPars->fCutMin = 1, pPars->fUseTts = pPars->fOptEdge = 0;
    if ( pPars->fCutMin && pPars->fUseTts )
        pPars->fCoarsen = 0;
    p = Jf_ManAlloc( pGia, pPars );
    p->pCutCmp = pPars->fAreaOnly ? Jf_CutCompareArea : Jf_CutCompareDelay;
    Jf_ManComputeCuts( p, 0 );
    Jf_ManComputeRefs( p );                         Jf_ManPrintStats( p, "Start" );
    for ( i = 0; i < pPars->nRounds; i++ )
    {
        if ( !p->pPars->fGenCnf )
        {
        Jf_ManPropagateFlow( p, pPars->fOptEdge );  Jf_ManPrintStats( p, "Flow " );
        }
        Jf_ManPropagateEla( p, 0 );                 Jf_ManPrintStats( p, "Area " );
        Jf_ManPropagateEla( p, 1 );                 Jf_ManPrintStats( p, "Edge " );
    }
    if ( p->pPars->fPureAig )
        pNew = Jf_ManDeriveGia(p);
    else if ( p->pPars->fCutMin )
        pNew = Jf_ManDeriveMappingGia(p);
    else
        Jf_ManDeriveMapping(p);
    Jf_ManFree( p );
    return pNew;
}

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


ABC_NAMESPACE_IMPL_END

