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

  FileName    [giaCone.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: giaCone.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

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

#include "gia.h"
#include "misc/extra/extra.h"
#include "misc/vec/vecHsh.h"
#include "misc/vec/vecWec.h"

ABC_NAMESPACE_IMPL_START


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

typedef struct Opa_Man_t_ Opa_Man_t;
struct Opa_Man_t_
{
    Gia_Man_t *    pGia;
    Vec_Int_t *    vFront;
    Vec_Int_t *    pvParts;
    int *          pId2Part; 
    int            nParts;
};

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////
    
/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Opa_Man_t * Opa_ManStart( Gia_Man_t * pGia)
{
    Opa_Man_t * p;
    Gia_Obj_t * pObj;
    int i;
    p = ABC_CALLOC( Opa_Man_t, 1 );
    p->pGia = pGia;
    p->pvParts  = ABC_CALLOC( Vec_Int_t, Gia_ManPoNum(pGia) );
    p->pId2Part = ABC_FALLOC( int, Gia_ManObjNum(pGia) );
    p->vFront   = Vec_IntAlloc( 100 );
    Gia_ManForEachPo( pGia, pObj, i )
    {
        Vec_IntPush( p->pvParts + i, Gia_ObjId(pGia, pObj) );
        p->pId2Part[Gia_ObjId(pGia, pObj)] = i;
        Vec_IntPush( p->vFront, Gia_ObjId(pGia, pObj) );
    }
    p->nParts = Gia_ManPoNum(pGia);
    return p;
}
static inline void Opa_ManStop( Opa_Man_t * p )
{
    int i;
    Vec_IntFree( p->vFront );
    for ( i = 0; i < Gia_ManPoNum(p->pGia); i++ )
        ABC_FREE( p->pvParts[i].pArray );
    ABC_FREE( p->pvParts );
    ABC_FREE( p->pId2Part );
    ABC_FREE( p );
}
static inline void Opa_ManPrint( Opa_Man_t * p )
{
    int i, k;
    printf( "Groups:\n" );
    for ( i = 0; i < Gia_ManPoNum(p->pGia); i++ )
    {
        if ( p->pvParts[i].nSize == 0 )
            continue;
        printf( "%3d : ", i );
        for ( k = 0; k < p->pvParts[i].nSize; k++ )
            printf( "%d ", p->pvParts[i].pArray[k] );
        printf( "\n" );
    }
}
static inline void Opa_ManPrint2( Opa_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i, k, Count;
    printf( "Groups %d: ", p->nParts );
    for ( i = 0; i < Gia_ManPoNum(p->pGia); i++ )
    {
        if ( p->pvParts[i].nSize == 0 )
            continue;
        // count POs in this group
        Count = 0;
        Gia_ManForEachObjVec( p->pvParts + i, p->pGia, pObj, k )
            Count += Gia_ObjIsPo(p->pGia, pObj);
        printf( "%d ", Count );
    }
    printf( "\n" );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Opa_ManMoveOne( Opa_Man_t * p, Gia_Obj_t * pObj, Gia_Obj_t * pFanin )
{
    int iObj   = Gia_ObjId(p->pGia, pObj);
    int iFanin = Gia_ObjId(p->pGia, pFanin);
    if ( iFanin == 0 )
        return;
    assert( p->pId2Part[ iObj ] >= 0 );
    if ( p->pId2Part[ iFanin ] == -1 )
    {
        p->pId2Part[ iFanin ] = p->pId2Part[ iObj ];
        Vec_IntPush( p->pvParts + p->pId2Part[ iObj ], iFanin );
        assert( Gia_ObjIsCi(pFanin) || Gia_ObjIsAnd(pFanin) );
        if ( Gia_ObjIsAnd(pFanin) )
            Vec_IntPush( p->vFront, iFanin );
        else if ( Gia_ObjIsRo(p->pGia, pFanin) )
        {
            pFanin = Gia_ObjRoToRi(p->pGia, pFanin);
            iFanin = Gia_ObjId(p->pGia, pFanin);
            assert( p->pId2Part[ iFanin ] == -1 );
            p->pId2Part[ iFanin ] = p->pId2Part[ iObj ];
            Vec_IntPush( p->pvParts + p->pId2Part[ iObj ], iFanin );
            Vec_IntPush( p->vFront, iFanin );
        }
    }
    else if ( p->pId2Part[ iObj ] != p->pId2Part[ iFanin ] )
    {
        Vec_Int_t * vPartObj = p->pvParts + p->pId2Part[ iObj ];
        Vec_Int_t * vPartFan = p->pvParts + p->pId2Part[ iFanin ];
        int iTemp, i;
//        printf( "Moving %d to %d (%d -> %d)\n", iObj, iFanin, Vec_IntSize(vPartObj), Vec_IntSize(vPartFan) );
        // add group of iObj to group of iFanin
        assert( Vec_IntSize(vPartObj) > 0 );
        Vec_IntForEachEntry( vPartObj, iTemp, i )
        {
            Vec_IntPush( vPartFan, iTemp );
            p->pId2Part[ iTemp ] = p->pId2Part[ iFanin ];
        }
        Vec_IntShrink( vPartObj, 0 );
        p->nParts--;
    }
}
void Opa_ManPerform( Gia_Man_t * pGia )
{
    Opa_Man_t * p;
    Gia_Obj_t * pObj;
    int i, Limit, Count = 0;
 
    p = Opa_ManStart( pGia );
    Limit = Vec_IntSize(p->vFront);
//Opa_ManPrint2( p );
    Gia_ManForEachObjVec( p->vFront, pGia, pObj, i )
    {
        if ( i == Limit )
        {
            printf( "%6d : %6d -> %6d\n", ++Count, i, p->nParts );
            Limit = Vec_IntSize(p->vFront);
            if ( Count > 1 )
                Opa_ManPrint2( p );
        }
//        printf( "*** Object %d  ", Gia_ObjId(pGia, pObj) );
        if ( Gia_ObjIsAnd(pObj) )
        {
            Opa_ManMoveOne( p, pObj, Gia_ObjFanin0(pObj) );
            Opa_ManMoveOne( p, pObj, Gia_ObjFanin1(pObj) );
        }
        else if ( Gia_ObjIsCo(pObj) )
            Opa_ManMoveOne( p, pObj, Gia_ObjFanin0(pObj) );
        else assert( 0 );
//        if ( i % 10 == 0 )
//            printf( "%d   ", p->nParts );
        if ( p->nParts == 1 )
            break;
        if ( Count == 5 )
            break;
    }
    printf( "\n" );
    Opa_ManStop( p );
}



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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManConeMark_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vRoots, int nLimit )
{
    if ( Gia_ObjIsTravIdCurrent(p, pObj) )
        return 0;
    Gia_ObjSetTravIdCurrent(p, pObj);
    if ( Gia_ObjIsAnd(pObj) )
    {
        if ( Gia_ManConeMark_rec( p, Gia_ObjFanin0(pObj), vRoots, nLimit ) )
            return 1;
        if ( Gia_ManConeMark_rec( p, Gia_ObjFanin1(pObj), vRoots, nLimit ) )
            return 1;
    }
    else if ( Gia_ObjIsCo(pObj) )
    {
        if ( Gia_ManConeMark_rec( p, Gia_ObjFanin0(pObj), vRoots, nLimit ) )
            return 1;
    }
    else if ( Gia_ObjIsRo(p, pObj) )
        Vec_IntPush( vRoots, Gia_ObjId(p, Gia_ObjRoToRi(p, pObj)) );
    else if ( Gia_ObjIsPi(p, pObj) )
    {}
    else assert( 0 );
    return (int)(Vec_IntSize(vRoots) > nLimit);
}
int Gia_ManConeMark( Gia_Man_t * p, int iOut, int Limit )
{
    Vec_Int_t * vRoots;
    Gia_Obj_t * pObj;
    int i, RetValue;
    // start the outputs
    pObj = Gia_ManPo( p, iOut );
    vRoots = Vec_IntAlloc( 100 );
    Vec_IntPush( vRoots, Gia_ObjId(p, pObj) );
    // mark internal nodes
    Gia_ManIncrementTravId( p );
    Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) );
    Gia_ManForEachObjVec( vRoots, p, pObj, i )
        if ( Gia_ManConeMark_rec( p, pObj, vRoots, Limit ) )
            break;
    RetValue = Vec_IntSize( vRoots ) - 1;
    Vec_IntFree( vRoots );
    return RetValue;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManCountFlops( Gia_Man_t * p, Vec_Int_t * vOuts )
{
    int Limit = ABC_INFINITY;
    Vec_Int_t * vRoots;
    Gia_Obj_t * pObj;
    int i, RetValue, iOut;
    // start the outputs
    vRoots = Vec_IntAlloc( 100 );
    Vec_IntForEachEntry( vOuts, iOut, i )
    {
        pObj = Gia_ManPo( p, iOut );
        Vec_IntPush( vRoots, Gia_ObjId(p, pObj) );
    }
    // mark internal nodes
    Gia_ManIncrementTravId( p );
    Gia_ObjSetTravIdCurrent( p, Gia_ManConst0(p) );
    Gia_ManForEachObjVec( vRoots, p, pObj, i )
        if ( Gia_ManConeMark_rec( p, pObj, vRoots, Limit ) )
            break;
    RetValue = Vec_IntSize( vRoots ) - Vec_IntSize(vOuts);
    Vec_IntFree( vRoots );
    return RetValue;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManFindPoPartition3( Gia_Man_t * p, int iOut, int nDelta, int nOutsMin, int nOutsMax, int fVerbose, Vec_Ptr_t ** pvPosEquivs )
{
/*
    int i, Count = 0;
    // mark nodes belonging to output 'iOut'
    for ( i = 0; i < Gia_ManPoNum(p); i++ )
        Count += (Gia_ManConeMark(p, i, 10000) < 10000);
     //   printf( "%d ", Gia_ManConeMark(p, i, 1000) );
    printf( "%d out of %d\n", Count, Gia_ManPoNum(p) );

    // add other outputs as long as they are nDelta away
*/
//    Opa_ManPerform( p );

    return NULL;
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Gia_ManFindPivots( Gia_Man_t * p, int SelectShift, int fOnlyCis, int fVerbose )
{
    Vec_Int_t * vPivots, * vWeights;
    Vec_Int_t * vCount, * vResult;
    int i, j, Count, * pPerm, Limit;
/*
    Gia_Obj_t * pObj;
    // count MUX controls
    vCount = Vec_IntStart( Gia_ManObjNum(p) );
    Gia_ManForEachAnd( p, pObj, i )
    {
        Gia_Obj_t * pNodeC, * pNodeT, * pNodeE;
        if ( !Gia_ObjIsMuxType(pObj) ) 
            continue;
        pNodeC = Gia_ObjRecognizeMux( pObj, &pNodeT, &pNodeE );
        Vec_IntAddToEntry( vCount, Gia_ObjId(p, Gia_Regular(pNodeC)), 1 );
    }
*/
    // count references
    Gia_ManCreateRefs( p );
    vCount = Vec_IntAllocArray( p->pRefs, Gia_ManObjNum(p) ); p->pRefs = NULL;

    // collect nodes 
    vPivots  = Vec_IntAlloc( 100 );
    vWeights = Vec_IntAlloc( 100 );
    Vec_IntForEachEntry( vCount, Count, i )
    {
        if ( Count < 2 ) continue;
        if ( fOnlyCis && !Gia_ObjIsCi(Gia_ManObj(p, i)) )
            continue;
        Vec_IntPush( vPivots, i );
        Vec_IntPush( vWeights, Count );
    }
    Vec_IntFree( vCount );

    if ( fVerbose )
        printf( "Selected %d pivots with more than one fanout (out of %d CIs and ANDs).\n", Vec_IntSize(vWeights), Gia_ManCiNum(p) + Gia_ManAndNum(p) );

    // permute
    Gia_ManRandom(1);
    Gia_ManRandom(0);
    for ( i = 0; i < Vec_IntSize(vWeights); i++ )
    {
        j = (Gia_ManRandom(0) >> 1) % Vec_IntSize(vWeights);
        ABC_SWAP( int, vPivots->pArray[i], vPivots->pArray[j] );
        ABC_SWAP( int, vWeights->pArray[i], vWeights->pArray[j] );
    }
    // sort
    if ( SelectShift == 0 )
        pPerm = Abc_QuickSortCost( Vec_IntArray(vWeights), Vec_IntSize(vWeights), 1 );
    else
    {
        Vec_Int_t * vTemp = Vec_IntStartNatural( Vec_IntSize(vWeights) );
        pPerm = Vec_IntReleaseArray( vTemp );
        Vec_IntFree( vTemp );
    }

    // select    
    Limit = Abc_MinInt( 64, Vec_IntSize(vWeights) );
    vResult = Vec_IntAlloc( Limit );
    for ( i = 0; i < Limit; i++ )
    {
        j = (i + SelectShift) % Vec_IntSize(vWeights);
        if ( fVerbose )
            printf( "%2d : Pivot =%7d  Fanout =%7d\n", j, Vec_IntEntry(vPivots, pPerm[j]), Vec_IntEntry(vWeights, pPerm[j]) );
        Vec_IntPush( vResult, Vec_IntEntry(vPivots, pPerm[j]) );
    }

    Vec_IntFree( vPivots );
    Vec_IntFree( vWeights );
    ABC_FREE( pPerm );

    return vResult;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Wrd_t * Gia_ManDeriveSigns( Gia_Man_t * p, Vec_Int_t * vPivots, int fVerbose )
{
    Vec_Wrd_t * vSigns;
    Gia_Obj_t * pObj, * pObjRi;
    int i, fChange = 1, Counter;

    Gia_ManFillValue( p );
    Gia_ManForEachObjVec( vPivots, p, pObj, i )
        pObj->Value = i;

    if ( fVerbose )
        printf( "Signature propagation: " );
    vSigns = Vec_WrdStart( Gia_ManObjNum(p) );
    while ( fChange )
    {
        fChange = 0;
        Gia_ManForEachObj( p, pObj, i )
        {
            if ( ~pObj->Value )
            {
                assert( pObj->Value >= 0 && pObj->Value < 64 );
                *Vec_WrdEntryP( vSigns, i ) |= ( (word)1 << pObj->Value );
            }
            if ( Gia_ObjIsAnd(pObj) )
                *Vec_WrdEntryP( vSigns, i ) |= Vec_WrdEntry(vSigns, Gia_ObjFaninId0(pObj, i)) | Vec_WrdEntry(vSigns, Gia_ObjFaninId1(pObj, i));
            else if ( Gia_ObjIsCo(pObj) )
                *Vec_WrdEntryP( vSigns, i ) |= Vec_WrdEntry(vSigns, Gia_ObjFaninId0(pObj, i));
        }
        Counter = 0;
        Gia_ManForEachRiRo( p, pObjRi, pObj, i )
        {
            word Value = Vec_WrdEntry(vSigns, Gia_ObjId(p, pObj));
            *Vec_WrdEntryP( vSigns, Gia_ObjId(p, pObj) ) |= Vec_WrdEntry(vSigns, Gia_ObjId(p, pObjRi));
            if ( Value != Vec_WrdEntry(vSigns, Gia_ObjId(p, pObj)) )
                fChange = 1, Counter++;
        }
        if ( fVerbose )
            printf( "%d ", Counter );
    }
    if ( fVerbose )
       printf( "\n" );
    return vSigns;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Gia_ManHashOutputs( Gia_Man_t * p, Vec_Wrd_t * vSigns, int fVerbose )
{
    Vec_Ptr_t * vBins;
    Vec_Wec_t * vClasses;
    Vec_Wrd_t * vSignsPo;
    Vec_Int_t * vPriority, * vBin;
    Gia_Obj_t * pObj;
    int i;
    // collect PO signatures
    vSignsPo = Vec_WrdAlloc( Gia_ManPoNum(p) );
    Gia_ManForEachPo( p, pObj, i )
        Vec_WrdPush( vSignsPo, Vec_WrdEntry(vSigns, Gia_ObjId(p, pObj)) );
    // find equivalence classes
    vPriority = Hsh_WrdManHashArray( vSignsPo, 1 );
    Vec_WrdFree( vSignsPo );
    vClasses = Vec_WecCreateClasses( vPriority );
    Vec_IntFree( vPriority );
    vBins = (Vec_Ptr_t *)Vec_WecConvertToVecPtr( vClasses );
    Vec_WecFree( vClasses );
    Vec_VecSort( (Vec_Vec_t *)vBins, 1 );

    if ( fVerbose )
        printf( "Computed %d partitions:\n", Vec_PtrSize(vBins) );
    if ( !fVerbose )
        printf( "Listing partitions with more than 100 outputs:\n" );
    Vec_PtrForEachEntry( Vec_Int_t *, vBins, vBin, i )
    {
        assert( Vec_IntSize(vBin) > 0 );
        if ( fVerbose || Vec_IntSize(vBin) > 100 )
        {
            int PoNum = Vec_IntEntry( vBin, 0 );
            Gia_Obj_t * pObj = Gia_ManPo( p, PoNum );
            word Sign = Vec_WrdEntry( vSigns, Gia_ObjId(p, pObj) );
            // print
            printf( "%3d ", i );
            Extra_PrintBinary( stdout, (unsigned *)&Sign, 64 );
            printf( "  " );
            printf( "PO =%7d  ", Vec_IntSize(vBin) );
            printf( "FF =%7d", Gia_ManCountFlops(p, vBin) );
            printf( "\n" );
        }
    }
    return vBins;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManFindPoPartition2( Gia_Man_t * p, int iStartNum, int nDelta, int nOutsMin, int nOutsMax, int fSetLargest, int fVerbose, Vec_Ptr_t ** pvPosEquivs )
{
    return NULL;
}


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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManFindPoPartition( Gia_Man_t * p, int SelectShift, int fOnlyCis, int fSetLargest, int fVerbose, Vec_Ptr_t ** pvPosEquivs )
{
    Gia_Man_t * pGia = NULL;
    Vec_Int_t * vPivots;
    Vec_Wrd_t * vSigns;
    Vec_Ptr_t * vParts;
    Vec_Int_t * vPart;
    abctime clk = Abc_Clock();
    vPivots = Gia_ManFindPivots( p, SelectShift, fOnlyCis, fVerbose );
    vSigns = Gia_ManDeriveSigns( p, vPivots, fVerbose );
    Vec_IntFree( vPivots );
    vParts = Gia_ManHashOutputs( p, vSigns, fVerbose );
    Vec_WrdFree( vSigns );
    if ( fSetLargest )
    {
        vPart = Vec_VecEntryInt( (Vec_Vec_t *)vParts, 0 );
        pGia = Gia_ManDupCones( p, Vec_IntArray(vPart), Vec_IntSize(vPart), 1 );
    }
    if ( pvPosEquivs )
    {
        *pvPosEquivs = vParts;
        printf( "The algorithm divided %d POs into %d partitions.   ", Gia_ManPoNum(p), Vec_PtrSize(vParts) );
        Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
    }
    else
        Vec_VecFree( (Vec_Vec_t *)vParts );
    return pGia;
}

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


ABC_NAMESPACE_IMPL_END