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

  FileName    [giaShrink7.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Implementation of DAG-aware unmapping for 6-input cuts.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "misc/vec/vecHash.h"
#include "misc/util/utilTruth.h"


ABC_NAMESPACE_IMPL_START

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

// operation manager
typedef struct Unm_Man_t_ Unm_Man_t;
struct Unm_Man_t_
{
    Gia_Man_t *        pGia;           // user's AIG
    Gia_Man_t *        pNew;           // constructed AIG
    Hash_IntMan_t *    pHash;          // hash table
    int                nNewSize;       // expected size of new manager
    Vec_Int_t *        vUsed;          // used nodes
    Vec_Int_t *        vId2Used;       // mapping of obj IDs into used node IDs
    Vec_Wrd_t *        vTruths;        // truth tables
    Vec_Int_t *        vLeaves;        // temporary storage for leaves
    abctime            clkStart;       // starting the clock
};

extern word Shr_ManComputeTruth6( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vLeaves, Vec_Wrd_t * vTruths );

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

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

  Synopsis    [Check if the function is decomposable with the given pair.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Unm_ManCheckAnd( word t, int i, int j, word * pOut )
{
    word c0 = Abc_Tt6Cofactor0( t, i );
    word c1 = Abc_Tt6Cofactor1( t, i );
    word c00 = Abc_Tt6Cofactor0( c0, j );
    word c01 = Abc_Tt6Cofactor1( c0, j );
    word c10 = Abc_Tt6Cofactor0( c1, j );
    word c11 = Abc_Tt6Cofactor1( c1, j );
    if ( c00 == c01 && c00 == c10 ) //  i *  j
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c00) | (s_Truths6[i] & c11);
        return 0;
    }
    if ( c11 == c00 && c11 == c10 ) //  i * !j
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c11) | (s_Truths6[i] & c01);
        return 1;
    }
    if ( c11 == c00 && c11 == c01 ) // !i *  j
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c11) | (s_Truths6[i] & c10);
        return 2;
    }
    if ( c11 == c01 && c11 == c10 ) // !i * !j
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c11) | (s_Truths6[i] & c00);
        return 3;
    }
    if ( c00 == c11 && c01 == c10 )
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c11) | (s_Truths6[i] & c10);
        return 4;
    }
    return -1;
}
static inline int Unm_ManCheckMux( word t, int i, word * pOut )
{
    word c0 = Abc_Tt6Cofactor0( t, i );
    word c1 = Abc_Tt6Cofactor1( t, i );
    word c00, c01, c10, c11;
    int k, fPres0, fPres1, iVar0 = -1, iVar1 = -1;
    for ( k = 0; k < 6; k++ )
    {
        if ( k == i ) continue;
        fPres0 = Abc_Tt6HasVar( c0, k );
        fPres1 = Abc_Tt6HasVar( c1, k );
        if ( fPres0 && !fPres1 )
        {
            if ( iVar0 >= 0 )
                return -1;
            iVar0 = k;
        }
        if ( !fPres0 && fPres1 )
        {
            if ( iVar1 >= 0 )
                return -1;
            iVar1 = k;
        }
    }
    if ( iVar0 == -1 || iVar1 == -1 )
        return -1;
    c00 = Abc_Tt6Cofactor0( c0, iVar0 );
    c01 = Abc_Tt6Cofactor1( c0, iVar0 );
    c10 = Abc_Tt6Cofactor0( c1, iVar1 );
    c11 = Abc_Tt6Cofactor1( c1, iVar1 );
    if ( c00 ==  c10 && c01 ==  c11 ) //  ITE(i,  iVar1,  iVar0)
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c10) | (s_Truths6[i] & c11);
        return (Abc_Var2Lit(iVar1, 0) << 16) | Abc_Var2Lit(iVar0, 0);
    }
    if ( c00 == ~c10 && c01 == ~c11 ) //  ITE(i,  iVar1, !iVar0)
    {
        if ( pOut ) *pOut = (~s_Truths6[i] & c10) | (s_Truths6[i] & c11);
        return (Abc_Var2Lit(iVar1, 0) << 16) | Abc_Var2Lit(iVar0, 1);
    }
    return -1;
}
void Unm_ManCheckTest2()
{
    word t, t1, Out, Var0, Var1, Var0_, Var1_;
    int iVar0, iVar1, i, Res;
    for ( iVar0 = 0; iVar0 < 6; iVar0++ )
    for ( iVar1 = 0; iVar1 < 6; iVar1++ )
    {
        if ( iVar0 == iVar1 )
            continue;
        Var0 = s_Truths6[iVar0];
        Var1 = s_Truths6[iVar1];
        for ( i = 0; i < 5; i++ )
        {
            Var0_ = ((i >> 0) & 1) ? ~Var0 : Var0;
            Var1_ = ((i >> 1) & 1) ? ~Var1 : Var1;

            t = Var0_ & Var1_;
            if ( i == 4 )
                t = ~(Var0_ ^ Var1_);

//            Kit_DsdPrintFromTruth( (unsigned *)&t, 6 ), printf( "\n" );

            Res = Unm_ManCheckAnd( t, iVar0, iVar1, &Out );
            if ( Res == -1 )
            {
                printf( "No decomposition\n" );
                continue;
            }

            Var0_ = s_Truths6[iVar0];
            Var0_ = ((Res >> 0) & 1) ? ~Var0_ : Var0_;

            Var1_ = s_Truths6[iVar1];
            Var1_ = ((Res >> 1) & 1) ? ~Var1_ : Var1_;

            t1 = Var0_ & Var1_;
            if ( Res == 4 )
                t1 = Var0_ ^ Var1_;

            t1 = (~t1 & Abc_Tt6Cofactor0(Out, iVar0)) | (t1 & Abc_Tt6Cofactor1(Out, iVar0));

//            Kit_DsdPrintFromTruth( (unsigned *)&t1, 6 ), printf( "\n" );

            if ( t1 != t )
                printf( "Verification failed.\n" );
            else
                printf( "Verification succeeded.\n" );
        }
    }
}
void Unm_ManCheckTest()
{
    word t, t1, Out, Ctrl, Var0, Var1, Ctrl_, Var0_, Var1_;
    int iVar0, iVar1, iCtrl, i, Res;
    for ( iCtrl = 0; iCtrl < 6; iCtrl++ )
    for ( iVar0 = 0; iVar0 < 6; iVar0++ )
    for ( iVar1 = 0; iVar1 < 6; iVar1++ )
    {
        if ( iCtrl == iVar0 || iCtrl == iVar1 || iVar0 == iVar1 )
            continue;
        Ctrl = s_Truths6[iCtrl];
        Var0 = s_Truths6[iVar0];
        Var1 = s_Truths6[iVar1];
        for ( i = 0; i < 8; i++ )
        {
            Ctrl_ = ((i >> 0) & 1) ? ~Ctrl : Ctrl;
            Var0_ = ((i >> 1) & 1) ? ~Var0 : Var0;
            Var1_ = ((i >> 2) & 1) ? ~Var1 : Var1;

            t = (~Ctrl_ & Var0_) | (Ctrl_ & Var1_);

//            Kit_DsdPrintFromTruth( (unsigned *)&t, 6 ), printf( "\n" );

            Res = Unm_ManCheckMux( t, iCtrl, &Out );
            if ( Res == -1 )
            {
                printf( "No decomposition\n" );
                continue;
            }

//            Kit_DsdPrintFromTruth( (unsigned *)&Out, 6 ), printf( "\n" );

            Ctrl_ = s_Truths6[iCtrl];
            Var0_ = s_Truths6[Abc_Lit2Var(Res & 0xFFFF)];
            Var0_ = Abc_LitIsCompl(Res & 0xFFFF) ? ~Var0_ : Var0_;

            Res >>= 16;
            Var1_ = s_Truths6[Abc_Lit2Var(Res & 0xFFFF)];
            Var1_ = Abc_LitIsCompl(Res & 0xFFFF) ? ~Var1_ : Var1_;

            t1 = (~Ctrl_ & Var0_) | (Ctrl_ & Var1_);

//            Kit_DsdPrintFromTruth( (unsigned *)&t1, 6 ), printf( "\n" );
//            Kit_DsdPrintFromTruth( (unsigned *)&Out, 6 ), printf( "\n" );

            t1 = (~t1 & Abc_Tt6Cofactor0(Out, iCtrl)) | (t1 & Abc_Tt6Cofactor1(Out, iCtrl));

//            Kit_DsdPrintFromTruth( (unsigned *)&t1, 6 ), printf( "\n" );

            if ( t1 != t )
                printf( "Verification failed.\n" );
            else
                printf( "Verification succeeded.\n" );
        }
    }
}


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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
Unm_Man_t * Unm_ManAlloc( Gia_Man_t * pGia )
{
    Unm_Man_t * p;
    p = ABC_CALLOC( Unm_Man_t, 1 );
    p->clkStart    = Abc_Clock();
    p->nNewSize    = 3 * Gia_ManObjNum(pGia) / 2;
    p->pGia        = pGia;
    p->pNew        = Gia_ManStart( p->nNewSize );
    p->pNew->pName = Abc_UtilStrsav( pGia->pName );
    p->pNew->pSpec = Abc_UtilStrsav( pGia->pSpec );
    Gia_ManHashAlloc( p->pNew );
    Gia_ManCleanLevels( p->pNew, p->nNewSize );
    // allocate traversal IDs
    p->pNew->nObjs = p->nNewSize;
    Gia_ManIncrementTravId( p->pNew );
    p->pNew->nObjs = 1;
    // start hashing
    p->pHash = Hash_IntManStart( 1000 );
    // truth tables
    p->vLeaves = Vec_IntStart( 10 );
    return p;
}
Gia_Man_t * Unm_ManFree( Unm_Man_t * p )
{
    Gia_Man_t * pTemp = p->pNew; p->pNew = NULL;
    Gia_ManHashStop( pTemp );
    Vec_IntFreeP( &pTemp->vLevels );
    Gia_ManSetRegNum( pTemp, Gia_ManRegNum(p->pGia) );
    // truth tables
    Vec_WrdFreeP( &p->vTruths );
    Vec_IntFreeP( &p->vLeaves );
    Vec_IntFreeP( &p->vUsed );
    Vec_IntFreeP( &p->vId2Used );
    // free data structures
    Hash_IntManStop( p->pHash );
    ABC_FREE( p );

    Gia_ManStop( pTemp );
    pTemp = NULL;

    return pTemp;
}

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

  Synopsis    [Computes information about node pairs.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
int Unm_ManPrintPairStats( Hash_IntMan_t * pHash, int nTotal0, int nPairs0, int nPairs1, int fUseLit )
{
    int i, Num, nRefs, nPairs = 0, nTotal = 0, Counter[21] = {0};
    Num = Hash_IntManEntryNum( pHash );
    for ( i = 1; i <= Num; i++ )
    {
        nRefs = Abc_MinInt( 20, Hash_IntObjData2(pHash, i) );
        nTotal += nRefs;
        Counter[nRefs]++;
        nPairs += (nRefs > 1);
/*
        if ( fUseLit )
            printf( "(%c%c, %c%c) %d\n", 
                Abc_LitIsCompl(Hash_IntObjData0(pHash, i)-2) ? '!' : ' ', 
                'a' + Abc_Lit2Var(Hash_IntObjData0(pHash, i)-2), 
                Abc_LitIsCompl(Hash_IntObjData1(pHash, i)-2) ? '!' : ' ', 
                'a' + Abc_Lit2Var(Hash_IntObjData1(pHash, i)-2), nRefs );
        else
            printf( "( %c,  %c) %d\n", 
                'a' + Hash_IntObjData0(pHash, i)-1, 
                'a' + Hash_IntObjData1(pHash, i)-1, nRefs );
*/
//        printf( "(%4d, %4d) %d\n", Hash_IntObjData0(pHash, i), Hash_IntObjData1(pHash, i), nRefs );

    }
    printf( "Statistics for pairs appearing less than 20 times:\n" );
    for ( i = 0; i < 21; i++ )
        if ( Counter[i] > 0 )
            printf( "%3d : %7d  %7.2f %%\n", i, Counter[i], 100.0 * Counter[i] * i / Abc_MaxInt(nTotal, 1) );
    printf( "Pairs:  Total = %8d    Init = %8d %7.2f %%    Final = %8d %7.2f %%    Real = %8d %7.2f %%\n", nTotal0, 
        nPairs0,  100.0 * nPairs0 / Abc_MaxInt(nTotal0, 1), 
        nPairs, 100.0 * nPairs / Abc_MaxInt(nTotal0, 1), 
        nPairs1, 100.0 * nPairs1 / Abc_MaxInt(nTotal0, 1) );
    return nPairs;
}
Vec_Int_t * Unm_ManComputePairs( Unm_Man_t * p, int fVerbose )
{
    Gia_Obj_t * pObj;
    Vec_Int_t * vPairs = Vec_IntAlloc( 1000 );
    Vec_Int_t * vNum2Obj = Vec_IntStart( 1 );
    Hash_IntMan_t * pHash = Hash_IntManStart( 1000 );
    int nTotal = 0, nPairs0 = 0, nPairs = 0;
    int i, k, j, FanK, FanJ, Num, nRefs;
    Gia_ManSetRefsMapped( p->pGia );
    Gia_ManForEachLut( p->pGia, i )
    {
        nTotal += Gia_ObjLutSize(p->pGia, i) * (Gia_ObjLutSize(p->pGia, i) - 1) / 2;
        pObj = Gia_ManObj( p->pGia, i );
        // collect leaves of this gate  
        Vec_IntClear( p->vLeaves );
        Gia_LutForEachFanin( p->pGia, i, Num, k )
            if ( Gia_ObjRefNumId(p->pGia, Num) > 1 )
                Vec_IntPush( p->vLeaves, Num );
        if ( Vec_IntSize(p->vLeaves) < 2 )
            continue;
        nPairs0 += Vec_IntSize(p->vLeaves) * (Vec_IntSize(p->vLeaves) - 1) / 2;
        // enumerate pairs
        Vec_IntForEachEntry( p->vLeaves, FanK, k )
        Vec_IntForEachEntryStart( p->vLeaves, FanJ, j, k+1 )
        {
            if ( FanK > FanJ )
                ABC_SWAP( int, FanK, FanJ );
            Num = Hash_Int2ManInsert( pHash, FanK, FanJ, 0 );
            nRefs = Hash_Int2ObjInc(pHash, Num);
            if ( nRefs == 0 )
            {
                assert( Num == Hash_IntManEntryNum(pHash) );
                assert( Num == Vec_IntSize(vNum2Obj) );
                Vec_IntPush( vNum2Obj, i );
                continue;
            }
            if ( nRefs == 1 )
            {
                assert( Num < Vec_IntSize(vNum2Obj) );
                Vec_IntPush( vPairs, Vec_IntEntry(vNum2Obj, Num) );
                Vec_IntPush( vPairs, FanK );
                Vec_IntPush( vPairs, FanJ);
            }
            Vec_IntPush( vPairs, i );
            Vec_IntPush( vPairs, FanK );
            Vec_IntPush( vPairs, FanJ );
        }
    }
    Vec_IntFree( vNum2Obj );
    if ( fVerbose )
        nPairs = Unm_ManPrintPairStats( pHash, nTotal, nPairs0, Vec_IntSize(vPairs) / 3, 0 );
    Hash_IntManStop( pHash );
    return vPairs;    
}
// finds used nodes
Vec_Int_t * Unm_ManFindUsedNodes( Vec_Int_t * vPairs, int nObjs )
{
    Vec_Int_t * vNodes = Vec_IntAlloc( 1000 );
    Vec_Str_t * vMarks = Vec_StrStart( nObjs ); int i;
    for ( i = 0; i < Vec_IntSize(vPairs); i += 3 )
        Vec_StrWriteEntry( vMarks, Vec_IntEntry(vPairs, i), 1 );
    for ( i = 0; i < nObjs; i++ )
        if ( Vec_StrEntry( vMarks, i ) )
            Vec_IntPush( vNodes, i );
    Vec_StrFree( vMarks );
    printf( "The number of used nodes = %d\n", Vec_IntSize(vNodes) );
    return vNodes;
}
// computes truth table for selected nodes
Vec_Wrd_t * Unm_ManComputeTruths( Unm_Man_t * p )
{
    Vec_Wrd_t * vTruthsTemp, * vTruths;
    int i, k, iObj, iNode;
    word uTruth;
    vTruths = Vec_WrdAlloc( Vec_IntSize(p->vUsed) );
    vTruthsTemp = Vec_WrdStart( Gia_ManObjNum(p->pGia) );
    Vec_IntForEachEntry( p->vUsed, iObj, i )
    {
        assert( Gia_ObjIsLut(p->pGia, iObj) );
        // collect leaves of this gate  
        Vec_IntClear( p->vLeaves );
        Gia_LutForEachFanin( p->pGia, iObj, iNode, k )
            Vec_IntPush( p->vLeaves, iNode );
        assert( Vec_IntSize(p->vLeaves) <= 6 );
        // compute truth table 
        uTruth = Shr_ManComputeTruth6( p->pGia, Gia_ManObj(p->pGia, iObj), p->vLeaves, vTruthsTemp );
        Vec_WrdPush( vTruths, uTruth );
//        if ( i % 100 == 0 )
//            Kit_DsdPrintFromTruth( (unsigned *)&uTruth, 6 ), printf( "\n" );
    }
    Vec_WrdFreeP( &vTruthsTemp );
    return vTruths;
}

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

  Synopsis    [Collects decomposable pairs.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Unm_ManCollectDecomp( Unm_Man_t * p, Vec_Int_t * vPairs, int fVerbose )
{
    word uTruth; int nNonUnique = 0;
    int i, k, j, s, iObj, iNode, iUsed, FanK, FanJ, Res, Num, nRefs;
    Vec_Int_t * vNum2Obj = Vec_IntStart( 1 );
    Vec_Int_t * vPairs2 = Vec_IntAlloc( 1000 );
    assert( Hash_IntManEntryNum(p->pHash) == 0 );
    for ( i = 0; i < Vec_IntSize(vPairs); i += 3 )
    {
        iObj = Vec_IntEntry( vPairs, i );
        assert( Gia_ObjIsLut(p->pGia, iObj) );
        // collect leaves of this gate  
        Vec_IntClear( p->vLeaves );
        Gia_LutForEachFanin( p->pGia, iObj, iNode, s )
            Vec_IntPush( p->vLeaves, iNode );
        assert( Vec_IntSize(p->vLeaves) <= 6 );
        FanK = Vec_IntEntry(vPairs, i+1);
        FanJ = Vec_IntEntry(vPairs, i+2);
        k = Vec_IntFind( p->vLeaves, FanK );
        j = Vec_IntFind( p->vLeaves, FanJ );
        assert( FanK < FanJ );
        iUsed = Vec_IntEntry( p->vId2Used, iObj );
        uTruth = Vec_WrdEntry( p->vTruths, iUsed );
        Res = Unm_ManCheckAnd( uTruth, k, j, NULL );
        if ( Res == -1 )
            continue;
        // derive literals
        FanK = Abc_Var2Lit( FanK, ((Res >> 0) & 1) );
        FanJ = Abc_Var2Lit( FanJ, ((Res >> 1) & 1) );
        if ( Res == 4 )
            ABC_SWAP( int, FanK, FanJ );
        Num = Hash_Int2ManInsert( p->pHash, FanK, FanJ, 0 );
        nRefs = Hash_Int2ObjInc(p->pHash, Num);
        if ( nRefs == 0 )
        {
            assert( Num == Hash_IntManEntryNum(p->pHash) );
            assert( Num == Vec_IntSize(vNum2Obj) );
            Vec_IntPush( vNum2Obj, iObj );
            continue;
        }
        if ( nRefs == 1 )
        {
            assert( Num < Vec_IntSize(vNum2Obj) );
            Vec_IntPush( vPairs2, Vec_IntEntry(vNum2Obj, Num) );
            Vec_IntPush( vPairs2, FanK );
            Vec_IntPush( vPairs2, FanJ );
            nNonUnique++;
        }
        Vec_IntPush( vPairs2, iObj );
        Vec_IntPush( vPairs2, FanK );
        Vec_IntPush( vPairs2, FanJ );
    }
    Vec_IntFree( vNum2Obj );
    if ( fVerbose )
        Unm_ManPrintPairStats( p->pHash, Vec_IntSize(vPairs)/3, Hash_IntManEntryNum(p->pHash), Vec_IntSize(vPairs2)/3, 1 );
//    Hash_IntManStop( pHash );
    return vPairs2;    
}

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

  Synopsis    [Compute truth tables for the selected nodes.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void Unm_ManWork( Unm_Man_t * p )
{
    Vec_Int_t * vPairs, * vPairs2;
    // find the duplicated pairs
    vPairs = Unm_ManComputePairs( p, 1 );
    // find the used nodes
    p->vUsed = Unm_ManFindUsedNodes( vPairs, Gia_ManObjNum(p->pGia) );
    p->vId2Used = Vec_IntInvert( p->vUsed, -1 );
    Vec_IntFillExtra( p->vId2Used, Gia_ManObjNum(p->pGia), -1 );
    // compute truth tables for used nodes
    p->vTruths = Unm_ManComputeTruths( p );
    // derive new pairs
    vPairs2 = Unm_ManCollectDecomp( p, vPairs, 1 );
    Vec_IntFreeP( &vPairs );
    Vec_IntFreeP( &vPairs2 );
}


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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Unm_ManTest( Gia_Man_t * pGia )
{
    Unm_Man_t * p;
    p = Unm_ManAlloc( pGia );
    Unm_ManWork( p );

    Abc_PrintTime( 1, "Time", Abc_Clock() - p->clkStart );
    return Unm_ManFree( p );
}

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


ABC_NAMESPACE_IMPL_END