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

  FileName    [ivyFanout.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [And-Inverter Graph package.]

  Synopsis    [Representation of the fanouts.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "ivy.h"

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

// getting hold of the next fanout of the node
static inline Ivy_Obj_t * Ivy_ObjNextFanout( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    if ( pFanout == NULL )
        return NULL;
    if ( Ivy_ObjFanin0(pFanout) == pObj )
        return pFanout->pNextFan0;
    assert( Ivy_ObjFanin1(pFanout) == pObj );
    return pFanout->pNextFan1;
}

// getting hold of the previous fanout of the node
static inline Ivy_Obj_t * Ivy_ObjPrevFanout( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    if ( pFanout == NULL )
        return NULL;
    if ( Ivy_ObjFanin0(pFanout) == pObj )
        return pFanout->pPrevFan0;
    assert( Ivy_ObjFanin1(pFanout) == pObj );
    return pFanout->pPrevFan1;
}

// getting hold of the place where the next fanout will be attached
static inline Ivy_Obj_t ** Ivy_ObjNextFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    if ( Ivy_ObjFanin0(pFanout) == pObj )
        return &pFanout->pNextFan0;
    assert( Ivy_ObjFanin1(pFanout) == pObj );
    return &pFanout->pNextFan1;
}

// getting hold of the place where the next fanout will be attached
static inline Ivy_Obj_t ** Ivy_ObjPrevFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    if ( Ivy_ObjFanin0(pFanout) == pObj )
        return &pFanout->pPrevFan0;
    assert( Ivy_ObjFanin1(pFanout) == pObj );
    return &pFanout->pPrevFan1;
}

// getting hold of the place where the next fanout will be attached
static inline Ivy_Obj_t ** Ivy_ObjPrevNextFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    Ivy_Obj_t * pTemp;
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    pTemp = Ivy_ObjPrevFanout(pObj, pFanout);
    if ( pTemp == NULL )
        return &pObj->pFanout;
    if ( Ivy_ObjFanin0(pTemp) == pObj )
        return &pTemp->pNextFan0;
    assert( Ivy_ObjFanin1(pTemp) == pObj );
    return &pTemp->pNextFan1;
}

// getting hold of the place where the next fanout will be attached
static inline Ivy_Obj_t ** Ivy_ObjNextPrevFanoutPlace( Ivy_Obj_t * pObj, Ivy_Obj_t * pFanout )
{
    Ivy_Obj_t * pTemp;
    assert( !Ivy_IsComplement(pObj) );
    assert( !Ivy_IsComplement(pFanout) );
    pTemp = Ivy_ObjNextFanout(pObj, pFanout);
    if ( pTemp == NULL )
        return NULL;
    if ( Ivy_ObjFanin0(pTemp) == pObj )
        return &pTemp->pPrevFan0;
    assert( Ivy_ObjFanin1(pTemp) == pObj );
    return &pTemp->pPrevFan1;
}

// iterator through the fanouts of the node
#define Ivy_ObjForEachFanoutInt( pObj, pFanout )                 \
    for ( pFanout = (pObj)->pFanout; pFanout;                    \
          pFanout = Ivy_ObjNextFanout(pObj, pFanout) )

