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

  FileName    [ivyHaig.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [And-Inverter Graph package.]

  Synopsis    [HAIG management procedures.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - May 11, 2006.]

  Revision    [$Id: ivyHaig.c,v 1.00 2006/05/11 00:00:00 alanmi Exp $]

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

#include "ivy.h"

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

/* 
    HAIGing rules in working AIG:
    - Each node in the working AIG has a pointer to the corresponding node in HAIG 
      (this node is not necessarily the representative of the equivalence class of HAIG nodes)
    - This pointer is complemented if the AIG node and its corresponding HAIG node have different phase

    Choice node rules in HAIG:
    - Equivalent nodes are linked into a ring
    - Exactly one node in the ring has fanouts (this node is called the representative)
    - The pointer going from a node to the next node in the ring is complemented 
      if the first node is complemented, compared to the representative node of the equivalence class
    - (consequence of the above) The representative node always has non-complemented pointer to the next node
    - New nodes are inserted into the ring immediately after the representative node
*/

// returns the representative node of the given HAIG node
static inline Ivy_Obj_t * Ivy_HaigObjRepr( Ivy_Obj_t * pObj )
{
    Ivy_Obj_t * pTemp;
    assert( !Ivy_IsComplement(pObj) );
    // if the node has no equivalent node or has fanout, it is representative
    if ( pObj->pEquiv == NULL || Ivy_ObjRefs(pObj) > 0 )
        return pObj;
    // the node belongs to a class and is not a representative
    // complemented edge (pObj->pEquiv) tells if it is complemented w.r.t. the repr
    for ( pTemp = Ivy_Regular(pObj->pEquiv); pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) )
        if ( Ivy_ObjRefs(pTemp) > 0 )
            break;
    // return the representative node
    assert( Ivy_ObjRefs(pTemp) > 0 );
    return Ivy_NotCond( pTemp, Ivy_IsComplement(pObj->pEquiv) );
}

// counts the number of nodes in the equivalence class
static inline int Ivy_HaigObjCountClass( Ivy_Obj_t * pObj )
{
    Ivy_Obj_t * pTemp;
    int Counter;
    assert( !Ivy_IsComplement(pObj) );
    assert( Ivy_ObjRefs(pObj) > 0 );
    if ( pObj->pEquiv == NULL )
        return 1;
    assert( !Ivy_IsComplement(pObj->pEquiv) );
    Counter = 1;
    for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) )
        Counter++;
    return Counter;
}

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

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

  Synopsis    [Starts HAIG for the manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigStart( Ivy_Man_t * p, int fVerbose )
{
    Vec_Int_t * vLatches;
    Ivy_Obj_t * pObj;
    int i;
    assert( p->pHaig == NULL );
    p->pHaig = Ivy_ManDup( p );

    if ( fVerbose )
    {
        printf( "Starting : " );
        Ivy_ManPrintStats( p->pHaig );
    }

    // collect latches of design D and set their values to be DC
    vLatches = Vec_IntAlloc( 100 );
    Ivy_ManForEachLatch( p->pHaig, pObj, i )
    {
        pObj->Init = IVY_INIT_DC;
        Vec_IntPush( vLatches, pObj->Id );
    }
    p->pHaig->pData = vLatches;
/*
    {
        int x;
        Ivy_ManShow( p, 0, NULL );
        Ivy_ManShow( p->pHaig, 1, NULL );
        x = 0;
    }
*/
}

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

  Synopsis    [Transfers the HAIG to the newly created manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigTrasfer( Ivy_Man_t * p, Ivy_Man_t * pNew )
{
    Ivy_Obj_t * pObj;
    int i;
    assert( p->pHaig != NULL );
    Ivy_ManConst1(pNew)->pEquiv = Ivy_ManConst1(p)->pEquiv;
    Ivy_ManForEachPi( pNew, pObj, i )
        pObj->pEquiv = Ivy_ManPi( p, i )->pEquiv;
    pNew->pHaig = p->pHaig;
}

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

  Synopsis    [Stops HAIG for the manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigStop( Ivy_Man_t * p )
{
    Ivy_Obj_t * pObj;
    int i;
    assert( p->pHaig != NULL );
    Vec_IntFree( p->pHaig->pData );
    Ivy_ManStop( p->pHaig );
    p->pHaig = NULL;
    // remove dangling pointers to the HAIG objects
    Ivy_ManForEachObj( p, pObj, i )
        pObj->pEquiv = NULL;
}

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

  Synopsis    [Creates a new node in HAIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigCreateObj( Ivy_Man_t * p, Ivy_Obj_t * pObj )
{
    Ivy_Obj_t * pEquiv0, * pEquiv1;
    assert( p->pHaig != NULL );
    assert( !Ivy_IsComplement(pObj) );
    if ( Ivy_ObjType(pObj) == IVY_BUF )
        pObj->pEquiv = Ivy_ObjChild0Equiv(pObj);
    else if ( Ivy_ObjType(pObj) == IVY_LATCH )
    {
//        pObj->pEquiv = Ivy_Latch( p->pHaig, Ivy_ObjChild0Equiv(pObj), pObj->Init );
        pEquiv0 = Ivy_ObjChild0Equiv(pObj);
        pEquiv0 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv0)), Ivy_IsComplement(pEquiv0) );
        pObj->pEquiv = Ivy_Latch( p->pHaig, pEquiv0, pObj->Init );
    }
    else if ( Ivy_ObjType(pObj) == IVY_AND )
    {
//        pObj->pEquiv = Ivy_And( p->pHaig, Ivy_ObjChild0Equiv(pObj), Ivy_ObjChild1Equiv(pObj) );
        pEquiv0 = Ivy_ObjChild0Equiv(pObj);
        pEquiv0 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv0)), Ivy_IsComplement(pEquiv0) );
        pEquiv1 = Ivy_ObjChild1Equiv(pObj);
        pEquiv1 = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pEquiv1)), Ivy_IsComplement(pEquiv1) );
        pObj->pEquiv = Ivy_And( p->pHaig, pEquiv0, pEquiv1 );
    }
    else assert( 0 );
    // make sure the node points to the representative
