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

  FileName    [giaEmbed.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Logic network derived from AIG.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include <math.h>
#include "gia.h"
#include "aig/ioa/ioa.h"

ABC_NAMESPACE_IMPL_START


/* 
    The code is based on the paper by D. Harel and Y. Koren, 
    "Graph drawing by high-dimensional embedding", 
    J. Graph Algs & Apps, Vol 8(2), pp. 195-217 (2004).
    http://www.emis.de/journals/JGAA/accepted/2004/HarelKoren2004.8.2.pdf 

    Iterative refinement is described in the paper: F. A. Aloul, I. L. Markov, and K. A. Sakallah.
    "FORCE: A Fast and Easy-To-Implement Variable-Ordering Heuristic", Proc. GLSVLSI’03.
    http://www.eecs.umich.edu/~imarkov/pubs/conf/glsvlsi03-force.pdf
*/

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

#define GIA_PLACE_SIZE 0x7fff 
// objects will be placed in box [0, GIA_PLACE_SIZE] x [0, GIA_PLACE_SIZE]

typedef float  Emb_Dat_t;

typedef struct Emb_Obj_t_ Emb_Obj_t;
struct Emb_Obj_t_
{
    unsigned       fCi      :  1;    // terminal node CI
    unsigned       fCo      :  1;    // terminal node CO
    unsigned       fMark0   :  1;    // first user-controlled mark
    unsigned       fMark1   :  1;    // second user-controlled mark
    unsigned       nFanins  : 28;    // the number of fanins
    unsigned       nFanouts;         // the number of fanouts
    int            hHandle;          // the handle of the node
    union {
    unsigned       TravId;           // user-specified value
    unsigned       iFanin; 
    };
    union {
    unsigned       Value;            // user-specified value
    unsigned       iFanout; 
    };
    int            Fanios[0];        // the array of fanins/fanouts
};

typedef struct Emb_Man_t_ Emb_Man_t;
struct Emb_Man_t_
{
    Gia_Man_t *    pGia;             // the original AIG manager
    Vec_Int_t *    vCis;             // the vector of CIs (PIs + LOs)
    Vec_Int_t *    vCos;             // the vector of COs (POs + LIs)
    int            nObjs;            // the number of objects
    int            nRegs;            // the number of registers
    int            nTravIds;         // traversal ID of the network
    int *          pObjData;         // the array containing data for objects
    int            nObjData;         // the size of array to store the logic network
    int            fVerbose;         // verbose output flag
    Emb_Dat_t *    pVecs;            // array of vectors of size nObjs * nDims
    int            nReached;         // the number of nodes reachable from the pivot
    int            nDistMax;         // the maximum distance from the node
    float **       pMatr;            // covariance matrix nDims * nDims
    float **       pEigen;           // the first several eigen values of the matrix
    float *        pSols;            // solutions to the problem nObjs * nSols;
    unsigned short*pPlacement;       // (x,y) coordinates for each cell
};

static inline int         Emb_ManRegNum( Emb_Man_t * p )                              { return p->nRegs;                                    }
static inline int         Emb_ManCiNum( Emb_Man_t * p )                               { return Vec_IntSize(p->vCis);                        }
static inline int         Emb_ManCoNum( Emb_Man_t * p )                               { return Vec_IntSize(p->vCos);                        }
static inline int         Emb_ManPiNum( Emb_Man_t * p )                               { return Vec_IntSize(p->vCis) - p->nRegs;             }
static inline int         Emb_ManPoNum( Emb_Man_t * p )                               { return Vec_IntSize(p->vCos) - p->nRegs;             }
static inline int         Emb_ManObjNum( Emb_Man_t * p )                              { return p->nObjs;                                    } 
static inline int         Emb_ManNodeNum( Emb_Man_t * p )                             { return p->nObjs - Vec_IntSize(p->vCis) - Vec_IntSize(p->vCos); } 

static inline Emb_Obj_t * Emb_ManObj( Emb_Man_t * p, unsigned hHandle )               { return (Emb_Obj_t *)(p->pObjData + hHandle);        } 
static inline Emb_Obj_t * Emb_ManCi( Emb_Man_t * p, int i )                           { return Emb_ManObj( p, Vec_IntEntry(p->vCis,i) );    }
static inline Emb_Obj_t * Emb_ManCo( Emb_Man_t * p, int i )                           { return Emb_ManObj( p, Vec_IntEntry(p->vCos,i) );    }

static inline int         Emb_ObjIsTerm( Emb_Obj_t * pObj )                           { return pObj->fCi || pObj->fCo;                      } 
static inline int         Emb_ObjIsCi( Emb_Obj_t * pObj )                             { return pObj->fCi;                                   } 
static inline int         Emb_ObjIsCo( Emb_Obj_t * pObj )                             { return pObj->fCo;                                   } 
//static inline int         Emb_ObjIsPi( Emb_Obj_t * pObj )                             { return pObj->fCi && pObj->nFanins == 0;             } 
//static inline int         Emb_ObjIsPo( Emb_Obj_t * pObj )                             { return pObj->fCo && pObj->nFanouts == 0;            } 
static inline int         Emb_ObjIsNode( Emb_Obj_t * pObj )                           { return!Emb_ObjIsTerm(pObj) && pObj->nFanins > 0;    } 
//static inline int         Emb_ObjIsConst0( Emb_Obj_t * pObj )                         { return!Emb_ObjIsTerm(pObj) && pObj->nFanins == 0;   } 

static inline int         Emb_ObjSize( Emb_Obj_t * pObj )                             { return sizeof(Emb_Obj_t) / 4 + pObj->nFanins + pObj->nFanouts;  } 
static inline int         Emb_ObjFaninNum( Emb_Obj_t * pObj )                         { return pObj->nFanins;                               } 
static inline int         Emb_ObjFanoutNum( Emb_Obj_t * pObj )                        { return pObj->nFanouts;                              } 
static inline Emb_Obj_t * Emb_ObjFanin( Emb_Obj_t * pObj, int i )                     { return (Emb_Obj_t *)(((int *)pObj) - pObj->Fanios[i]);               } 
static inline Emb_Obj_t * Emb_ObjFanout( Emb_Obj_t * pObj, int i )                    { return (Emb_Obj_t *)(((int *)pObj) + pObj->Fanios[pObj->nFanins+i]); } 

static inline void        Emb_ManResetTravId( Emb_Man_t * p )                         { extern void Emb_ManCleanTravId( Emb_Man_t * p ); Emb_ManCleanTravId( p ); p->nTravIds = 1;  }
static inline void        Emb_ManIncrementTravId( Emb_Man_t * p )                     { p->nTravIds++;                                      }
static inline void        Emb_ObjSetTravId( Emb_Obj_t * pObj, int TravId )            { pObj->TravId = TravId;                              }
static inline void        Emb_ObjSetTravIdCurrent( Emb_Man_t * p, Emb_Obj_t * pObj )  { pObj->TravId = p->nTravIds;                         }
static inline void        Emb_ObjSetTravIdPrevious( Emb_Man_t * p, Emb_Obj_t * pObj ) { pObj->TravId = p->nTravIds - 1;                     }
static inline int         Emb_ObjIsTravIdCurrent( Emb_Man_t * p, Emb_Obj_t * pObj )   { return ((int)pObj->TravId == p->nTravIds);          }
static inline int         Emb_ObjIsTravIdPrevious( Emb_Man_t * p, Emb_Obj_t * pObj )  { return ((int)pObj->TravId == p->nTravIds - 1);      }

static inline Emb_Dat_t * Emb_ManVec( Emb_Man_t * p, int v )                          { return p->pVecs + v * p->nObjs;                     }
static inline float *     Emb_ManSol( Emb_Man_t * p, int v )                          { return p->pSols + v * p->nObjs;                     }

#define Emb_ManForEachObj( p, pObj, i )               \
    for ( i = 0; (i < p->nObjData) && (pObj = Emb_ManObj(p,i)); i += Emb_ObjSize(pObj) )
#define Emb_ManForEachNode( p, pObj, i )              \
    for ( i = 0; (i < p->nObjData) && (pObj = Emb_ManObj(p,i)); i += Emb_ObjSize(pObj) ) if ( Emb_ObjIsTerm(pObj) ) {} else
#define Emb_ManForEachObjVec( vVec, p, pObj, i )                        \
    for ( i = 0; (i < Vec_IntSize(vVec)) && ((pObj) = Emb_ManObj(p, Vec_IntEntry(vVec,i))); i++ )
#define Emb_ObjForEachFanin( pObj, pNext, i )         \
    for ( i = 0; (i < (int)pObj->nFanins) && (pNext = Emb_ObjFanin(pObj,i)); i++ )
#define Emb_ObjForEachFanout( pObj, pNext, i )        \
    for ( i = 0; (i < (int)pObj->nFanouts) && (pNext = Emb_ObjFanout(pObj,i)); i++ )

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

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

  Synopsis    [Creates fanin/fanout pair.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ObjAddFanin( Emb_Obj_t * pObj, Emb_Obj_t * pFanin )
{ 
    assert( pObj->iFanin < pObj->nFanins );
    assert( pFanin->iFanout < pFanin->nFanouts );
    pFanin->Fanios[pFanin->nFanins + pFanin->iFanout++] = 
    pObj->Fanios[pObj->iFanin++] = pObj->hHandle - pFanin->hHandle;
}

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

  Synopsis    [Creates logic network isomorphic to the given AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Emb_Man_t * Emb_ManStartSimple( Gia_Man_t * pGia )
{
    Emb_Man_t * p;
    Emb_Obj_t * pObjLog, * pFanLog;
    Gia_Obj_t * pObj, * pObjRi, * pObjRo;
    int i, nNodes, hHandle = 0;
    // prepare the AIG
    Gia_ManCreateRefs( pGia );
    // create logic network
    p = ABC_CALLOC( Emb_Man_t, 1 );
    p->pGia  = pGia;
    p->nRegs = Gia_ManRegNum(pGia);
    p->vCis  = Vec_IntAlloc( Gia_ManCiNum(pGia) );
    p->vCos  = Vec_IntAlloc( Gia_ManCoNum(pGia) );
    p->nObjData = (sizeof(Emb_Obj_t) / 4) * Gia_ManObjNum(pGia) + 2 * (2 * Gia_ManAndNum(pGia) + Gia_ManCoNum(pGia) + Gia_ManRegNum(pGia) + Gia_ManCoNum(pGia));
    p->pObjData = ABC_CALLOC( int, p->nObjData );
    // create constant node
    Gia_ManConst0(pGia)->Value = hHandle;
    pObjLog = Emb_ManObj( p, hHandle );
    pObjLog->hHandle  = hHandle;
    pObjLog->nFanins  = Gia_ManCoNum(pGia);  //0;
    pObjLog->nFanouts = Gia_ObjRefs( pGia, Gia_ManConst0(pGia) );
    // count objects
    hHandle += Emb_ObjSize( pObjLog );
    nNodes = 1;
    p->nObjs++;
    // create the PIs
    Gia_ManForEachCi( pGia, pObj, i )
    {
        // create PI object
        pObj->Value = hHandle;
        Vec_IntPush( p->vCis, hHandle );
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = Gia_ObjIsRo( pGia, pObj );
        pObjLog->nFanouts = Gia_ObjRefs( pGia, pObj );
        pObjLog->fCi = 1;
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        p->nObjs++;
    }
    // create internal nodes
    Gia_ManForEachAnd( pGia, pObj, i )
    {
        assert( Gia_ObjRefs( pGia, pObj ) > 0 );
        // create node object
        pObj->Value = hHandle;
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = 2;
        pObjLog->nFanouts = Gia_ObjRefs( pGia, pObj );
        // add fanins
        pFanLog = Emb_ManObj( p, Gia_ObjValue(Gia_ObjFanin0(pObj)) ); 
        Emb_ObjAddFanin( pObjLog, pFanLog );
        pFanLog = Emb_ManObj( p, Gia_ObjValue(Gia_ObjFanin1(pObj)) ); 
        Emb_ObjAddFanin( pObjLog, pFanLog );
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        nNodes++;
        p->nObjs++;
    }
    // create the POs
    Gia_ManForEachCo( pGia, pObj, i )
    {
        // create PO object
        pObj->Value = hHandle;
        Vec_IntPush( p->vCos, hHandle );
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = 1;
        pObjLog->nFanouts = 1 + Gia_ObjIsRi( pGia, pObj );
        pObjLog->fCo = 1;
        // add fanins
        pFanLog = Emb_ManObj( p, Gia_ObjValue(Gia_ObjFanin0(pObj)) );
        Emb_ObjAddFanin( pObjLog, pFanLog );
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        p->nObjs++;
    }
    // connect registers
    Gia_ManForEachRiRo( pGia, pObjRi, pObjRo, i )
        Emb_ObjAddFanin( Emb_ManObj(p,Gia_ObjValue(pObjRo)), Emb_ManObj(p,Gia_ObjValue(pObjRi)) );
    assert( nNodes  == Emb_ManNodeNum(p) );
    assert( hHandle == p->nObjData );
    assert( p->nObjs == Gia_ManObjNum(pGia) );
    if ( hHandle != p->nObjData )
        printf( "Emb_ManStartSimple(): Fatal error in internal representation.\n" );
    // make sure the fanin/fanout counters are correct
    Gia_ManForEachObj( pGia, pObj, i )
    {
        if ( !~Gia_ObjValue(pObj) )
            continue;
        pObjLog = Emb_ManObj( p, Gia_ObjValue(pObj) );
        assert( pObjLog->nFanins  == pObjLog->iFanin || Gia_ObjIsConst0(pObj) );
        assert( pObjLog->nFanouts == pObjLog->iFanout || Gia_ObjIsCo(pObj) );
        pObjLog->iFanin = pObjLog->iFanout = 0;
    }
    ABC_FREE( pGia->pRefs );
    return p;
}

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

  Synopsis    [Collect the fanin IDs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManCollectSuper_rec( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vSuper, Vec_Int_t * vVisit )  
{
    if ( pObj->fMark1 )
        return;
    pObj->fMark1 = 1;
    Vec_IntPush( vVisit, Gia_ObjId(p, pObj) );
    if ( pObj->fMark0 )
    {
        Vec_IntPush( vSuper, Gia_ObjId(p, pObj) );
        return;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Emb_ManCollectSuper_rec( p, Gia_ObjFanin0(pObj), vSuper, vVisit );
    Emb_ManCollectSuper_rec( p, Gia_ObjFanin1(pObj), vSuper, vVisit );
    
}

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

  Synopsis    [Collect the fanin IDs.]

  Description []
               
  SideEffects []

  SeeAlso     [] 

***********************************************************************/
void Emb_ManCollectSuper( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Int_t * vSuper, Vec_Int_t * vVisit )  
{
    int Entry, i;
    Vec_IntClear( vSuper );
    Vec_IntClear( vVisit );
    assert( pObj->fMark0 == 1 );
    pObj->fMark0 = 0;
    Emb_ManCollectSuper_rec( p, pObj, vSuper, vVisit );
    pObj->fMark0 = 1;
    Vec_IntForEachEntry( vVisit, Entry, i )
        Gia_ManObj(p, Entry)->fMark1 = 0;
}

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

  Synopsis    [Assigns references while removing the MUX/XOR ones.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManCreateRefsSpecial( Gia_Man_t * p )  
{
    Gia_Obj_t * pObj, * pFan0, * pFan1;
    Gia_Obj_t * pObjC, * pObjD0, * pObjD1;
    int i;
    assert( p->pRefs == NULL );
    Gia_ManCleanMark0( p );
    Gia_ManCreateRefs( p );
    Gia_ManForEachAnd( p, pObj, i )
    {
        assert( pObj->fMark0 == 0 );
        pFan0 = Gia_ObjFanin0(pObj);
        pFan1 = Gia_ObjFanin1(pObj);
        // skip nodes whose fanins are PIs or are already marked
        if ( Gia_ObjIsCi(pFan0) || pFan0->fMark0 || 
             Gia_ObjIsCi(pFan1) || pFan1->fMark0 )
             continue;
        // skip nodes that are not MUX type
        if ( !Gia_ObjIsMuxType(pObj) )
            continue;
        // the node is MUX type, mark it and its fanins
        pObj->fMark0  = 1;
        pFan0->fMark0 = 1;
        pFan1->fMark0 = 1;
        // deref the control 
        pObjC = Gia_ObjRecognizeMux( pObj, &pObjD1, &pObjD0 );
        Gia_ObjRefDec( p, Gia_Regular(pObjC) );
        if ( Gia_Regular(pObjD0) == Gia_Regular(pObjD1) )
            Gia_ObjRefDec( p, Gia_Regular(pObjD0) );
    }
    Gia_ManForEachAnd( p, pObj, i )
        assert( Gia_ObjRefs(p, pObj) > 0 );
    Gia_ManCleanMark0( p );
}

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

  Synopsis    [Assigns references while removing the MUX/XOR ones.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManTransformRefs( Gia_Man_t * p, int * pnObjs, int * pnFanios )  
{
    Vec_Int_t * vSuper, * vVisit;
    Gia_Obj_t * pObj, * pFanin;
    int i, k, Counter;
    assert( p->pRefs != NULL );

    // mark nodes to be used in the logic network
    Gia_ManCleanMark0( p );
    Gia_ManConst0(p)->fMark0 = 1;
    // mark the inputs
    Gia_ManForEachCi( p, pObj, i )
        pObj->fMark0 = 1;
    // mark those nodes that have ref count more than 1
    Gia_ManForEachAnd( p, pObj, i )
        pObj->fMark0 = (Gia_ObjRefs(p, pObj) > 1);
    // mark the output drivers
    Gia_ManForEachCoDriver( p, pObj, i )
        pObj->fMark0 = 1;

    // count the number of nodes
    Counter = 0;
    Gia_ManForEachObj( p, pObj, i )
        Counter += pObj->fMark0;
    *pnObjs = Counter + Gia_ManCoNum(p);

    // reset the references
    ABC_FREE( p->pRefs );
    p->pRefs = ABC_CALLOC( int, Gia_ManObjNum(p) );
    // reference from internal nodes
    Counter = 0;
    vSuper = Vec_IntAlloc( 100 );
    vVisit = Vec_IntAlloc( 100 );
    Gia_ManCleanMark1( p );
    Gia_ManForEachAnd( p, pObj, i )
    {
        if ( pObj->fMark0 == 0 )
            continue;
        Emb_ManCollectSuper( p, pObj, vSuper, vVisit );
        Gia_ManForEachObjVec( vSuper, p, pFanin, k )
        {
            assert( pFanin->fMark0 );
            Gia_ObjRefInc( p, pFanin );
        }
        Counter += Vec_IntSize( vSuper );
    }
    Gia_ManCheckMark1( p );
    Vec_IntFree( vSuper );
    Vec_IntFree( vVisit );
    // reference from outputs
    Gia_ManForEachCoDriver( p, pObj, i )
    {
        assert( pObj->fMark0 );
        Gia_ObjRefInc( p, pObj );
    }
    *pnFanios = Counter + Gia_ManCoNum(p);
}

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

  Synopsis    [Cleans the value.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManCleanTravId( Emb_Man_t * p )  
{
    Emb_Obj_t * pObj;
    int i;
    Emb_ManForEachObj( p, pObj, i )
        pObj->TravId = 0;
}

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

  Synopsis    [Cleans the value.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManSetValue( Emb_Man_t * p )  
{
    Emb_Obj_t * pObj;
    int i, Counter = 0;
    Emb_ManForEachObj( p, pObj, i )
    {
        pObj->Value = Counter++;
//        if ( pObj->fCi && pObj->nFanins == 0 ) 
//            printf( "CI:  Handle = %8d.  Value = %6d. Fanins = %d.\n", pObj->hHandle, pObj->Value, pObj->nFanins );
    }
}

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

  Synopsis    [Creates logic network isomorphic to the given AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Emb_Man_t * Emb_ManStart( Gia_Man_t * pGia )
{
    Emb_Man_t * p;
    Emb_Obj_t * pObjLog, * pFanLog;
    Gia_Obj_t * pObj, * pObjRi, * pObjRo, * pFanin;
    Vec_Int_t * vSuper, * vVisit;
    int nObjs, nFanios, nNodes = 0;
    int i, k, hHandle = 0;
    // prepare the AIG
//    Gia_ManCreateRefs( pGia );
    Emb_ManCreateRefsSpecial( pGia );
    Emb_ManTransformRefs( pGia, &nObjs, &nFanios );
    Gia_ManFillValue( pGia );
    // create logic network
    p = ABC_CALLOC( Emb_Man_t, 1 );
    p->pGia  = pGia;
    p->nRegs = Gia_ManRegNum(pGia);
    p->vCis  = Vec_IntAlloc( Gia_ManCiNum(pGia) );
    p->vCos  = Vec_IntAlloc( Gia_ManCoNum(pGia) );
    p->nObjData = (sizeof(Emb_Obj_t) / 4) * nObjs + 2 * (nFanios + Gia_ManRegNum(pGia) + Gia_ManCoNum(pGia));
    p->pObjData = ABC_CALLOC( int, p->nObjData );
    // create constant node
    Gia_ManConst0(pGia)->Value = hHandle;
    pObjLog = Emb_ManObj( p, hHandle );
    pObjLog->hHandle  = hHandle;
    pObjLog->nFanins  = Gia_ManCoNum(pGia);  //0;
    pObjLog->nFanouts = Gia_ObjRefs( pGia, Gia_ManConst0(pGia) );
    // count objects
    hHandle += Emb_ObjSize( pObjLog );
    nNodes++;
    p->nObjs++;
    // create the PIs
    Gia_ManForEachCi( pGia, pObj, i )
    {
        // create PI object
        pObj->Value = hHandle;
        Vec_IntPush( p->vCis, hHandle );
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = Gia_ObjIsRo( pGia, pObj );
        pObjLog->nFanouts = Gia_ObjRefs( pGia, pObj );
        pObjLog->fCi = 1;
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        p->nObjs++;
    }
    // create internal nodes
    vSuper = Vec_IntAlloc( 100 );
    vVisit = Vec_IntAlloc( 100 );
    Gia_ManForEachAnd( pGia, pObj, i )
    {
        if ( pObj->fMark0 == 0 )
        {
            assert( Gia_ObjRefs( pGia, pObj ) == 0 );
            continue;
        }
        assert( Gia_ObjRefs( pGia, pObj ) > 0 );
        Emb_ManCollectSuper( pGia, pObj, vSuper, vVisit );
        // create node object
        pObj->Value = hHandle;
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = Vec_IntSize( vSuper );
        pObjLog->nFanouts = Gia_ObjRefs( pGia, pObj );
        // add fanins
        Gia_ManForEachObjVec( vSuper, pGia, pFanin, k )
        {
            pFanLog = Emb_ManObj( p, Gia_ObjValue(pFanin) ); 
            Emb_ObjAddFanin( pObjLog, pFanLog );
        }
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        nNodes++;
        p->nObjs++;
    }
    Vec_IntFree( vSuper );
    Vec_IntFree( vVisit );
    // create the POs
    Gia_ManForEachCo( pGia, pObj, i )
    {
        // create PO object
        pObj->Value = hHandle;
        Vec_IntPush( p->vCos, hHandle );
        pObjLog = Emb_ManObj( p, hHandle );
        pObjLog->hHandle  = hHandle;
        pObjLog->nFanins  = 1;
        pObjLog->nFanouts = 1 + Gia_ObjIsRi( pGia, pObj );
        pObjLog->fCo = 1;
        // add fanins
        pFanLog = Emb_ManObj( p, Gia_ObjValue(Gia_ObjFanin0(pObj)) );
        Emb_ObjAddFanin( pObjLog, pFanLog );
        // count objects
        hHandle += Emb_ObjSize( pObjLog );
        p->nObjs++;
    }
    // connect registers
    Gia_ManForEachRiRo( pGia, pObjRi, pObjRo, i )
        Emb_ObjAddFanin( Emb_ManObj(p,Gia_ObjValue(pObjRo)), Emb_ManObj(p,Gia_ObjValue(pObjRi)) );
    Gia_ManCleanMark0( pGia );
    assert( nNodes  == Emb_ManNodeNum(p) );
    assert( nObjs   == p->nObjs );
    assert( hHandle == p->nObjData );
    if ( hHandle != p->nObjData )
        printf( "Emb_ManStart(): Fatal error in internal representation.\n" );
    // make sure the fanin/fanout counters are correct
    Gia_ManForEachObj( pGia, pObj, i )
    {
        if ( !~Gia_ObjValue(pObj) )
            continue;
        pObjLog = Emb_ManObj( p, Gia_ObjValue(pObj) );
        assert( pObjLog->nFanins  == pObjLog->iFanin || Gia_ObjIsConst0(pObj) );
        assert( pObjLog->nFanouts == pObjLog->iFanout || Gia_ObjIsCo(pObj) );
        pObjLog->iFanin = pObjLog->iFanout = 0;
    }
    ABC_FREE( pGia->pRefs );
    return p;
}

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

  Synopsis    [Creates logic network isomorphic to the given AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManPrintStats( Emb_Man_t * p )
{
//    if ( p->pName )
//        printf( "%8s : ", p->pName );
    printf( "i/o =%7d/%7d  ", Emb_ManPiNum(p), Emb_ManPoNum(p) );
    if ( Emb_ManRegNum(p) )
        printf( "ff =%7d  ", Emb_ManRegNum(p) );
    printf( "node =%8d  ", Emb_ManNodeNum(p) );
    printf( "obj =%8d  ", Emb_ManObjNum(p) );
//    printf( "lev =%5d  ", Emb_ManLevelNum(p) );
//    printf( "cut =%5d  ", Emb_ManCrossCut(p) );
    printf( "mem =%5.2f MB", 4.0*p->nObjData/(1<<20) );
//    printf( "obj =%5d  ", Emb_ManObjNum(p) );
    printf( "\n" );

//    Emb_ManSatExperiment( p );
}

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

  Synopsis    [Creates logic network isomorphic to the given AIG.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManStop( Emb_Man_t * p )
{
    Vec_IntFree( p->vCis );
    Vec_IntFree( p->vCos );
    ABC_FREE( p->pPlacement );
    ABC_FREE( p->pVecs );
    ABC_FREE( p->pSols );
    ABC_FREE( p->pMatr );
    ABC_FREE( p->pEigen );
    ABC_FREE( p->pObjData );
    ABC_FREE( p );
}

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

  Synopsis    [Prints the distribution of fanins/fanouts in the network.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManPrintFanio( Emb_Man_t * p )
{
    char Buffer[100];
    Emb_Obj_t * pNode;
    Vec_Int_t * vFanins, * vFanouts;
    int nFanins, nFanouts, nFaninsMax, nFanoutsMax, nFaninsAll, nFanoutsAll;
    int i, k, nSizeMax;

    // determine the largest fanin and fanout
    nFaninsMax = nFanoutsMax = 0;
    nFaninsAll = nFanoutsAll = 0;
    Emb_ManForEachNode( p, pNode, i )
    {
        if ( i == 0 ) continue; // skip const 0 obj
        nFanins  = Emb_ObjFaninNum(pNode);
        nFanouts = Emb_ObjFanoutNum(pNode);
        nFaninsAll  += nFanins;
        nFanoutsAll += nFanouts;
        nFaninsMax   = Abc_MaxInt( nFaninsMax, nFanins );
        nFanoutsMax  = Abc_MaxInt( nFanoutsMax, nFanouts );
    }

    // allocate storage for fanin/fanout numbers
    nSizeMax = Abc_MaxInt( 10 * (Abc_Base10Log(nFaninsMax) + 1), 10 * (Abc_Base10Log(nFanoutsMax) + 1) );
    vFanins  = Vec_IntStart( nSizeMax );
    vFanouts = Vec_IntStart( nSizeMax );

    // count the number of fanins and fanouts
    Emb_ManForEachNode( p, pNode, i )
    {
        if ( i == 0 ) continue; // skip const 0 obj
        nFanins  = Emb_ObjFaninNum(pNode);
        nFanouts = Emb_ObjFanoutNum(pNode);

        if ( nFanins < 10 )
            Vec_IntAddToEntry( vFanins, nFanins, 1 );
        else if ( nFanins < 100 )
            Vec_IntAddToEntry( vFanins, 10 + nFanins/10, 1 );
        else if ( nFanins < 1000 )
            Vec_IntAddToEntry( vFanins, 20 + nFanins/100, 1 );
        else if ( nFanins < 10000 )
            Vec_IntAddToEntry( vFanins, 30 + nFanins/1000, 1 );
        else if ( nFanins < 100000 )
            Vec_IntAddToEntry( vFanins, 40 + nFanins/10000, 1 );
        else if ( nFanins < 1000000 )
            Vec_IntAddToEntry( vFanins, 50 + nFanins/100000, 1 );
        else if ( nFanins < 10000000 )
            Vec_IntAddToEntry( vFanins, 60 + nFanins/1000000, 1 );

        if ( nFanouts < 10 )
            Vec_IntAddToEntry( vFanouts, nFanouts, 1 );
        else if ( nFanouts < 100 )
            Vec_IntAddToEntry( vFanouts, 10 + nFanouts/10, 1 );
        else if ( nFanouts < 1000 )
            Vec_IntAddToEntry( vFanouts, 20 + nFanouts/100, 1 );
        else if ( nFanouts < 10000 )
            Vec_IntAddToEntry( vFanouts, 30 + nFanouts/1000, 1 );
        else if ( nFanouts < 100000 )
            Vec_IntAddToEntry( vFanouts, 40 + nFanouts/10000, 1 );
        else if ( nFanouts < 1000000 )
            Vec_IntAddToEntry( vFanouts, 50 + nFanouts/100000, 1 );
        else if ( nFanouts < 10000000 )
            Vec_IntAddToEntry( vFanouts, 60 + nFanouts/1000000, 1 );
    }

    printf( "The distribution of fanins and fanouts in the network:\n" );
    printf( "         Number   Nodes with fanin  Nodes with fanout\n" );
    for ( k = 0; k < nSizeMax; k++ )
    {
        if ( vFanins->pArray[k] == 0 && vFanouts->pArray[k] == 0 )
            continue;
        if ( k < 10 )
            printf( "%15d : ", k );
        else
        {
            sprintf( Buffer, "%d - %d", (int)pow((double)10, k/10) * (k%10), (int)pow((double)10, k/10) * (k%10+1) - 1 ); 
            printf( "%15s : ", Buffer );
        }
        if ( vFanins->pArray[k] == 0 )
            printf( "              " );
        else
            printf( "%12d  ", vFanins->pArray[k] );
        printf( "    " );
        if ( vFanouts->pArray[k] == 0 )
            printf( "              " );
        else
            printf( "%12d  ", vFanouts->pArray[k] );
        printf( "\n" );
    }
    Vec_IntFree( vFanins );
    Vec_IntFree( vFanouts );

    printf( "Fanins: Max = %d. Ave = %.2f.  Fanouts: Max = %d. Ave =  %.2f.\n", 
        nFaninsMax,  1.0*nFaninsAll/Emb_ManNodeNum(p), 
        nFanoutsMax, 1.0*nFanoutsAll/Emb_ManNodeNum(p)  );
}

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

  Synopsis    [Computes the distance from the given object]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Emb_ManComputeDistance_old( Emb_Man_t * p, Emb_Obj_t * pPivot )
{
    Vec_Int_t * vThis, * vNext, * vTemp;
    Emb_Obj_t * pThis, * pNext;
    int i, k, d, nVisited = 0;
//    assert( Emb_ObjIsTerm(pPivot) );
    vThis = Vec_IntAlloc( 1000 );
    vNext = Vec_IntAlloc( 1000 );
    Emb_ManIncrementTravId( p );
    Emb_ObjSetTravIdCurrent( p, pPivot );
    Vec_IntPush( vThis, pPivot->hHandle );
    for ( d = 0; Vec_IntSize(vThis) > 0; d++ )
    {
        nVisited += Vec_IntSize(vThis);
        Vec_IntClear( vNext );
        Emb_ManForEachObjVec( vThis, p, pThis, i )
        {
            Emb_ObjForEachFanin( pThis, pNext, k )
            {
                if ( Emb_ObjIsTravIdCurrent(p, pNext) )
                    continue;
                Emb_ObjSetTravIdCurrent(p, pNext);
                Vec_IntPush( vNext, pNext->hHandle );
                nVisited += !Emb_ObjIsTerm(pNext);
            }
            Emb_ObjForEachFanout( pThis, pNext, k )
            {
                if ( Emb_ObjIsTravIdCurrent(p, pNext) )
                    continue;
                Emb_ObjSetTravIdCurrent(p, pNext);
                Vec_IntPush( vNext, pNext->hHandle );
                nVisited += !Emb_ObjIsTerm(pNext);
            }
        }
        vTemp = vThis; vThis = vNext; vNext = vTemp;
    }
    Vec_IntFree( vThis );
    Vec_IntFree( vNext );
    // check if there are several strongly connected components
//    if ( nVisited < Emb_ManNodeNum(p) )
//        printf( "Visited less nodes (%d) than present (%d).\n", nVisited, Emb_ManNodeNum(p) );
    return d;
}

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

  Synopsis    [Traverses from the given node.]

  Description []
               
  SideEffects []

  SeeAlso     []
 
***********************************************************************/
void Gia_ManTestDistanceInternal( Emb_Man_t * p )
{
    int nAttempts = 20;
    int i, iNode, Dist;
    clock_t clk;
    Emb_Obj_t * pPivot, * pNext;
    Gia_ManRandom( 1 );
    Emb_ManResetTravId( p );
    // compute distances from several randomly selected PIs
    clk = clock();
    printf( "From inputs: " );
    for ( i = 0; i < nAttempts; i++ )
    {
        iNode = Gia_ManRandom( 0 ) % Emb_ManCiNum(p);
        pPivot = Emb_ManCi( p, iNode );
        if ( Emb_ObjFanoutNum(pPivot) == 0 )
            { i--; continue; }
        pNext = Emb_ObjFanout( pPivot, 0 );
        if ( !Emb_ObjIsNode(pNext) )
            { i--; continue; }
        Dist = Emb_ManComputeDistance_old( p, pPivot );
        printf( "%d ", Dist );
    }
    ABC_PRT( "Time", clock() - clk );
    // compute distances from several randomly selected POs
    clk = clock();
    printf( "From outputs: " );
    for ( i = 0; i < nAttempts; i++ )
    {
        iNode = Gia_ManRandom( 0 ) % Emb_ManCoNum(p);
        pPivot = Emb_ManCo( p, iNode );
        pNext = Emb_ObjFanin( pPivot, 0 );
        if ( !Emb_ObjIsNode(pNext) )
            { i--; continue; }
        Dist = Emb_ManComputeDistance_old( p, pPivot );
        printf( "%d ", Dist );
    }
    ABC_PRT( "Time", clock() - clk );
    // compute distances from several randomly selected nodes
    clk = clock();
    printf( "From nodes: " );
    for ( i = 0; i < nAttempts; i++ )
    {
        iNode = Gia_ManRandom( 0 ) % Gia_ManObjNum(p->pGia);
        if ( !~Gia_ManObj(p->pGia, iNode)->Value )
            { i--; continue; }
        pPivot = Emb_ManObj( p, Gia_ManObj(p->pGia, iNode)->Value );
        if ( !Emb_ObjIsNode(pPivot) )
            { i--; continue; }
        Dist = Emb_ManComputeDistance_old( p, pPivot );
        printf( "%d ", Dist );
    }
    ABC_PRT( "Time", clock() - clk );
}

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

  Synopsis    [Returns sorted array of node handles with largest fanout.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManTestDistance( Gia_Man_t * pGia )
{
    Emb_Man_t * p;
    clock_t clk = clock();
    p = Emb_ManStart( pGia );
//    Emb_ManPrintFanio( p );
    Emb_ManPrintStats( p );
ABC_PRT( "Time", clock() - clk );
    Gia_ManTestDistanceInternal( p );
    Emb_ManStop( p );
}




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

  Synopsis    [Perform BFS from the set of nodes.]

  Description [Returns one of the most distant objects.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Emb_Obj_t * Emb_ManPerformBfs( Emb_Man_t * p, Vec_Int_t * vThis, Vec_Int_t * vNext, Emb_Dat_t * pDist )
{
    Vec_Int_t * vTemp;
    Emb_Obj_t * pThis, * pNext, * pResult;
    int i, k;
    assert( Vec_IntSize(vThis) > 0 );
    for ( p->nDistMax = 0; Vec_IntSize(vThis) > 0; p->nDistMax++ )
    {
        p->nReached += Vec_IntSize(vThis);
        Vec_IntClear( vNext );
        Emb_ManForEachObjVec( vThis, p, pThis, i )
        {
            if ( pDist ) pDist[pThis->Value] = p->nDistMax;
            Emb_ObjForEachFanin( pThis, pNext, k )
            {
                if ( Emb_ObjIsTravIdCurrent(p, pNext) )
                    continue;
                Emb_ObjSetTravIdCurrent(p, pNext);
                Vec_IntPush( vNext, pNext->hHandle );
            }
            Emb_ObjForEachFanout( pThis, pNext, k )
            {
                if ( Emb_ObjIsTravIdCurrent(p, pNext) )
                    continue;
                Emb_ObjSetTravIdCurrent(p, pNext);
                Vec_IntPush( vNext, pNext->hHandle );
            }
        }
        vTemp = vThis; vThis = vNext; vNext = vTemp;
    }
    assert( Vec_IntSize(vNext) > 0 );
    pResult = Emb_ManObj( p, Vec_IntEntry(vNext, 0) );
    assert( pDist == NULL || pDist[pResult->Value] == p->nDistMax - 1 );
    return pResult;
}

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

  Synopsis    [Computes the distances from the given set of objects.]

  Description [Returns one of the most distant objects.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Emb_ManConnectedComponents( Emb_Man_t * p )
{
    Gia_Obj_t * pObj;
    Vec_Int_t * vThis, * vNext, * vResult;
    Emb_Obj_t * pThis;
    int i;
    vResult = Vec_IntAlloc( 1000 );
    vThis   = Vec_IntAlloc( 1000 );
    vNext   = Vec_IntAlloc( 1000 );
    p->nReached = 0;
    Emb_ManIncrementTravId( p );
    Gia_ManForEachCo( p->pGia, pObj, i )
    {
        pThis = Emb_ManObj( p, Gia_ObjValue(pObj) );
        if ( Emb_ObjIsTravIdCurrent(p, pThis) )
            continue;
        Emb_ObjSetTravIdCurrent( p, pThis );
        Vec_IntPush( vResult, pThis->hHandle );
        // perform BFS from this node
        Vec_IntClear( vThis );
        Vec_IntPush( vThis, pThis->hHandle );
        Emb_ManPerformBfs( p, vThis, vNext, NULL ); 
    }
    Vec_IntFree( vThis );
    Vec_IntFree( vNext );
    return vResult;
}

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

  Synopsis    [Computes the distances from the given set of objects.]

  Description [Returns one of the most distant objects.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Emb_Obj_t * Emb_ManFindDistances( Emb_Man_t * p, Vec_Int_t * vStart, Emb_Dat_t * pDist )
{
    Vec_Int_t * vThis, * vNext;
    Emb_Obj_t * pThis, * pResult;
    int i;
    p->nReached = p->nDistMax = 0;
    vThis = Vec_IntAlloc( 1000 );
    vNext = Vec_IntAlloc( 1000 );
    Emb_ManIncrementTravId( p );
    Emb_ManForEachObjVec( vStart, p, pThis, i )
    {
        Emb_ObjSetTravIdCurrent( p, pThis );
        Vec_IntPush( vThis, pThis->hHandle );
    }
    pResult = Emb_ManPerformBfs( p, vThis, vNext, pDist );
    Vec_IntFree( vThis );
    Vec_IntFree( vNext );
    return pResult;
}

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

  Synopsis    [Traverses from the given node.]

  Description []
               
  SideEffects []

  SeeAlso     []
 
***********************************************************************/
Emb_Obj_t * Emb_ManRandomVertex( Emb_Man_t * p )
{
    Emb_Obj_t * pPivot;
    do {
        int iNode = (911 * Gia_ManRandom(0)) % Gia_ManObjNum(p->pGia);
        if ( ~Gia_ManObj(p->pGia, iNode)->Value )
            pPivot = Emb_ManObj( p, Gia_ManObj(p->pGia, iNode)->Value );
        else
            pPivot = NULL;
    }
    while ( pPivot == NULL || !Emb_ObjIsNode(pPivot) );
    return pPivot;
}

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

  Synopsis    [Computes the distances from the given set of objects.]

  Description [Returns one of the most distant objects.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_DumpGraphIntoFile( Emb_Man_t * p )
{
    FILE * pFile;
    Emb_Obj_t * pThis, * pNext;
    int i, k;
    pFile = fopen( "1.g", "w" );
    Emb_ManForEachObj( p, pThis, i )
    {
        if ( !Emb_ObjIsTravIdCurrent(p, pThis) )
            continue;
        Emb_ObjForEachFanout( pThis, pNext, k )
        {
            assert( Emb_ObjIsTravIdCurrent(p, pNext) );
            fprintf( pFile, "%d %d\n", pThis->Value, pNext->Value );
        }
    }
    fclose( pFile );
}

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

  Synopsis    [Computes dimentions of the graph.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManComputeDimensions( Emb_Man_t * p, int nDims )
{
    Emb_Obj_t * pRandom, * pPivot;
    Vec_Int_t * vStart, * vComps;
    int d, nReached;
    int i;//, Counter;
    // connect unconnected components
    vComps = Emb_ManConnectedComponents( p );
//    printf( "Components = %d. Considered %d objects (out of %d).\n", Vec_IntSize(vComps), p->nReached, Emb_ManObjNum(p) );
    if ( Vec_IntSize(vComps) > 1 )
    {
        Emb_Obj_t * pFanin, * pObj = Emb_ManObj( p, 0 );
        Emb_ManForEachObjVec( vComps, p, pFanin, i )
        {
            assert( Emb_ObjIsCo(pFanin) );
            pFanin->Fanios[pFanin->nFanins + pFanin->nFanouts-1] = 
                pObj->Fanios[i] = pObj->hHandle - pFanin->hHandle;
        }
    }
    Vec_IntFree( vComps );
    // allocate memory for vectors
    assert( p->pVecs == NULL );
    p->pVecs = ABC_CALLOC( Emb_Dat_t, p->nObjs * nDims );
//    for ( i = 0; i < p->nObjs * nDims; i++ )
//        p->pVecs[i] = ABC_INFINITY;
    vStart = Vec_IntAlloc( nDims );
    // get the pivot vertex
    pRandom = Emb_ManRandomVertex( p );
    Vec_IntPush( vStart, pRandom->hHandle );
    // get the most distant vertex from the pivot
    pPivot = Emb_ManFindDistances( p, vStart, NULL );
//    Emb_DumpGraphIntoFile( p );
    nReached = p->nReached;
    if ( nReached < Emb_ManObjNum(p) )
    {
//        printf( "Considering a connected component with %d objects (out of %d).\n", p->nReached, Emb_ManObjNum(p) );
    }
    // start dimensions with this vertex
    Vec_IntClear( vStart );
    for ( d = 0; d < nDims; d++ )
    {
//        printf( "%3d : Adding vertex %7d with distance %3d.\n", d+1, pPivot->Value, p->nDistMax ); 
        Vec_IntPush( vStart, pPivot->hHandle );
        if ( d+1 == nReached )
            break;
        pPivot = Emb_ManFindDistances( p, vStart, Emb_ManVec(p, d) );
        assert( nReached == p->nReached );
    }
    Vec_IntFree( vStart );
    // make sure the number of reached objects is correct
//    Counter = 0;
//    for ( i = 0; i < p->nObjs; i++ )
//        if ( p->pVecs[i] < ABC_INFINITY )
//            Counter++;
//    assert( Counter == nReached );
}

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

  Synopsis    [Allocated square matrix of floats.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float ** Emb_ManMatrAlloc( int nDims )
{
    int i;
    float ** pMatr = (float **)ABC_ALLOC( char, sizeof(float *) * nDims + sizeof(float) * nDims * nDims );
    pMatr[0] = (float *)(pMatr + nDims);
    for ( i = 1; i < nDims; i++ )
        pMatr[i] = pMatr[i-1] + nDims;
    return pMatr;
}

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

  Synopsis    [Computes covariance matrix.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManComputeCovariance( Emb_Man_t * p, int nDims )
{
    Emb_Dat_t * pOne, * pTwo;
    double Ave;
    float * pRow;
    int d, i, k, v;
    // average vectors
    for ( d = 0; d < nDims; d++ )
    {
        // compute average
        Ave = 0.0;
        pOne = Emb_ManVec( p, d );
        for ( v = 0; v < p->nObjs; v++ )
            if ( pOne[v] < ABC_INFINITY )
                Ave += pOne[v];
        Ave /= p->nReached;
        // update the vector
        for ( v = 0; v < p->nObjs; v++ )
            if ( pOne[v] < ABC_INFINITY )
                pOne[v] -= Ave;
            else
                pOne[v] = 0.0;        
    }
    // compute the matrix
    assert( p->pMatr == NULL );
    assert( p->pEigen == NULL );
    p->pMatr = Emb_ManMatrAlloc( nDims );
    p->pEigen = Emb_ManMatrAlloc( nDims );
    for ( i = 0; i < nDims; i++ )
    {
        pOne = Emb_ManVec( p, i );
        pRow = p->pMatr[i];
        for ( k = 0; k < nDims; k++ )
        {
            pTwo = Emb_ManVec( p, k );
            pRow[k] = 0.0;
            for ( v = 0; v < p->nObjs; v++ )
                pRow[k] += pOne[v]*pTwo[v];
        }
    }
}

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

  Synopsis    [Returns random vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManVecRandom( float * pVec, int nDims )
{
    int i;
    for ( i = 0; i < nDims; i++ )
        pVec[i] = Gia_ManRandom( 0 );
}

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

  Synopsis    [Returns normalized vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManVecNormal( float * pVec, int nDims )
{
    int i;
    double Norm = 0.0;
    for ( i = 0; i < nDims; i++ )
        Norm += pVec[i] * pVec[i];
    Norm = pow( Norm, 0.5 );
    for ( i = 0; i < nDims; i++ )
        pVec[i] /= Norm;
}

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

  Synopsis    [Multiplies vector by vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Emb_ManVecMultiplyOne( float * pVec0, float * pVec1, int nDims )
{
    float Res = 0.0;
    int i;
    for ( i = 0; i < nDims; i++ )
        Res += pVec0[i] * pVec1[i];
    return Res;
}

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

  Synopsis    [Copies the vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManVecCopyOne( float * pVecDest, float * pVecSour, int nDims )
{
    int i;
    for ( i = 0; i < nDims; i++ )
        pVecDest[i] = pVecSour[i];
}

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

  Synopsis    [Multiplies matrix by vector.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManVecMultiply( float ** pMatr, float * pVec, int nDims, float * pRes )
{
    int k;
    for ( k = 0; k < nDims; k++ )
        pRes[k] = Emb_ManVecMultiplyOne( pMatr[k], pVec, nDims );
}

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

  Synopsis    [Multiplies vector by matrix.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManVecOrthogonolizeOne( float * pEigen, float * pVecI, int nDims, float * pVecRes )
{
    int k;
    for ( k = 0; k < nDims; k++ )
        pVecRes[k] = pVecI[k] - pEigen[k] * Emb_ManVecMultiplyOne( pVecI, pEigen, nDims );
}

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

  Synopsis    [Computes the first nSols eigen-vectors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManComputeEigenvectors( Emb_Man_t * p, int nDims, int nSols )
{
    float * pVecUiHat, * pVecUi;
    int i, j, k;
    assert( nSols < nDims );
    pVecUiHat = p->pEigen[nSols];
    for ( i = 0; i < nSols; i++ )
    {
        pVecUi = p->pEigen[i];
        Emb_ManVecRandom( pVecUiHat, nDims );
        Emb_ManVecNormal( pVecUiHat, nDims );
        k = 0;
        do {
            k++;
            Emb_ManVecCopyOne( pVecUi, pVecUiHat, nDims );
            for ( j = 0; j < i; j++ )
            {
                Emb_ManVecOrthogonolizeOne( p->pEigen[j], pVecUi, nDims, pVecUiHat );
                Emb_ManVecCopyOne( pVecUi, pVecUiHat, nDims );
            }
            Emb_ManVecMultiply( p->pMatr, pVecUi, nDims, pVecUiHat );
            Emb_ManVecNormal( pVecUiHat, nDims );
        } while ( Emb_ManVecMultiplyOne( pVecUiHat, pVecUi, nDims ) < 0.999 && k < 100 );
        Emb_ManVecCopyOne( pVecUi, pVecUiHat, nDims );
//        printf( "Converged after %d iterations.\n", k );
    }
}

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

  Synopsis    [Derives solutions from original vectors and eigenvectors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManComputeSolutions( Emb_Man_t * p, int nDims, int nSols )
{
    Emb_Dat_t * pX;
    float * pY;
    int i, j, k;
    assert( p->pSols == NULL );
    p->pSols = ABC_CALLOC( float, p->nObjs * nSols );
    for ( i = 0; i < nDims; i++ )
    {
        pX = Emb_ManVec( p, i );
        for ( j = 0; j < nSols; j++ )
        {
            pY = Emb_ManSol( p, j );
            for ( k = 0; k < p->nObjs; k++ )
                pY[k] += pX[k] * p->pEigen[j][i];
        }
    }
}

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

  Synopsis    [Projects into square of size [0;GIA_PLACE_SIZE] x [0;GIA_PLACE_SIZE].]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManDerivePlacement( Emb_Man_t * p, int nSols )
{
    float * pY0, * pY1, Max0, Max1, Min0, Min1, Str0, Str1;
    int * pPerm0, * pPerm1;
    int k;
    if ( nSols != 2 )
        return;
    // compute intervals
    Min0 =  ABC_INFINITY;
    Max0 = -ABC_INFINITY;
    pY0 = Emb_ManSol( p, 0 );
    for ( k = 0; k < p->nObjs; k++ )
    {
        Min0 = Abc_MinInt( Min0, pY0[k] );
        Max0 = Abc_MaxInt( Max0, pY0[k] );
    }
    Str0 = 1.0*GIA_PLACE_SIZE/(Max0 - Min0);
    // update the coordinates
    for ( k = 0; k < p->nObjs; k++ )
        pY0[k] = (pY0[k] != 0.0) ? ((pY0[k] - Min0) * Str0) : 0.0;

    // compute intervals
    Min1 =  ABC_INFINITY;
    Max1 = -ABC_INFINITY;
    pY1 = Emb_ManSol( p, 1 );
    for ( k = 0; k < p->nObjs; k++ )
    {
        Min1 = Abc_MinInt( Min1, pY1[k] );
        Max1 = Abc_MaxInt( Max1, pY1[k] );
    }
    Str1 = 1.0*GIA_PLACE_SIZE/(Max1 - Min1);
    // update the coordinates
    for ( k = 0; k < p->nObjs; k++ )
        pY1[k] = (pY1[k] != 0.0) ? ((pY1[k] - Min1) * Str1) : 0.0;

    // derive the order of these numbers
    pPerm0 = Gia_SortFloats( pY0, NULL, p->nObjs );
    pPerm1 = Gia_SortFloats( pY1, NULL, p->nObjs );

    // average solutions and project them into square [0;GIA_PLACE_SIZE] x [0;GIA_PLACE_SIZE]
    p->pPlacement = ABC_ALLOC( unsigned short, 2 * p->nObjs );
    for ( k = 0; k < p->nObjs; k++ )
    {
        p->pPlacement[2*pPerm0[k]+0] = (unsigned short)(int)(1.0 * k * GIA_PLACE_SIZE / p->nObjs);
        p->pPlacement[2*pPerm1[k]+1] = (unsigned short)(int)(1.0 * k * GIA_PLACE_SIZE / p->nObjs);
    }
    ABC_FREE( pPerm0 );
    ABC_FREE( pPerm1 );
}


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

  Synopsis    [Computes wire-length.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
double Emb_ManComputeHPWL( Emb_Man_t * p )
{
    double Result = 0.0;
    Emb_Obj_t * pThis, * pNext;
    int i, k, iMinX, iMaxX, iMinY, iMaxY;
    if ( p->pPlacement == NULL )
        return 0.0;
    Emb_ManForEachObj( p, pThis, i )
    {
        iMinX = iMaxX = p->pPlacement[2*pThis->Value+0];
        iMinY = iMaxY = p->pPlacement[2*pThis->Value+1];
        Emb_ObjForEachFanout( pThis, pNext, k )
        {
            iMinX = Abc_MinInt( iMinX, p->pPlacement[2*pNext->Value+0] );
            iMaxX = Abc_MaxInt( iMaxX, p->pPlacement[2*pNext->Value+0] );
            iMinY = Abc_MinInt( iMinY, p->pPlacement[2*pNext->Value+1] );
            iMaxY = Abc_MaxInt( iMaxY, p->pPlacement[2*pNext->Value+1] );
        }
        Result += (iMaxX - iMinX) + (iMaxY - iMinY);
    }
    return Result;
}


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

  Synopsis    [Performs iterative refinement of the given placement.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManPlacementRefine( Emb_Man_t * p, int nIters, int fVerbose )
{
    Emb_Obj_t * pThis, * pNext;
    double CostThis, CostPrev;
    float * pEdgeX, * pEdgeY;
    float * pVertX, * pVertY;
    float VertX, VertY;
    int * pPermX, * pPermY;
    int i, k, Iter, iMinX, iMaxX, iMinY, iMaxY;
    clock_t clk = clock();
    if ( p->pPlacement == NULL )
        return;
    pEdgeX = ABC_ALLOC( float, p->nObjs );
    pEdgeY = ABC_ALLOC( float, p->nObjs );
    pVertX = ABC_ALLOC( float, p->nObjs );
    pVertY = ABC_ALLOC( float, p->nObjs );
    // refine placement
    CostPrev = 0.0;
    for ( Iter = 0; Iter < nIters; Iter++ )
    {
        // compute centers of hyperedges
        CostThis = 0.0;
        Emb_ManForEachObj( p, pThis, i )
        {
            iMinX = iMaxX = p->pPlacement[2*pThis->Value+0];
            iMinY = iMaxY = p->pPlacement[2*pThis->Value+1];
            Emb_ObjForEachFanout( pThis, pNext, k )
            {
                iMinX = Abc_MinInt( iMinX, p->pPlacement[2*pNext->Value+0] );
                iMaxX = Abc_MaxInt( iMaxX, p->pPlacement[2*pNext->Value+0] );
                iMinY = Abc_MinInt( iMinY, p->pPlacement[2*pNext->Value+1] );
                iMaxY = Abc_MaxInt( iMaxY, p->pPlacement[2*pNext->Value+1] );
            }
            pEdgeX[pThis->Value] = 0.5 * (iMaxX + iMinX);
            pEdgeY[pThis->Value] = 0.5 * (iMaxY + iMinY);
            CostThis += (iMaxX - iMinX) + (iMaxY - iMinY);
        }
        // compute new centers of objects
        Emb_ManForEachObj( p, pThis, i )
        {
            VertX = pEdgeX[pThis->Value];
            VertY = pEdgeY[pThis->Value];
            Emb_ObjForEachFanin( pThis, pNext, k )
            {
                VertX += pEdgeX[pNext->Value];
                VertY += pEdgeY[pNext->Value];
            }
            pVertX[pThis->Value] = VertX / (Emb_ObjFaninNum(pThis) + 1);
            pVertY[pThis->Value] = VertY / (Emb_ObjFaninNum(pThis) + 1);
        }
        // sort these numbers
        pPermX = Gia_SortFloats( pVertX, NULL, p->nObjs );
        pPermY = Gia_SortFloats( pVertY, NULL, p->nObjs );
        for ( k = 0; k < p->nObjs; k++ )
        {
            p->pPlacement[2*pPermX[k]+0] = (unsigned short)(int)(1.0 * k * GIA_PLACE_SIZE / p->nObjs);
            p->pPlacement[2*pPermY[k]+1] = (unsigned short)(int)(1.0 * k * GIA_PLACE_SIZE / p->nObjs);
        }
        ABC_FREE( pPermX );
        ABC_FREE( pPermY );
        // evaluate cost
        if ( fVerbose )
        {
        printf( "%2d : HPWL = %e  ", Iter+1, CostThis );
        ABC_PRT( "Time", clock() - clk );
        }
    }
    ABC_FREE( pEdgeX );
    ABC_FREE( pEdgeY );
    ABC_FREE( pVertX );
    ABC_FREE( pVertY );
}


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

  Synopsis    [Derives solutions from original vectors and eigenvectors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManPrintSolutions( Emb_Man_t * p, int nSols )
{
    float * pSol;
    int i, k;
    for ( i = 0; i < nSols; i++ )
    {
        pSol = Emb_ManSol( p, i );
        for ( k = 0; k < p->nObjs; k++ )
            printf( "%4d ", (int)(100 * pSol[k]) );
        printf( "\n" );
    }
}

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

  Synopsis    [Prepares image for dumping.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Emb_ManDumpGnuplotPrepare( Emb_Man_t * p )
{
//    int nRows = 496;
//    int nCols = 710;
    int nRows = 500;
    int nCols = 700;
    Vec_Int_t * vLines;
    Emb_Obj_t * pThis;
    char * pBuffer, ** ppRows;
    int i, k, placeX, placeY;
    int fStart;
    // alloc memory
    pBuffer = ABC_CALLOC( char, nRows * (nCols+1) );
    ppRows  = ABC_ALLOC( char *, nRows );
    for ( i = 0; i < nRows; i++ )
        ppRows[i] = pBuffer + i*(nCols+1);
    // put data into them
    Emb_ManForEachObj( p, pThis, i )
    {
        placeX = p->pPlacement[2*pThis->Value+0] * nCols / (1<<16);
        placeY = p->pPlacement[2*pThis->Value+1] * nRows / (1<<16);
        assert( placeX < nCols && placeY < nRows );
        ppRows[placeY][placeX] = 1;
    }
    // select lines
    vLines = Vec_IntAlloc( 1000 );
    for ( i = 0; i < nRows; i++ )
    {
        fStart = 0;
        for ( k = 0; k <= nCols; k++ )
        {
            if ( ppRows[i][k] && !fStart )
            {
                Vec_IntPush( vLines, k );
                Vec_IntPush( vLines, i );
                fStart = 1;
            }
            if ( !ppRows[i][k] && fStart )
            {
                Vec_IntPush( vLines, k-1 );
                Vec_IntPush( vLines, i );
                fStart = 0;
            }
        }
        assert( fStart == 0 );
    }
    ABC_FREE( pBuffer );
    ABC_FREE( ppRows );
    return vLines;
}

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

  Synopsis    [Derives solutions from original vectors and eigenvectors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Emb_ManDumpGnuplot( Emb_Man_t * p, char * pName, int fDumpLarge, int fShowImage )
{
    extern void Gia_ManGnuplotShow( char * pPlotFileName );
//    char * pDirectory = "place\\";
    char * pDirectory = "";
//    extern char * Ioa_TimeStamp();
    FILE * pFile;
    char Buffer[1000];
    Emb_Obj_t * pThis, * pNext;
    int i, k;
    if ( p->pPlacement == NULL )
    {
        printf( "Emb_ManDumpGnuplot(): Placement is not available.\n" );
        return;
    }
    sprintf( Buffer, "%s%s", pDirectory, Gia_FileNameGenericAppend(pName, ".plt") ); 
    pFile = fopen( Buffer, "w" );
    fprintf( pFile, "# This Gnuplot file was produced by ABC on %s\n", Ioa_TimeStamp() );
    fprintf( pFile, "\n" );
    fprintf( pFile, "set nokey\n" );
    fprintf( pFile, "\n" );
    if ( !fShowImage )
    {
//    fprintf( pFile, "set terminal postscript\n" );
    fprintf( pFile, "set terminal gif font \'arial\' 10 size 800,600 xffffff x000000 x000000 x000000\n" );
    fprintf( pFile, "set output \'%s\'\n", Gia_FileNameGenericAppend(pName, ".gif") );
    fprintf( pFile, "\n" );
    }
    fprintf( pFile, "set title \"%s :  PI = %d   PO = %d   FF = %d   Node = %d   Obj = %d  HPWL = %.2e\\n", 
        pName, Emb_ManPiNum(p), Emb_ManPoNum(p), Emb_ManRegNum(p), Emb_ManNodeNum(p), Emb_ManObjNum(p), Emb_ManComputeHPWL(p) );
    fprintf( pFile, "(image generated by ABC and Gnuplot on %s)\"", Ioa_TimeStamp() );
    fprintf( pFile, "font \"Times, 12\"\n" );
    fprintf( pFile, "\n" );
    fprintf( pFile, "plot [:] '-' w l\n" );
    fprintf( pFile, "\n" );
    if ( fDumpLarge )
    {
        int begX, begY, endX, endY;
        Vec_Int_t * vLines = Emb_ManDumpGnuplotPrepare( p );
        Vec_IntForEachEntry( vLines, begX, i )
        {
            begY = Vec_IntEntry( vLines, i+1 );
            endX = Vec_IntEntry( vLines, i+2 );
            endY = Vec_IntEntry( vLines, i+3 );
            i += 3;
            fprintf( pFile, "%5d %5d\n", begX, begY );
            fprintf( pFile, "%5d %5d\n", endX, endY );
            fprintf( pFile, "\n" );
        }
        Vec_IntFree( vLines );
    }
    else
    {
        Emb_ManForEachObj( p, pThis, i )
        {
            if ( !Emb_ObjIsTravIdCurrent(p, pThis) )
                continue;
            Emb_ObjForEachFanout( pThis, pNext, k )
            {
                assert( Emb_ObjIsTravIdCurrent(p, pNext) );
                fprintf( pFile, "%5d %5d\n", p->pPlacement[2*pThis->Value+0], p->pPlacement[2*pThis->Value+1] );
                fprintf( pFile, "%5d %5d\n", p->pPlacement[2*pNext->Value+0], p->pPlacement[2*pNext->Value+1] );
                fprintf( pFile, "\n" );
            }
        }
    }
    fprintf( pFile, "EOF\n" );
    fprintf( pFile, "\n" );
    if ( fShowImage )
    {
    fprintf( pFile, "pause -1 \"Close window\"\n" );  // Hit return to continue
    fprintf( pFile, "reset\n" );
    fprintf( pFile, "\n" );
    }
    else
    {
    fprintf( pFile, "# pause -1 \"Close window\"\n" );  // Hit return to continue
    fprintf( pFile, "# reset\n" );
    fprintf( pFile, "\n" );
    }
    fclose( pFile );
    if ( fShowImage )
        Gia_ManGnuplotShow( Buffer );
}

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

  Synopsis    [Computes dimentions of the graph.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_ManSolveProblem( Gia_Man_t * pGia, Emb_Par_t * pPars )
{
    Emb_Man_t * p;
    int i;
    clock_t clkSetup;
    clock_t clk;
//   Gia_ManTestDistance( pGia );

    // transform AIG into internal data-structure
clk = clock();
    if ( pPars->fCluster )
    {
        p = Emb_ManStart( pGia );
        if ( pPars->fVerbose )
        {
            printf( "Clustered: " );
            Emb_ManPrintStats( p );
        }
    }
    else
        p = Emb_ManStartSimple( pGia );
    p->fVerbose = pPars->fVerbose;
//    Emb_ManPrintFanio( p );

    // prepare data-structure
    Gia_ManRandom( 1 );  // reset random numbers for deterministic behavior
    Emb_ManResetTravId( p );
    Emb_ManSetValue( p );
clkSetup = clock() - clk;

clk = clock();
    Emb_ManComputeDimensions( p, pPars->nDims );
if ( pPars->fVerbose )
ABC_PRT( "Setup     ", clkSetup );
if ( pPars->fVerbose )
ABC_PRT( "Dimensions", clock() - clk );

clk = clock();
    Emb_ManComputeCovariance( p, pPars->nDims );
if ( pPars->fVerbose )
ABC_PRT( "Matrix    ", clock() - clk );

clk = clock();
    Emb_ManComputeEigenvectors( p, pPars->nDims, pPars->nSols );
    Emb_ManComputeSolutions( p, pPars->nDims, pPars->nSols );
    Emb_ManDerivePlacement( p, pPars->nSols );
if ( pPars->fVerbose )
ABC_PRT( "Eigenvecs ", clock() - clk );

    if ( pPars->fRefine )
    {
clk = clock();
    Emb_ManPlacementRefine( p, pPars->nIters, pPars->fVerbose );
if ( pPars->fVerbose )
ABC_PRT( "Refinement", clock() - clk );
    }

    if ( (pPars->fDump || pPars->fDumpLarge) && pPars->nSols == 2 )
    {
clk = clock();
        Emb_ManDumpGnuplot( p, pGia->pName, pPars->fDumpLarge, pPars->fShowImage );
if ( pPars->fVerbose )
ABC_PRT( "Image dump", clock() - clk );
    }

    // transfer placement
    if ( Gia_ManObjNum(pGia) == p->nObjs )
    {
        // assuming normalized ordering of the AIG
        pGia->pPlacement = ABC_CALLOC( Gia_Plc_t, p->nObjs );
        for ( i = 0; i < p->nObjs; i++ ) 
        {
            pGia->pPlacement[i].xCoord = p->pPlacement[2*i+0];
            pGia->pPlacement[i].yCoord = p->pPlacement[2*i+1];
        }
    }
    Emb_ManStop( p );
}

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


ABC_NAMESPACE_IMPL_END