// safe iterator through the fanouts of the node
#define Ivy_ObjForEachFanoutIntSafe( pObj, pFanout, pFanout2 )   \
    for ( pFanout  = (pObj)->pFanout,                            \
          pFanout2 = Ivy_ObjNextFanout(pObj, pFanout);           \
          pFanout;                                               \
          pFanout  = pFanout2,                                   \
          pFanout2 = Ivy_ObjNextFanout(pObj, pFanout) )

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

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

  Synopsis    [Starts the fanout representation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManStartFanout( Ivy_Man_t * p )
{
    Ivy_Obj_t * pObj;
    int i;
    assert( !p->fFanout );
    p->fFanout = 1;
    Ivy_ManForEachObj( p, pObj, i )
    {
        if ( Ivy_ObjFanin0(pObj) )
            Ivy_ObjAddFanout( p, Ivy_ObjFanin0(pObj), pObj );
        if ( Ivy_ObjFanin1(pObj) )
            Ivy_ObjAddFanout( p, Ivy_ObjFanin1(pObj), pObj );
    }
}

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

  Synopsis    [Stops the fanout representation.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ManStopFanout( Ivy_Man_t * p )
{
    Ivy_Obj_t * pObj;
    int i;
    assert( p->fFanout );
    p->fFanout = 0;
    Ivy_ManForEachObj( p, pObj, i )
        pObj->pFanout = pObj->pNextFan0 = pObj->pNextFan1 = pObj->pPrevFan0 = pObj->pPrevFan1 = NULL;
}

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

  Synopsis    [Add the fanout.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ObjAddFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanout )
{
    assert( p->fFanout );
    if ( pFanin->pFanout )
    {
        *Ivy_ObjNextFanoutPlace(pFanin, pFanout) = pFanin->pFanout;
        *Ivy_ObjPrevFanoutPlace(pFanin, pFanin->pFanout) = pFanout;
    }
    pFanin->pFanout = pFanout;
}

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

  Synopsis    [Removes the fanout.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ObjDeleteFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanout )
{
    Ivy_Obj_t ** ppPlace1, ** ppPlace2, ** ppPlaceN;
    assert( pFanin->pFanout != NULL );

    ppPlace1 = Ivy_ObjNextFanoutPlace(pFanin, pFanout);
    ppPlaceN = Ivy_ObjPrevNextFanoutPlace(pFanin, pFanout);
    assert( *ppPlaceN == pFanout );
    if ( ppPlaceN )
        *ppPlaceN = *ppPlace1;

    ppPlace2 = Ivy_ObjPrevFanoutPlace(pFanin, pFanout);
    ppPlaceN = Ivy_ObjNextPrevFanoutPlace(pFanin, pFanout);
    assert( ppPlaceN == NULL || *ppPlaceN == pFanout );
    if ( ppPlaceN )
        *ppPlaceN = *ppPlace2;

    *ppPlace1 = NULL;
    *ppPlace2 = NULL;
}

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

  Synopsis    [Replaces the fanout of pOld to be pFanoutNew.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ivy_ObjPatchFanout( Ivy_Man_t * p, Ivy_Obj_t * pFanin, Ivy_Obj_t * pFanoutOld, Ivy_Obj_t * pFanoutNew )
{
    Ivy_Obj_t ** ppPlace;
    ppPlace = Ivy_ObjPrevNextFanoutPlace(pFanin, pFanoutOld);
    assert( *ppPlace == pFanoutOld );
    if ( ppPlace )
        *ppPlace = pFanoutNew;
    ppPlace = Ivy_ObjNextPrevFanoutPlace(pFanin, pFanoutOld);
    assert( ppPlace == NULL || *ppPlace == pFanoutOld );
    if ( ppPlace )
        *ppPlace = pFanoutNew;
    // assuming that pFanoutNew already points to the next fanout
}

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

  Synopsis    [Starts iteration through the fanouts.]

  Description [Copies the currently available fanouts into the array.]
               
  SideEffects [Can be used while the fanouts are being removed.]

  SeeAlso     []

***********************************************************************/
void Ivy_ObjCollectFanouts( Ivy_Man_t * p, Ivy_Obj_t * pObj, Vec_Ptr_t * vArray )
{
    Ivy_Obj_t * pFanout;
    assert( p->fFanout );
    assert( !Ivy_IsComplement(pObj) );
    Vec_PtrClear( vArray );
    Ivy_ObjForEachFanoutInt( pObj, pFanout )
        Vec_PtrPush( vArray, pFanout );
}

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

  Synopsis    [Reads one fanout.]

  Description [Returns fanout if there is only one fanout.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Ivy_Obj_t * Ivy_ObjReadFirstFanout( Ivy_Man_t * p, Ivy_Obj_t * pObj )
{
    return pObj->pFanout;
}

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

  Synopsis    [Reads one fanout.]

  Description [Returns fanout if there is only one fanout.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Ivy_ObjFanoutNum( Ivy_Man_t * p, Ivy_Obj_t * pObj )
{
    Ivy_Obj_t * pFanout;
    int Counter = 0;
    Ivy_ObjForEachFanoutInt( pObj, pFanout )
        Counter++;
    return Counter;
}

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