//    pObj->pEquiv = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObj->pEquiv)), Ivy_IsComplement(pObj->pEquiv) );
}

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

  Synopsis    [Checks if the old node is in the TFI of the new node.]

  Description [] 
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Ivy_ObjIsInTfi_rec( Ivy_Obj_t * pObjNew, Ivy_Obj_t * pObjOld, int Levels )
{
    if ( pObjNew == pObjOld )
        return 1;
    if ( Levels == 0 || Ivy_ObjIsCi(pObjNew) || Ivy_ObjIsConst1(pObjNew) )
        return 0;
    if ( Ivy_ObjIsInTfi_rec( Ivy_ObjFanin0(pObjNew), pObjOld, Levels - 1 ) )
        return 1;
    if ( Ivy_ObjIsNode(pObjNew) && Ivy_ObjIsInTfi_rec( Ivy_ObjFanin1(pObjNew), pObjOld, Levels - 1 ) )
        return 1;
    return 0;
}

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

  Synopsis    [Sets the pair of equivalent nodes in HAIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigCreateChoice( Ivy_Man_t * p, Ivy_Obj_t * pObjOld, Ivy_Obj_t * pObjNew )
{
    Ivy_Obj_t * pObjOldHaig, * pObjNewHaig;
    Ivy_Obj_t * pObjOldHaigR, * pObjNewHaigR;
    int fCompl;
//printf( "\nCreating choice for %d and %d in AIG\n", pObjOld->Id, Ivy_Regular(pObjNew)->Id );

    assert( p->pHaig != NULL );
    assert( !Ivy_IsComplement(pObjOld) );
    // get pointers to the representatives of pObjOld and pObjNew
    pObjOldHaig = pObjOld->pEquiv;
    pObjNewHaig = Ivy_NotCond( Ivy_Regular(pObjNew)->pEquiv, Ivy_IsComplement(pObjNew) );
    // get the classes
    pObjOldHaig = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObjOldHaig)), Ivy_IsComplement(pObjOldHaig) );
    pObjNewHaig = Ivy_NotCond( Ivy_HaigObjRepr(Ivy_Regular(pObjNewHaig)), Ivy_IsComplement(pObjNewHaig) );
    // get regular pointers
    pObjOldHaigR = Ivy_Regular(pObjOldHaig);
    pObjNewHaigR = Ivy_Regular(pObjNewHaig);
    // check if there is phase difference between them
    fCompl = (Ivy_IsComplement(pObjOldHaig) != Ivy_IsComplement(pObjNewHaig));
    // if the class is the same, nothing to do
    if ( pObjOldHaigR == pObjNewHaigR )
        return;
    // if the second node belongs to a class, do not merge classes (for the time being)
    if ( Ivy_ObjRefs(pObjOldHaigR) == 0 || pObjNewHaigR->pEquiv != NULL || 
        Ivy_ObjRefs(pObjNewHaigR) > 0 ) //|| Ivy_ObjIsInTfi_rec(pObjNewHaigR, pObjOldHaigR, 10) )
    {
/*
        if ( pObjNewHaigR->pEquiv != NULL )
            printf( "c" );
        if ( Ivy_ObjRefs(pObjNewHaigR) > 0 )
            printf( "f" );
        printf( " " );
*/
        p->pHaig->nClassesSkip++;
        return;
    }

    // add this node to the class of pObjOldHaig
    assert( Ivy_ObjRefs(pObjOldHaigR) > 0 );
    assert( !Ivy_IsComplement(pObjOldHaigR->pEquiv) );
    if ( pObjOldHaigR->pEquiv == NULL )
        pObjNewHaigR->pEquiv = Ivy_NotCond( pObjOldHaigR, fCompl );
    else
        pObjNewHaigR->pEquiv = Ivy_NotCond( pObjOldHaigR->pEquiv, fCompl );
    pObjOldHaigR->pEquiv = pObjNewHaigR;
