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

  FileName    [aigIsoFast.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [AIG package.]

  Synopsis    [Graph isomorphism package.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - April 28, 2007.]

  Revision    [$Id: aigIsoFast.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $]

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

#include "saig.h"

ABC_NAMESPACE_IMPL_START


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

#define AIG_ISO_NUM 16

typedef struct Iso_Dat_t_ Iso_Dat_t;
struct Iso_Dat_t_
{
    unsigned      nFiNeg    :  3;
    unsigned      nFoNeg    :  2;
    unsigned      nFoPos    :  2;
    unsigned      Fi0Lev    :  3;
    unsigned      Fi1Lev    :  3;
    unsigned      Level     :  3;
    unsigned      fVisit    : 16;
};

typedef struct Iso_Dat2_t_ Iso_Dat2_t;
struct Iso_Dat2_t_
{
    unsigned      Data      : 16;
};

typedef struct Iso_Sto_t_ Iso_Sto_t;
struct Iso_Sto_t_
{
    Aig_Man_t *   pAig;       // user's AIG manager
    int           nObjs;      // number of objects
    Iso_Dat_t *   pData;      // data for each object
    Vec_Int_t *   vVisited;   // visited nodes
    Vec_Ptr_t *   vRoots;     // root nodes
    Vec_Int_t *   vPlaces;    // places in the counter lists
    int *         pCounters;  // counters    
};

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Iso_Sto_t * Iso_StoStart( Aig_Man_t * pAig )
{
    Iso_Sto_t * p;
    p = ABC_CALLOC( Iso_Sto_t, 1 );
    p->pAig      = pAig;
    p->nObjs     = Aig_ManObjNumMax( pAig );
    p->pData     = ABC_CALLOC( Iso_Dat_t, p->nObjs );
    p->vVisited  = Vec_IntStart( 1000 );
    p->vPlaces   = Vec_IntStart( 1000 );
    p->vRoots    = Vec_PtrStart( 1000 );
    p->pCounters = ABC_CALLOC( int, (1 << AIG_ISO_NUM) );
    return p;
}
void Iso_StoStop( Iso_Sto_t * p )
{
    Vec_IntFree( p->vPlaces );
    Vec_IntFree( p->vVisited );
    Vec_PtrFree( p->vRoots );
    ABC_FREE( p->pCounters );
    ABC_FREE( p->pData );
    ABC_FREE( p );
}

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

  Synopsis    [Collect statistics about one node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Iso_StoCollectInfo_rec( Aig_Man_t * p, Aig_Obj_t * pObj, int fCompl, Vec_Int_t * vVisited, Iso_Dat_t * pData, Vec_Ptr_t * vRoots )
{
    Iso_Dat_t * pThis = pData + Aig_ObjId(pObj);
    assert( Aig_ObjIsCi(pObj) || Aig_ObjIsNode(pObj) );
    if ( pThis->fVisit )
    {
        if ( fCompl )
            pThis->nFoNeg++;
        else
            pThis->nFoPos++;
        return;
    }
    assert( *((int *)pThis) == 0 );
    pThis->fVisit = 1;
    if ( fCompl )
        pThis->nFoNeg++;
    else
        pThis->nFoPos++;
    pThis->Level = pObj->Level;
    pThis->nFiNeg = Aig_ObjFaninC0(pObj) + Aig_ObjFaninC1(pObj);
    if ( Aig_ObjIsNode(pObj) )
    {
        if ( Aig_ObjFaninC0(pObj) < Aig_ObjFaninC1(pObj) || (Aig_ObjFaninC0(pObj) == Aig_ObjFaninC1(pObj) && Aig_ObjFanin0(pObj)->Level < Aig_ObjFanin1(pObj)->Level) )
        {
            pThis->Fi0Lev = pObj->Level - Aig_ObjFanin0(pObj)->Level;
            pThis->Fi1Lev = pObj->Level - Aig_ObjFanin1(pObj)->Level;
        }
        else
        {
            pThis->Fi0Lev = pObj->Level - Aig_ObjFanin1(pObj)->Level;
            pThis->Fi1Lev = pObj->Level - Aig_ObjFanin0(pObj)->Level;
        }
        Iso_StoCollectInfo_rec( p, Aig_ObjFanin0(pObj), Aig_ObjFaninC0(pObj), vVisited, pData, vRoots );
        Iso_StoCollectInfo_rec( p, Aig_ObjFanin1(pObj), Aig_ObjFaninC1(pObj), vVisited, pData, vRoots );
    }
    else if ( Saig_ObjIsLo(p, pObj) )
    {
        pThis->Fi0Lev = 1;
        pThis->Fi1Lev = 0;
        Vec_PtrPush( vRoots, Saig_ObjLoToLi(p, pObj) );
    }
    else if ( Saig_ObjIsPi(p, pObj) )
    {
        pThis->Fi0Lev = 0;
        pThis->Fi1Lev = 0;
    }
    else
        assert( 0 );
    assert( pThis->nFoNeg + pThis->nFoPos );
    Vec_IntPush( vVisited, Aig_ObjId(pObj) );
}

//static abctime time_Trav = 0;

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

  Synopsis    [Collect statistics about one output.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Iso_StoCollectInfo( Iso_Sto_t * p, Aig_Obj_t * pPo )
{
    int fVerboseShow = 0;
    Vec_Int_t * vInfo;
    Iso_Dat2_t * pData2 = (Iso_Dat2_t *)p->pData;
    Aig_Man_t * pAig = p->pAig;
    Aig_Obj_t * pObj;
    int i, Value, Entry, * pPerm;
//    abctime clk = Abc_Clock();

    assert( Aig_ObjIsCo(pPo) );

    // collect initial POs
    Vec_IntClear( p->vVisited );
    Vec_PtrClear( p->vRoots );
    Vec_PtrPush( p->vRoots, pPo );

    // mark internal nodes
    Vec_PtrForEachEntry( Aig_Obj_t *, p->vRoots, pObj, i )
        if ( !Aig_ObjIsConst1(Aig_ObjFanin0(pObj)) )
            Iso_StoCollectInfo_rec( pAig, Aig_ObjFanin0(pObj), Aig_ObjFaninC0(pObj), p->vVisited, p->pData, p->vRoots );
//    time_Trav += Abc_Clock() - clk;

    // count how many times each data entry appears
    Vec_IntClear( p->vPlaces );
    Vec_IntForEachEntry( p->vVisited, Entry, i )
    {
        Value = pData2[Entry].Data;
//        assert( Value > 0 );
        if ( p->pCounters[Value]++ == 0 )
            Vec_IntPush( p->vPlaces, Value );
//        pData2[Entry].Data = 0;
        *((int *)(p->pData + Entry)) = 0;
    }

    // collect non-trivial counters
    Vec_IntClear( p->vVisited );
    Vec_IntForEachEntry( p->vPlaces, Entry, i )
    {
        assert( p->pCounters[Entry] );
        Vec_IntPush( p->vVisited, p->pCounters[Entry] );
        p->pCounters[Entry] = 0;
    }
//    printf( "%d ", Vec_IntSize(p->vVisited) );

    // sort the costs in the increasing order
    pPerm = Abc_MergeSortCost( Vec_IntArray(p->vVisited), Vec_IntSize(p->vVisited) );
    assert( Vec_IntEntry(p->vVisited, pPerm[0]) <= Vec_IntEntry(p->vVisited, pPerm[Vec_IntSize(p->vVisited)-1]) );

    // create information
    vInfo = Vec_IntAlloc( Vec_IntSize(p->vVisited) );
    for ( i = Vec_IntSize(p->vVisited)-1; i >= 0; i-- )
    {
        Entry = Vec_IntEntry( p->vVisited, pPerm[i] );
        Entry = (Entry << AIG_ISO_NUM) | Vec_IntEntry( p->vPlaces, pPerm[i] );
        Vec_IntPush( vInfo, Entry );
    }
    ABC_FREE( pPerm );

    // show the final result
    if ( fVerboseShow )
    Vec_IntForEachEntry( vInfo, Entry, i )
    {
        Iso_Dat2_t Data = { (unsigned)Entry & 0xFFFF };
        Iso_Dat_t * pData = (Iso_Dat_t *)&Data;

        printf( "%6d : ", i );
        printf( "Freq =%6d ", Entry >> 16 );

        printf( "FiNeg =%3d ", pData->nFiNeg );
        printf( "FoNeg =%3d ", pData->nFoNeg );
        printf( "FoPos =%3d ", pData->nFoPos );
        printf( "Fi0L  =%3d ", pData->Fi0Lev );
        printf( "Fi1L  =%3d ", pData->Fi1Lev );
        printf( "Lev   =%3d ", pData->Level  );
        printf( "\n" );
    }
    return vInfo;
}

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

  Synopsis    [Takes multi-output sequential AIG.]

  Description [Returns candidate equivalence classes of POs.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Iso_StoCompareVecInt( Vec_Int_t ** p1, Vec_Int_t ** p2 )
{
    return Vec_IntCompareVec( *p1, *p2 );
}

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

  Synopsis    [Takes multi-output sequential AIG.]

  Description [Returns candidate equivalence classes of POs.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Vec_t * Saig_IsoDetectFast( Aig_Man_t * pAig )
{
    Iso_Sto_t * pMan;
    Aig_Obj_t * pObj;
    Vec_Ptr_t * vClasses, * vInfos;
    Vec_Int_t * vInfo, * vPrev, * vLevel;
    int i, Number, nUnique = 0;
    abctime clk = Abc_Clock();

    // collect infos and remember their number
    pMan = Iso_StoStart( pAig );
    vInfos = Vec_PtrAlloc( Saig_ManPoNum(pAig) );
    Saig_ManForEachPo( pAig, pObj, i )
    {
        vInfo = Iso_StoCollectInfo(pMan, pObj);
        Vec_IntPush( vInfo, i );
        Vec_PtrPush( vInfos, vInfo );
    }
    Iso_StoStop( pMan );
    Abc_PrintTime( 1, "Info computation time", Abc_Clock() - clk );

    // sort the infos
    clk = Abc_Clock();
    Vec_PtrSort( vInfos, (int (*)(const void *, const void *))Iso_StoCompareVecInt );

    // create classes
    clk = Abc_Clock();
    vClasses = Vec_PtrAlloc( Saig_ManPoNum(pAig) );
    // start the first class
    Vec_PtrPush( vClasses, (vLevel = Vec_IntAlloc(4)) );
    vPrev = (Vec_Int_t *)Vec_PtrEntry( vInfos, 0 );
    Vec_IntPush( vLevel, Vec_IntPop(vPrev) );
    // consider other classes
    Vec_PtrForEachEntryStart( Vec_Int_t *, vInfos, vInfo, i, 1 )
    {
        Number = Vec_IntPop( vInfo );
        if ( Vec_IntCompareVec(vPrev, vInfo) )
            Vec_PtrPush( vClasses, Vec_IntAlloc(4) );
        vLevel = (Vec_Int_t *)Vec_PtrEntryLast( vClasses );
        Vec_IntPush( vLevel, Number );
        vPrev = vInfo;
    }
    Vec_VecFree( (Vec_Vec_t *)vInfos );
    Abc_PrintTime( 1, "Sorting time", Abc_Clock() - clk );
//    Abc_PrintTime( 1, "Traversal time", time_Trav );

    // report the results
//    Vec_VecPrintInt( (Vec_Vec_t *)vClasses );
    printf( "Divided %d outputs into %d cand equiv classes.\n", Saig_ManPoNum(pAig), Vec_PtrSize(vClasses) );

    Vec_PtrForEachEntry( Vec_Int_t *, vClasses, vLevel, i )
        if ( Vec_IntSize(vLevel) > 1 )
            printf( "%d ", Vec_IntSize(vLevel) );
        else
            nUnique++;
    printf( " Unique = %d\n", nUnique );

//    return (Vec_Vec_t *)vClasses;
    Vec_VecFree( (Vec_Vec_t *)vClasses );
    return NULL;
}



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


ABC_NAMESPACE_IMPL_END

