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

  FileName    [saigSimFast.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Sequential AIG package.]

  Synopsis    [Fast sequential AIG simulator.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "saig.h"

#include "main.h"

ABC_NAMESPACE_IMPL_START


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

// the AIG manager
typedef struct Faig_Man_t_ Faig_Man_t;
struct Faig_Man_t_
{
    // parameters
    int            nPis;
    int            nPos;
    int            nCis;
    int            nCos;
    int            nFfs;
    int            nNos;
    // offsets
    int            nPis1;
    int            nCis1;
    int            nCisNos1;
    int            nCisNosPos1;
    int            nObjs;
    // allocated data
    int            nWords;
    int            pObjs[0];
};

static inline int  Faig_CheckIdPi( Faig_Man_t * p, int i )    { return i >= 1 && i < p->nPis1;                     }
static inline int  Faig_CheckIdLo( Faig_Man_t * p, int i )    { return i >= p->nPis1 && i < p->nCis1;              }
static inline int  Faig_CheckIdNo( Faig_Man_t * p, int i )    { return i >= p->nCis1 && i < p->nCisNos1;           }
static inline int  Faig_CheckIdPo( Faig_Man_t * p, int i )    { return i >= p->nCisNos1 && i < p->nCisNosPos1;     }
static inline int  Faig_CheckIdLi( Faig_Man_t * p, int i )    { return i >= p->nCisNosPos1 && i < p->nObjs;        }
static inline int  Faig_CheckIdCo( Faig_Man_t * p, int i )    { return i >= p->nCisNos1 && i < p->nObjs;           }
static inline int  Faig_CheckIdObj( Faig_Man_t * p, int i )   { return i >= 0 && i < p->nObjs;                     }

static inline int  Faig_ObjIdToNumPi( Faig_Man_t * p, int i ) { assert( Faig_CheckIdPi(p,i) ); return i - 1;              }
static inline int  Faig_ObjIdToNumLo( Faig_Man_t * p, int i ) { assert( Faig_CheckIdLo(p,i) ); return i - p->nPis1;       }
static inline int  Faig_ObjIdToNumNo( Faig_Man_t * p, int i ) { assert( Faig_CheckIdNo(p,i) ); return i - p->nCis1;       }
static inline int  Faig_ObjIdToNumPo( Faig_Man_t * p, int i ) { assert( Faig_CheckIdPo(p,i) ); return i - p->nCisNos1;    }
static inline int  Faig_ObjIdToNumLi( Faig_Man_t * p, int i ) { assert( Faig_CheckIdLi(p,i) ); return i - p->nCisNosPos1; }
static inline int  Faig_ObjIdToNumCo( Faig_Man_t * p, int i ) { assert( Faig_CheckIdCo(p,i) ); return i - p->nCisNos1;    }

static inline int  Faig_ObjLoToLi( Faig_Man_t * p, int i )    { assert( Faig_CheckIdLo(p,i) ); return p->nObjs - (p->nCis1 - i); }
static inline int  Faig_ObjLiToLo( Faig_Man_t * p, int i )    { assert( Faig_CheckIdLi(p,i) ); return p->nCis1 - (p->nObjs - i); }

static inline int  Faig_NodeChild0( Faig_Man_t * p, int n )   { return p->pObjs[n<<1];             }
static inline int  Faig_NodeChild1( Faig_Man_t * p, int n )   { return p->pObjs[(n<<1)+1];         }
static inline int  Faig_CoChild0( Faig_Man_t * p, int n )     { return p->pObjs[(p->nNos<<1)+n];   }
static inline int  Faig_ObjFaninC( int iFan )                 { return iFan & 1;                   }
static inline int  Faig_ObjFanin( int iFan )                  { return iFan >> 1;                  }

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

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

  Synopsis    [Checks if the manager is correct.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Faig_ManIsCorrect( Aig_Man_t * pAig )
{
    return Aig_ManObjNumMax(pAig) == 
        1 + Aig_ManPiNum(pAig) + Aig_ManNodeNum(pAig) + Aig_ManPoNum(pAig);
}

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

  Synopsis    [Creates fast simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Faig_Man_t * Faig_ManAlloc( Aig_Man_t * pAig )
{
    Faig_Man_t * p;
    int nWords;
//    assert( Faig_ManIsCorrect(pAig) );
    nWords = 2 * Aig_ManNodeNum(pAig) + Aig_ManPoNum(pAig);
    p = (Faig_Man_t *)ABC_ALLOC( char, sizeof(Faig_Man_t) + sizeof(int) * nWords );
//printf( "Allocating %7.2f Mb.\n", 1.0 * (sizeof(Faig_Man_t) + sizeof(int) * nWords)/(1<<20) );
    memset( p, 0, sizeof(Faig_Man_t) );
    p->nPis   = Aig_ManPiNum(pAig) - Aig_ManRegNum(pAig);
    p->nPos   = Aig_ManPoNum(pAig) - Aig_ManRegNum(pAig);
    p->nCis   = Aig_ManPiNum(pAig);
    p->nCos   = Aig_ManPoNum(pAig);
    p->nFfs   = Aig_ManRegNum(pAig);
    p->nNos   = Aig_ManNodeNum(pAig);
    // offsets
    p->nPis1  = p->nPis + 1;
    p->nCis1  = p->nCis + 1;
    p->nCisNos1 = p->nCis + p->nNos + 1;
    p->nCisNosPos1 = p->nCis + p->nNos + p->nPos + 1;
    p->nObjs  = p->nCis + p->nNos + p->nCos + 1;
    p->nWords = nWords;
    return p;
}

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

  Synopsis    [Creates fast simulation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Faig_Man_t * Faig_ManCreate( Aig_Man_t * pAig )
{
    Faig_Man_t * p;
    Aig_Obj_t * pObj;
    int i, iWord = 0;
    p = Faig_ManAlloc( pAig );
    Aig_ManForEachNode( pAig, pObj, i )
    {
        p->pObjs[iWord++] = (Aig_ObjFaninId0(pObj) << 1) | Aig_ObjFaninC0(pObj);
        p->pObjs[iWord++] = (Aig_ObjFaninId1(pObj) << 1) | Aig_ObjFaninC1(pObj);
    }
    Aig_ManForEachPo( pAig, pObj, i )
        p->pObjs[iWord++] = (Aig_ObjFaninId0(pObj) << 1) | Aig_ObjFaninC0(pObj);
    assert( iWord == p->nWords );
    return p;
}

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

  Synopsis    [Simulates one node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned Faig_SimulateNode( Faig_Man_t * p, int Id, unsigned * pSimInfo )
{
    int n = Faig_ObjIdToNumNo( p, Id );
    int iFan0 = Faig_NodeChild0( p, n );
    int iFan1 = Faig_NodeChild1( p, n );
    if ( Faig_ObjFaninC(iFan0) && Faig_ObjFaninC(iFan1) )
        return ~(pSimInfo[Faig_ObjFanin(iFan0)] | pSimInfo[Faig_ObjFanin(iFan1)]);
    if ( Faig_ObjFaninC(iFan0) && !Faig_ObjFaninC(iFan1) )
        return (~pSimInfo[Faig_ObjFanin(iFan0)] & pSimInfo[Faig_ObjFanin(iFan1)]);
    if ( !Faig_ObjFaninC(iFan0) && Faig_ObjFaninC(iFan1) )
        return (pSimInfo[Faig_ObjFanin(iFan0)] & ~pSimInfo[Faig_ObjFanin(iFan1)]);
    // if ( !Faig_ObjFaninC(iFan0) && !Faig_ObjFaninC(iFan1) )
    return (pSimInfo[Faig_ObjFanin(iFan0)] & pSimInfo[Faig_ObjFanin(iFan1)]);
} 

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

  Synopsis    [Simulates one node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned Faig_SimulateCo( Faig_Man_t * p, int Id, unsigned * pSimInfo )
{
    int n = Faig_ObjIdToNumCo( p, Id );
    int iFan0 = Faig_CoChild0( p, n );
    if ( Faig_ObjFaninC(iFan0) )
        return ~pSimInfo[Faig_ObjFanin(iFan0)];
    // if ( !Faig_ObjFaninC(iFan0) )
    return pSimInfo[Faig_ObjFanin(iFan0)];
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned Faig_SimulateRandomShift( unsigned uOld )
{
    return (uOld << 16) | ((uOld ^ Aig_ManRandom(0)) & 0xffff);
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline unsigned Faig_SimulateTransferShift( unsigned uOld, unsigned uNew )
{
    return (uOld << 16) | (uNew & 0xffff);
}

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

  Synopsis    [Simulates the timeframes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int * Faig_ManSimulateFrames( Faig_Man_t * p, int nFrames, int nPref, int fTrans )
{
    int * pNumOnes = ABC_CALLOC( int, p->nObjs );
    unsigned * pSimInfo = ABC_ALLOC( unsigned, p->nObjs );
    int f, i;
//printf( "Allocating %7.2f Mb.\n", 1.0 * 4 * p->nObjs/(1<<20) );
//printf( "Allocating %7.2f Mb.\n", 1.0 * 4 * p->nObjs/(1<<20) );
    // set constant 1
    pSimInfo[0] = ~0;
    for ( f = 0; f < nFrames; f++ )
    {
        if ( fTrans )
        {
            for ( i = 1; i < p->nPis1; i++ )
                pSimInfo[i] = f? Faig_SimulateRandomShift( pSimInfo[i] ) : Aig_ManRandom( 0 );
            for (      ; i < p->nCis1; i++ )
                pSimInfo[i] = f? Faig_SimulateTransferShift( pSimInfo[i], pSimInfo[Faig_ObjLoToLi(p,i)] ) : 0;
        }
        else
        {
            for ( i = 1; i < p->nPis1; i++ )
                pSimInfo[i] = Aig_ManRandom( 0 );
            for (      ; i < p->nCis1; i++ )
                pSimInfo[i] = f? pSimInfo[Faig_ObjLoToLi(p,i)] : 0;
        }
        for (      ; i < p->nCisNos1; i++ )
            pSimInfo[i] = Faig_SimulateNode( p, i, pSimInfo );
        for (      ; i < p->nObjs; i++ )
            pSimInfo[i] = Faig_SimulateCo( p, i, pSimInfo );
        if ( f < nPref )
            continue;
        if ( fTrans )
        {
            for ( i = 0; i < p->nObjs; i++ )
                pNumOnes[i] += Aig_WordCountOnes( (pSimInfo[i] ^ (pSimInfo[i] >> 16)) & 0xffff );
        }
        else
        {
            for ( i = 0; i < p->nObjs; i++ )
                pNumOnes[i] += Aig_WordCountOnes( pSimInfo[i] );
        }
    }
    ABC_FREE( pSimInfo );
    return pNumOnes;
}

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

  Synopsis    [Computes switching activity of one node.]

  Description [Uses the formula: Switching = 2 * nOnes * nZeros / (nTotal ^ 2) ]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Faig_ManComputeSwitching( int nOnes, int nSimWords )
{
    int nTotal = 32 * nSimWords;
    return (float)2.0 * nOnes / nTotal * (nTotal - nOnes) / nTotal;
}

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

  Synopsis    [Computes switching activity of one node.]

  Description [Uses the formula: Switching = 2 * nOnes * nZeros / (nTotal ^ 2) ]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
float Faig_ManComputeProbOne( int nOnes, int nSimWords )
{
    int nTotal = 32 * nSimWords;
    return (float)nOnes / nTotal;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Faig_ManComputeSwitchProbs4( Aig_Man_t * p, int nFrames, int nPref, int fProbOne )
{
    int fTrans = 1;
    Faig_Man_t * pAig;
    Vec_Int_t * vSwitching;
    int * pProbs;
    float * pSwitching;
    int nFramesReal, clk, clkTotal = clock();
    if ( fProbOne )
        fTrans = 0;
    vSwitching = Vec_IntStart( Aig_ManObjNumMax(p) );
    pSwitching = (float *)vSwitching->pArray;
clk = clock();
    pAig = Faig_ManCreate( p );
//ABC_PRT( "\nCreation  ", clock() - clk );
    Aig_ManRandom( 1 );
    // get the number of  frames to simulate
    // if the parameter "seqsimframes" is defined, use it
    // otherwise, use the given number of frames "nFrames"
    nFramesReal = nFrames;
    if ( Abc_FrameReadFlag("seqsimframes") )
        nFramesReal = atoi( Abc_FrameReadFlag("seqsimframes") );
    if ( nFramesReal <= nPref )
    {
        printf( "The total number of frames (%d) should exceed prefix (%d).\n", nFramesReal, nPref );
        printf( "Setting the total number of frames to be %d.\n", nFrames );
        nFramesReal = nFrames;
    }
//printf( "Simulating %d frames.\n", nFramesReal );
clk = clock();
    pProbs = Faig_ManSimulateFrames( pAig, nFramesReal, nPref, fTrans );
//ABC_PRT( "Simulation", clock() - clk );
clk = clock();
    if ( fTrans )
    {
        Aig_Obj_t * pObj;
        int i, Counter = 0;
        pObj = Aig_ManConst1(p);
        pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], (nFramesReal - nPref)/2 );
        Aig_ManForEachPi( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], (nFramesReal - nPref)/2 );
        Aig_ManForEachNode( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], (nFramesReal - nPref)/2 );
        Aig_ManForEachPo( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], (nFramesReal - nPref)/2 );
        assert( Counter == pAig->nObjs );
    }
    else if ( fProbOne )
    {
        Aig_Obj_t * pObj;
        int i, Counter = 0;
        pObj = Aig_ManConst1(p);
        pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachPi( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachNode( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachPo( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeProbOne( pProbs[Counter++], nFramesReal - nPref );
        assert( Counter == pAig->nObjs );
    }
    else 
    {
        Aig_Obj_t * pObj;
        int i, Counter = 0;
        pObj = Aig_ManConst1(p);
        pSwitching[pObj->Id] = Faig_ManComputeSwitching( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachPi( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeSwitching( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachNode( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeSwitching( pProbs[Counter++], nFramesReal - nPref );
        Aig_ManForEachPo( p, pObj, i )
            pSwitching[pObj->Id] = Faig_ManComputeSwitching( pProbs[Counter++], nFramesReal - nPref );
        assert( Counter == pAig->nObjs );
    }
    ABC_FREE( pProbs );
    ABC_FREE( pAig );
//ABC_PRT( "Switch    ", clock() - clk );
//ABC_PRT( "TOTAL     ", clock() - clkTotal );
    return vSwitching;
}

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

  Synopsis    [Computes probability of switching (or of being 1).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Saig_ManComputeSwitchProb3s( Aig_Man_t * p, int nFrames, int nPref, int fProbOne )
{
//    return Faig_ManComputeSwitchProbs( p, nFrames, nPref, fProbOne );
    return NULL;
}


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


ABC_NAMESPACE_IMPL_END