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

  FileName    [sscClass.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT sweeping under constraints.]

  Synopsis    [Equivalence classes.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 29, 2008.]

  Revision    [$Id: sscClass.c,v 1.00 2008/07/29 00:00:00 alanmi Exp $]

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

#include "sscInt.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Computes hash key of the simuation info.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Ssc_GiaSimHashKey( Gia_Man_t * p, int iObj, int nTableSize )
{
    static int s_Primes[16] = { 
        1291, 1699, 1999, 2357, 2953, 3313, 3907, 4177, 
        4831, 5147, 5647, 6343, 6899, 7103, 7873, 8147 };
    word * pSim = Gia_ObjSim( p, iObj );
    unsigned uHash = 0;
    int i, nWords = Gia_ObjSimWords(p);
    if ( pSim[0] & 1 )
        for ( i = 0; i < nWords; i++ )
            uHash ^= ~pSim[i] * s_Primes[i & 0xf];
    else
        for ( i = 0; i < nWords; i++ )
            uHash ^= pSim[i] * s_Primes[i & 0xf];
    return (int)(uHash % nTableSize);
}

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

  Synopsis    [Returns 1 if sim patterns are equal up to the complement.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Ssc_GiaSimIsConst0( Gia_Man_t * p, int iObj0 )
{
    int w, nWords = Gia_ObjSimWords(p);
    word * pSim = Gia_ObjSim( p, iObj0 );
    if ( pSim[0] & 1 )
    {
        for ( w = 0; w < nWords; w++ )
            if ( ~pSim[w] )
                return 0;
    }
    else
    {
        for ( w = 0; w < nWords; w++ )
            if ( pSim[w] )
                return 0;
    }
    return 1;
}
static inline int Ssc_GiaSimAreEqual( Gia_Man_t * p, int iObj0, int iObj1 )
{
    int w, nWords = Gia_ObjSimWords(p);
    word * pSim0 = Gia_ObjSim( p, iObj0 );
    word * pSim1 = Gia_ObjSim( p, iObj1 );
    if ( (pSim0[0] & 1) != (pSim1[0] & 1) )
    {
        for ( w = 0; w < nWords; w++ )
            if ( pSim0[w] != ~pSim1[w] )
                return 0;
    }
    else
    {
        for ( w = 0; w < nWords; w++ )
            if ( pSim0[w] != pSim1[w] )
                return 0;
    }
    return 1;
}
static inline int Ssc_GiaSimAreEqualBit( Gia_Man_t * p, int iObj0, int iObj1 )
{
    Gia_Obj_t * pObj0 = Gia_ManObj( p, iObj0 );
    Gia_Obj_t * pObj1 = Gia_ManObj( p, iObj1 );
    return (pObj0->fPhase ^ pObj0->fMark0) == (pObj1->fPhase ^ pObj1->fMark0);
}

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

  Synopsis    [Refines one equivalence class.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ssc_GiaSimClassCreate( Gia_Man_t * p, Vec_Int_t * vClass )
{
    int Repr = GIA_VOID, EntPrev = -1, Ent, i;
    assert( Vec_IntSize(vClass) > 0 );
    Vec_IntForEachEntry( vClass, Ent, i )
    {
        if ( i == 0 )
        {
            Repr = Ent;
            Gia_ObjSetRepr( p, Ent, GIA_VOID );
            EntPrev = Ent;
        }
        else
        {
            assert( Repr < Ent );
            Gia_ObjSetRepr( p, Ent, Repr );
            Gia_ObjSetNext( p, EntPrev, Ent );
            EntPrev = Ent;
        }
    }
    Gia_ObjSetNext( p, EntPrev, 0 );
}

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

  Synopsis    [Refines one equivalence class using individual bit-pattern.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Ssc_GiaSimClassRefineOneBit( Gia_Man_t * p, int i )
{
    Gia_Obj_t * pObj;
    int Ent;
    assert( Gia_ObjIsHead( p, i ) );
    Vec_IntClear( p->vClassOld );
    Vec_IntClear( p->vClassNew );
    Vec_IntPush( p->vClassOld, i );
    pObj = Gia_ManObj(p, i);
    Gia_ClassForEachObj1( p, i, Ent )
    {
        if ( Ssc_GiaSimAreEqualBit( p, i, Ent ) )
            Vec_IntPush( p->vClassOld, Ent );
        else
            Vec_IntPush( p->vClassNew, Ent );
    }
    if ( Vec_IntSize( p->vClassNew ) == 0 )
        return 0;
    Ssc_GiaSimClassCreate( p, p->vClassOld );
    Ssc_GiaSimClassCreate( p, p->vClassNew );
    return 1;
}


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

  Synopsis    [Refines one class using simulation patterns.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Ssc_GiaSimClassRefineOne( Gia_Man_t * p, int i )
{
    Gia_Obj_t * pObj;
    int Ent;
    assert( Gia_ObjIsHead( p, i ) );
    Vec_IntClear( p->vClassOld );
    Vec_IntClear( p->vClassNew );
    Vec_IntPush( p->vClassOld, i );
    pObj = Gia_ManObj(p, i);
    Gia_ClassForEachObj1( p, i, Ent )
    {
        if ( Ssc_GiaSimAreEqual( p, i, Ent ) )
            Vec_IntPush( p->vClassOld, Ent );
        else
            Vec_IntPush( p->vClassNew, Ent );
    }
    if ( Vec_IntSize( p->vClassNew ) == 0 )
        return 0;
    Ssc_GiaSimClassCreate( p, p->vClassOld );
    Ssc_GiaSimClassCreate( p, p->vClassNew );
    if ( Vec_IntSize(p->vClassNew) > 1 )
        return 1 + Ssc_GiaSimClassRefineOne( p, Vec_IntEntry(p->vClassNew,0) );
    return 1;
}
void Ssc_GiaSimProcessRefined( Gia_Man_t * p, Vec_Int_t * vRefined )
{
    int * pTable, nTableSize, i, k, Key;
    if ( Vec_IntSize(vRefined) == 0 )
        return;
    nTableSize = Abc_PrimeCudd( 100 + Vec_IntSize(vRefined) / 3 );
    pTable = ABC_CALLOC( int, nTableSize );
    Vec_IntForEachEntry( vRefined, i, k )
    {
        assert( !Ssc_GiaSimIsConst0( p, i ) );
        Key = Ssc_GiaSimHashKey( p, i, nTableSize );
        if ( pTable[Key] == 0 )
        {
            assert( Gia_ObjRepr(p, i) == 0 );
            assert( Gia_ObjNext(p, i) == 0 );
            Gia_ObjSetRepr( p, i, GIA_VOID );
        }
        else
        {
            Gia_ObjSetNext( p, pTable[Key], i );
            Gia_ObjSetRepr( p, i, Gia_ObjRepr(p, pTable[Key]) );
            if ( Gia_ObjRepr(p, i) == GIA_VOID )
                Gia_ObjSetRepr( p, i, pTable[Key] );
            assert( Gia_ObjRepr(p, i) > 0 );
        }
        pTable[Key] = i;
    }
    Vec_IntForEachEntry( vRefined, i, k )
        if ( Gia_ObjIsHead( p, i ) )
            Ssc_GiaSimClassRefineOne( p, i );
    ABC_FREE( pTable );
}

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

  Synopsis    [Refines equivalence classes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ssc_GiaClassesInit( Gia_Man_t * p )
{
    Gia_Obj_t * pObj;
    int i;
    assert( p->pReprs == NULL );
    p->pReprs = ABC_CALLOC( Gia_Rpr_t, Gia_ManObjNum(p) );
    p->pNexts = ABC_CALLOC( int, Gia_ManObjNum(p) );
    Gia_ManForEachObj( p, pObj, i )
        Gia_ObjSetRepr( p, i, Gia_ObjIsCand(pObj) ? 0 : GIA_VOID );
    if ( p->vClassOld == NULL )
        p->vClassOld = Vec_IntAlloc( 100 );
    if ( p->vClassNew == NULL )
        p->vClassNew = Vec_IntAlloc( 100 );
}
int Ssc_GiaClassesRefine( Gia_Man_t * p )
{
    Vec_Int_t * vRefinedC;
    Gia_Obj_t * pObj;
    int i, Counter = 0;
    assert( p->pReprs != NULL );
    vRefinedC = Vec_IntAlloc( 100 );
    Gia_ManForEachCand( p, pObj, i )
        if ( Gia_ObjIsTail(p, i) )
            Counter += Ssc_GiaSimClassRefineOne( p, Gia_ObjRepr(p, i) );
        else if ( Gia_ObjIsConst(p, i) && !Ssc_GiaSimIsConst0(p, i) )
            Vec_IntPush( vRefinedC, i );
    Ssc_GiaSimProcessRefined( p, vRefinedC );
    Counter += Vec_IntSize( vRefinedC );
    Vec_IntFree( vRefinedC );
    return Counter;
}


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

  Synopsis    [Check if the pairs have been disproved.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ssc_GiaClassesCheckPairs( Gia_Man_t * p, Vec_Int_t * vDisPairs )
{
    int i, iRepr, iObj, Result = 1;
    Vec_IntForEachEntryDouble( vDisPairs, iRepr, iObj, i )
        if ( iRepr == Gia_ObjRepr(p, iObj) )
            printf( "Pair (%d, %d) are still equivalent.\n", iRepr, iObj ), Result = 0;
//    if ( Result )
//        printf( "Classes are refined correctly.\n" );
}


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


ABC_NAMESPACE_IMPL_END