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

  FileName    [nwkFanio.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Logic network representation.]

  Synopsis    [Manipulation of fanins/fanouts.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: nwkFanio.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

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

#include "nwk.h"

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

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

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

  Synopsis    [Collects fanins of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjCollectFanins( Nwk_Obj_t * pNode, Vec_Ptr_t * vNodes )
{
    Nwk_Obj_t * pFanin;
    int i;
    Vec_PtrClear(vNodes);
    Nwk_ObjForEachFanin( pNode, pFanin, i )
        Vec_PtrPush( vNodes, pFanin );
}

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

  Synopsis    [Collects fanouts of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjCollectFanouts( Nwk_Obj_t * pNode, Vec_Ptr_t * vNodes )
{
    Nwk_Obj_t * pFanout;
    int i;
    Vec_PtrClear(vNodes);
    Nwk_ObjForEachFanout( pNode, pFanout, i )
        Vec_PtrPush( vNodes, pFanout );
}

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

  Synopsis    [Returns the number of the fanin of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Nwk_ObjFindFanin( Nwk_Obj_t * pObj, Nwk_Obj_t * pFanin )
{  
    Nwk_Obj_t * pTemp;
    int i;
    Nwk_ObjForEachFanin( pObj, pTemp, i )
        if ( pTemp == pFanin )
            return i;
    return -1;
}

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

  Synopsis    [Returns the number of the fanout of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Nwk_ObjFindFanout( Nwk_Obj_t * pObj, Nwk_Obj_t * pFanout )
{  
    Nwk_Obj_t * pTemp;
    int i;
    Nwk_ObjForEachFanout( pObj, pTemp, i )
        if ( pTemp == pFanout )
            return i;
    return -1;
}

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

  Synopsis    [Returns 1 if the node has to be reallocated.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Nwk_ObjReallocIsNeeded( Nwk_Obj_t * pObj )
{  
    return pObj->nFanins + pObj->nFanouts == pObj->nFanioAlloc;
}

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

  Synopsis    [Reallocates the object.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static Nwk_Obj_t * Nwk_ManReallocNode_old( Nwk_Obj_t * pObj )
{  
    Nwk_Obj_t * pObjNew, * pTemp;
    int i, iNum;
    assert( Nwk_ObjReallocIsNeeded(pObj) );
    pObjNew = (Nwk_Obj_t *)Aig_MmFlexEntryFetch( pObj->pMan->pMemObjs, sizeof(Nwk_Obj_t) + 2 * pObj->nFanioAlloc * sizeof(Nwk_Obj_t *) );
    memmove( pObjNew, pObj, sizeof(Nwk_Obj_t) + pObj->nFanioAlloc * sizeof(Nwk_Obj_t *) );
    pObjNew->nFanioAlloc = 2 * pObj->nFanioAlloc;
    // update the fanouts' fanins
    Nwk_ObjForEachFanout( pObj, pTemp, i )
    {
        iNum = Nwk_ObjFindFanin( pTemp, pObj );
        if ( iNum == -1 )
            printf( "Nwk_ManReallocNode(): Error! Fanin cannot be found.\n" );
        pTemp->pFanio[iNum] = pObjNew;
    }
    // update the fanins' fanouts
    Nwk_ObjForEachFanin( pObj, pTemp, i )
    {
        iNum = Nwk_ObjFindFanout( pTemp, pObj );
        if ( iNum == -1 )
            printf( "Nwk_ManReallocNode(): Error! Fanout cannot be found.\n" );
        pTemp->pFanio[pTemp->nFanins+iNum] = pObjNew;
    }
    memset( pObj, 0, sizeof(Nwk_Obj_t) + pObj->nFanioAlloc * sizeof(Nwk_Obj_t *) );
    assert( Nwk_ManObj(pObjNew->pMan, pObjNew->Id) == pObj );
    Vec_PtrWriteEntry( pObjNew->pMan->vObjs, pObjNew->Id, pObjNew );
    if ( Nwk_ObjIsCi(pObjNew) )
    {
        assert( Nwk_ManCi(pObjNew->pMan, pObjNew->PioId) == pObj );
        Vec_PtrWriteEntry( pObjNew->pMan->vCis, pObjNew->PioId, pObjNew );
    }
    if ( Nwk_ObjIsCo(pObjNew) )
    {
        assert( Nwk_ManCo(pObjNew->pMan, pObjNew->PioId) == pObj );
        Vec_PtrWriteEntry( pObjNew->pMan->vCos, pObjNew->PioId, pObjNew );
    }
    pObjNew->pMan->nRealloced++;
    return pObjNew;
}
 
/**Function*************************************************************

  Synopsis    [Reallocates the object.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static Nwk_Obj_t * Nwk_ManReallocNode( Nwk_Obj_t * pObj )
{  
    Nwk_Obj_t ** pFanioOld = pObj->pFanio;
    assert( Nwk_ObjReallocIsNeeded(pObj) );
    pObj->pFanio = (Nwk_Obj_t **)Aig_MmFlexEntryFetch( pObj->pMan->pMemObjs, 2 * pObj->nFanioAlloc * sizeof(Nwk_Obj_t *) );
    memmove( pObj->pFanio, pFanioOld, pObj->nFanioAlloc * sizeof(Nwk_Obj_t *) );
    pObj->nFanioAlloc *= 2;
    pObj->pMan->nRealloced++;
    return NULL;
}


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

  Synopsis    [Creates fanout/fanin relationship between the nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjAddFanin( Nwk_Obj_t * pObj, Nwk_Obj_t * pFanin )
{
    int i;
    assert( pObj->pMan == pFanin->pMan );
    assert( pObj->Id >= 0 && pFanin->Id >= 0 );
    if ( Nwk_ObjReallocIsNeeded(pObj) )
        Nwk_ManReallocNode( pObj );
    if ( Nwk_ObjReallocIsNeeded(pFanin) )
        Nwk_ManReallocNode( pFanin );
    for ( i = pObj->nFanins + pObj->nFanouts; i > (int)pObj->nFanins; i-- )
        pObj->pFanio[i] = pObj->pFanio[i-1];
    pObj->pFanio[pObj->nFanins++] = pFanin;
    pFanin->pFanio[pFanin->nFanins + pFanin->nFanouts++] = pObj;
    pObj->Level = AIG_MAX( pObj->Level, pFanin->Level + Nwk_ObjIsNode(pObj) );
}

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

  Synopsis    [Removes fanout/fanin relationship between the nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjDeleteFanin( Nwk_Obj_t * pObj, Nwk_Obj_t * pFanin )
{
    int i, k, Limit;
    // remove pFanin from the fanin list of pObj
    Limit = pObj->nFanins + pObj->nFanouts;
    for ( k = i = 0; i < Limit; i++ )
        if ( pObj->pFanio[i] != pFanin )
            pObj->pFanio[k++] = pObj->pFanio[i];
    assert( i == k + 1 );
    pObj->nFanins--;
    // remove pObj from the fanout list of pFanin
    Limit = pFanin->nFanins + pFanin->nFanouts;
    for ( k = i = pFanin->nFanins; i < Limit; i++ )
        if ( pFanin->pFanio[i] != pObj )
            pFanin->pFanio[k++] = pFanin->pFanio[i];
    assert( i == k + 1 );
    pFanin->nFanouts--;
}

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

  Synopsis    [Replaces a fanin of the node.]

  Description [The node is pObj. An old fanin of this node (pFaninOld) has to be
  replaced by a new fanin (pFaninNew). Assumes that the node and the old fanin 
  are not complemented. The new fanin can be complemented. In this case, the
  polarity of the new fanin will change, compared to the polarity of the old fanin.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjPatchFanin( Nwk_Obj_t * pObj, Nwk_Obj_t * pFaninOld, Nwk_Obj_t * pFaninNew )
{
    int i, k, iFanin, Limit;
    assert( pFaninOld != pFaninNew );
    assert( pObj != pFaninOld );
    assert( pObj != pFaninNew );
    assert( pObj->pMan == pFaninOld->pMan );
    assert( pObj->pMan == pFaninNew->pMan );
    // update the fanin
    iFanin = Nwk_ObjFindFanin( pObj, pFaninOld );
    if ( iFanin == -1 )
    {
        printf( "Nwk_ObjPatchFanin(); Error! Node %d is not among", pFaninOld->Id );
        printf( " the fanins of node %s...\n", pObj->Id );
        return;
    }
    pObj->pFanio[iFanin] = pFaninNew;
    // remove pObj from the fanout list of pFaninOld
    Limit = pFaninOld->nFanins + pFaninOld->nFanouts;
    for ( k = i = pFaninOld->nFanins; i < Limit; i++ )
        if ( pFaninOld->pFanio[i] != pObj )
            pFaninOld->pFanio[k++] = pFaninOld->pFanio[i];
    pFaninOld->nFanouts--;
    // add pObj to the fanout list of pFaninNew
    if ( Nwk_ObjReallocIsNeeded(pFaninNew) )
        Nwk_ManReallocNode( pFaninNew );
    pFaninNew->pFanio[pFaninNew->nFanins + pFaninNew->nFanouts++] = pObj;
}


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

  Synopsis    [Transfers fanout from the old node to the new node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjTransferFanout( Nwk_Obj_t * pNodeFrom, Nwk_Obj_t * pNodeTo )
{
    Vec_Ptr_t * vFanouts = pNodeFrom->pMan->vTemp;
    Nwk_Obj_t * pTemp;
    int nFanoutsOld, i;
    assert( !Nwk_ObjIsCo(pNodeFrom) && !Nwk_ObjIsCo(pNodeTo) );
    assert( pNodeFrom->pMan == pNodeTo->pMan );
    assert( pNodeFrom != pNodeTo );
    assert( Nwk_ObjFanoutNum(pNodeFrom) > 0 );
    // get the fanouts of the old node
    nFanoutsOld = Nwk_ObjFanoutNum(pNodeTo);
    Nwk_ObjCollectFanouts( pNodeFrom, vFanouts );
    // patch the fanin of each of them
    Vec_PtrForEachEntry( vFanouts, pTemp, i )
        Nwk_ObjPatchFanin( pTemp, pNodeFrom, pNodeTo );
    assert( Nwk_ObjFanoutNum(pNodeFrom) == 0 );
    assert( Nwk_ObjFanoutNum(pNodeTo) == nFanoutsOld + Vec_PtrSize(vFanouts) );
}

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

  Synopsis    [Replaces the node by a new node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Nwk_ObjReplace( Nwk_Obj_t * pNodeOld, Nwk_Obj_t * pNodeNew )
{
    assert( pNodeOld->pMan == pNodeNew->pMan );
    assert( pNodeOld != pNodeNew );
    assert( Nwk_ObjFanoutNum(pNodeOld) > 0 );
    // transfer the fanouts to the old node
    Nwk_ObjTransferFanout( pNodeOld, pNodeNew );
    // remove the old node
    Nwk_ManDeleteNode_rec( pNodeOld );
}


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