//printf( "Setting choice node %d -> %d.\n", pObjOldHaigR->Id, pObjNewHaigR->Id );
    // update the class of the new node
//    Ivy_Regular(pObjNew)->pEquiv = Ivy_NotCond( pObjOldHaigR, fCompl ^ Ivy_IsComplement(pObjNew) );
//printf( "Creating choice for %d and %d in HAIG\n", pObjOldHaigR->Id, pObjNewHaigR->Id );

//    if ( pObjOldHaigR->Id == 13 )
//    {
//        Ivy_ManShow( p, 0 );
//        Ivy_ManShow( p->pHaig, 1 );
//    }
//    if ( !Ivy_ManIsAcyclic( p->pHaig ) )
//        printf( "HAIG contains a cycle\n" );
}

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

  Synopsis    [Count the number of choices and choice nodes in HAIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Ivy_ManHaigCountChoices( Ivy_Man_t * p, int * pnChoices )
{
    Ivy_Obj_t * pObj;
    int nChoices, nChoiceNodes, Counter, i;
    assert( p->pHaig != NULL );
    nChoices = nChoiceNodes = 0;
    Ivy_ManForEachObj( p->pHaig, pObj, i )
    {
        if ( Ivy_ObjIsTerm(pObj) || i == 0 )
            continue;
        if ( Ivy_ObjRefs(pObj) == 0 )
            continue;
        Counter = Ivy_HaigObjCountClass( pObj );
        nChoiceNodes += (int)(Counter > 1);
        nChoices += Counter - 1;
//        if ( Counter > 1 )
//            printf( "Choice node %d %s\n", pObj->Id, Ivy_ObjIsLatch(pObj)? "(latch)": "" );
    }
    *pnChoices = nChoices;
    return nChoiceNodes;
} 

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

  Synopsis    [Prints statistics of the HAIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigPostprocess( Ivy_Man_t * p, int fVerbose )
{
    int nChoices, nChoiceNodes;

    assert( p->pHaig != NULL );

    if ( fVerbose )
    {
        printf( "Final    : " );
        Ivy_ManPrintStats( p );
        printf( "HAIG     : " );
        Ivy_ManPrintStats( p->pHaig );

        // print choice node stats
        nChoiceNodes = Ivy_ManHaigCountChoices( p, &nChoices );
        printf( "Total choice nodes = %d. Total choices = %d. Skipped classes = %d.\n", 
            nChoiceNodes, nChoices, p->pHaig->nClassesSkip ); 
    }

    if ( Ivy_ManIsAcyclic( p->pHaig ) )
    {
        if ( fVerbose )
            printf( "HAIG is acyclic\n" );
    }
    else
        printf( "HAIG contains a cycle\n" );

//    if ( fVerbose )
//        Ivy_ManHaigSimulate( p );
}


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

  Synopsis    [Applies the simulation rules.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Ivy_Init_t Ivy_ManHaigSimulateAnd( Ivy_Init_t In0, Ivy_Init_t In1 )
{
    assert( In0 != IVY_INIT_NONE && In1 != IVY_INIT_NONE );
    if ( In0 == IVY_INIT_DC || In1 == IVY_INIT_DC )
        return IVY_INIT_DC;
    if ( In0 == IVY_INIT_1 && In1 == IVY_INIT_1 )
        return IVY_INIT_1;
    return IVY_INIT_0;
}

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

  Synopsis    [Applies the simulation rules.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline Ivy_Init_t Ivy_ManHaigSimulateChoice( Ivy_Init_t In0, Ivy_Init_t In1 )
{
    assert( In0 != IVY_INIT_NONE && In1 != IVY_INIT_NONE );
    if ( (In0 == IVY_INIT_0 && In1 == IVY_INIT_1) || (In0 == IVY_INIT_1 && In1 == IVY_INIT_0) )
    {
         printf( "Compatibility fails.\n" );
         return IVY_INIT_0;
    }
    if ( In0 == IVY_INIT_DC && In1 == IVY_INIT_DC )
        return IVY_INIT_DC;
    if ( In0 != IVY_INIT_DC )
        return In0;
    return In1;
}

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

  Synopsis    [Simulate HAIG using modified 3-valued simulation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManHaigSimulate( Ivy_Man_t * p )
{
    Vec_Int_t * vNodes, * vLatches, * vLatchesD;
    Ivy_Obj_t * pObj, * pTemp;
    Ivy_Init_t In0, In1;
    int i, k, Counter;
    int fVerbose = 0;

    // check choices
    Ivy_ManCheckChoices( p );

    // switch to HAIG
    assert( p->pHaig != NULL );
    p = p->pHaig;

if ( fVerbose )
Ivy_ManForEachPi( p, pObj, i )
printf( "Setting PI %d\n", pObj->Id );

    // collect latches and nodes in the DFS order
    vNodes = Ivy_ManDfsSeq( p, &vLatches );

if ( fVerbose )
Ivy_ManForEachNodeVec( p, vNodes, pObj, i )
printf( "Collected node %d with fanins %d and %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id, Ivy_ObjFanin1(pObj)->Id );

    // set the PI values
    Ivy_ManConst1(p)->Init = IVY_INIT_1;
    Ivy_ManForEachPi( p, pObj, i )
        pObj->Init = IVY_INIT_0;

    // set the latch values
    Ivy_ManForEachNodeVec( p, vLatches, pObj, i )
        pObj->Init = IVY_INIT_DC;
    // set the latches of D to be determinate
    vLatchesD = p->pData;
    Ivy_ManForEachNodeVec( p, vLatchesD, pObj, i )
        pObj->Init = IVY_INIT_0;

    // perform several rounds of simulation
    for ( k = 0; k < 10; k++ )
    {
        // count the number of non-determinate values
        Counter = 0;
        Ivy_ManForEachNodeVec( p, vLatches, pObj, i )
            Counter += ( pObj->Init == IVY_INIT_DC );
        printf( "Iter %d : Non-determinate = %d\n", k, Counter );    
        
        // simulate the internal nodes
        Ivy_ManForEachNodeVec( p, vNodes, pObj, i )
        {
if ( fVerbose )
printf( "Processing node %d with fanins %d and %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id, Ivy_ObjFanin1(pObj)->Id );
            In0 = Ivy_InitNotCond( Ivy_ObjFanin0(pObj)->Init, Ivy_ObjFaninC0(pObj) );
            In1 = Ivy_InitNotCond( Ivy_ObjFanin1(pObj)->Init, Ivy_ObjFaninC1(pObj) );
            pObj->Init = Ivy_ManHaigSimulateAnd( In0, In1 );
            // simulate the equivalence class if the node is a representative
            if ( pObj->pEquiv && Ivy_ObjRefs(pObj) > 0 )
            {
if ( fVerbose )
printf( "Processing choice node %d\n", pObj->Id );
                In0 = pObj->Init;
                assert( !Ivy_IsComplement(pObj->pEquiv) );
                for ( pTemp = pObj->pEquiv; pTemp != pObj; pTemp = Ivy_Regular(pTemp->pEquiv) )
                {
if ( fVerbose )
printf( "Processing secondary node %d\n", pTemp->Id );
                    In1 = Ivy_InitNotCond( pTemp->Init, Ivy_IsComplement(pTemp->pEquiv) );
                    In0 = Ivy_ManHaigSimulateChoice( In0, In1 );
                }
                pObj->Init = In0;
            }
        }

        // simulate the latches
        Ivy_ManForEachNodeVec( p, vLatches, pObj, i )
        {
            pObj->Level = Ivy_ObjFanin0(pObj)->Init;
if ( fVerbose )
printf( "Using latch %d with fanin %d\n", pObj->Id, Ivy_ObjFanin0(pObj)->Id );
        }
        Ivy_ManForEachNodeVec( p, vLatches, pObj, i )
            pObj->Init = pObj->Level, pObj->Level = 0;
    }
    // ABC_FREE arrays
    Vec_IntFree( vNodes );
    Vec_IntFree( vLatches );
}

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