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

  FileName    [giaGig.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Parser for Gate-Inverter Graph by Niklas Een.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "gia.h"
#include "misc/extra/extra.h"
#include "misc/util/utilTruth.h"

ABC_NAMESPACE_IMPL_START


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

#define MAX_LINE 1000000

// network types
enum { 
    GLS_NONE = -1,  // not used
    GLS_ZERO =  0,  // zero
    GLS_ONE  =  1,  // one
    GLS_PI   =  2,  // primary input
    GLS_PO   =  3,  // primary output
    GLS_BAR  =  4,  // barrier
    GLS_SEQ  =  5,  // sequential
    GLS_SEL  =  6,  // fan
    GLS_LUT4 =  7,  // LUT4
    GLS_LUT6 =  8,  // LUT6
    GLS_BOX  =  9,  // sequential box
    GLS_DEL  = 10,  // delay box
    GLS_FINAL
};

static char * s_Strs[GLS_FINAL] =
{
    "0",     // GLS_ZERO =  0,  // zero
    "1",     // GLS_ONE  =  1,  // one
    "PI",    // GLS_PI   =  2,  // primary input
    "PO",    // GLS_PO   =  3,  // primary output
    "Bar",   // GLS_BAR  =  4,  // barrier
    "Seq",   // GLS_SEQ  =  5,  // sequential
    "Sel",   // GLS_SEL  =  6,  // fan
    "Lut4",  // GLS_LUT4 =  7,  // LUT4
    "Lut6",  // GLS_LUT6 =  8,  // LUT6
    "Box",   // GLS_BOX  =  9,  // sequential box
    "Del"    // GLS_DEL  = 10,  // delay box
};

typedef struct Gls_Man_t_ Gls_Man_t;
struct Gls_Man_t_
{
    // general
    Vec_Str_t *    vLines;       // line types
    Vec_Str_t *    vTypes;       // gate types
    Vec_Int_t *    vIndexes;     // gate indexes
    // specific types
    Vec_Int_t *    vLut4s;       // 4-LUTs (4-tuples)
    Vec_Int_t *    vLut4TTs;     // truth tables
    Vec_Int_t *    vLut6s;       // 6-LUTs (6-tuples)
    Vec_Wrd_t *    vLut6TTs;     // truth tables
    Vec_Int_t *    vBoxes;       // boxes (5-tuples)
    Vec_Wec_t *    vDelayIns;    // delay fanins
    Vec_Wec_t *    vDelayOuts;   // delay fanouts
    Vec_Int_t *    vDelays;      // delay values
    // ordering
    Vec_Int_t *    vOrderPis;  
    Vec_Int_t *    vOrderPos;  
    Vec_Int_t *    vOrderBoxes;  
    Vec_Int_t *    vOrderDelays;  
    Vec_Int_t *    vOrderLuts;  
    Vec_Int_t *    vOrderSeqs;  
};

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gls_Man_t * Gls_ManAlloc( Vec_Str_t * vLines, int * pCounts )
{
    Gls_Man_t * p = ABC_CALLOC( Gls_Man_t, 1 );
    p->vLines     = vLines;
    p->vTypes     = Vec_StrStart( Vec_StrSize(vLines)+100 ); 
    p->vIndexes   = Vec_IntStart( Vec_StrSize(vLines)+100 ); 
    p->vLut4s     = Vec_IntAlloc( 4 * pCounts[GLS_LUT4] );
    p->vLut4TTs   = Vec_IntAlloc( pCounts[GLS_LUT4] );
    p->vLut6s     = Vec_IntAlloc( 6 * pCounts[GLS_LUT6] );
    p->vLut6TTs   = Vec_WrdAlloc( pCounts[GLS_LUT6] );
    p->vBoxes     = Vec_IntAlloc( 5 * pCounts[GLS_BOX] );
    p->vDelays    = Vec_IntAlloc( pCounts[GLS_DEL] );
    p->vDelayIns  = Vec_WecAlloc( pCounts[GLS_DEL] );
    p->vDelayOuts = Vec_WecAlloc( pCounts[GLS_DEL] );
    // ordering
    p->vOrderPis    = Vec_IntAlloc( pCounts[GLS_PI] );
    p->vOrderPos    = Vec_IntAlloc( pCounts[GLS_PO] );
    p->vOrderBoxes  = Vec_IntAlloc( pCounts[GLS_BOX] );
    p->vOrderDelays = Vec_IntAlloc( pCounts[GLS_DEL] );
    p->vOrderLuts   = Vec_IntAlloc( pCounts[GLS_LUT4] + pCounts[GLS_LUT6] + 2*pCounts[GLS_BAR] );
    p->vOrderSeqs   = Vec_IntAlloc( pCounts[GLS_SEQ] );
    return p;
}
void Gls_ManStop( Gls_Man_t * p )
{
    Vec_StrFree( p->vLines );
    Vec_StrFree( p->vTypes );
    Vec_IntFree( p->vIndexes );
    Vec_IntFree( p->vLut4s );
    Vec_IntFree( p->vLut4TTs );
    Vec_IntFree( p->vLut6s );
    Vec_WrdFree( p->vLut6TTs );
    Vec_IntFree( p->vBoxes );
    Vec_IntFree( p->vDelays );
    Vec_WecFree( p->vDelayIns );
    Vec_WecFree( p->vDelayOuts );
    // ordering
    Vec_IntFree( p->vOrderPis );
    Vec_IntFree( p->vOrderPos );
    Vec_IntFree( p->vOrderBoxes );
    Vec_IntFree( p->vOrderDelays );
    Vec_IntFree( p->vOrderLuts );
    Vec_IntFree( p->vOrderSeqs );
    ABC_FREE( p );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Str_t * Gls_ManCount( FILE * pFile, int pCounts[GLS_FINAL] )
{
    char * pLine, * pBuffer = ABC_ALLOC(char, MAX_LINE); int Type;
    Vec_Str_t * vLines = Vec_StrAlloc( 10000 );
    memset( pCounts, 0, sizeof(int)*GLS_FINAL );
    while ( fgets( pBuffer, MAX_LINE, pFile ) != NULL )
    {
        pLine = pBuffer;
        while ( *pLine )
            if ( *pLine++ == '=' )
                break;
        while ( *pLine == ' ' )
            pLine++;
        if ( *pLine == 'L' )
        {
            if ( pLine[3] == '4' )
                Type = GLS_LUT4;
            else if ( pLine[3] == '6' )
                Type = GLS_LUT6;
            else assert( 0 );
        }
        else if ( *pLine == 'P' )
        {
            if ( pLine[1] == 'I' )
                Type = GLS_PI;
            else if ( pLine[1] == 'O' )
                Type = GLS_PO;
            else assert( 0 );
        }
        else if ( *pLine == 'B' )
        {
            if ( pLine[1] == 'o' )
                Type = GLS_BOX;
            else if ( pLine[1] == 'a' )
                Type = GLS_BAR;
            else assert( 0 );
        }
        else if ( *pLine == 'S' )
        {
            if ( pLine[2] == 'l' )
                Type = GLS_SEL;
            else if ( pLine[2] == 'q' )
                Type = GLS_SEQ;
            else assert( 0 );
        }
        else if ( *pLine == 'D' )
            Type = GLS_DEL;
        else assert( 0 );
        Vec_StrPush( vLines, (char)Type );
        pCounts[Type]++;
    }
    ABC_FREE( pBuffer );
    return vLines;
}
int Gls_ManParseOne( char ** ppLine )
{
    int Entry;
    char * pLine = *ppLine;
    while ( *pLine == ' ' )   pLine++;
    if ( *pLine == '-' )
        Entry = GLS_NONE;
    else if ( *pLine == '0' )
        Entry = 0;
    else if ( *pLine == '1' )
        Entry = 1;
    else if ( *pLine == 'w' )
        Entry = atoi(++pLine);
    else assert( 0 );
    while ( *pLine == '-' || (*pLine >= '0' && *pLine <= '9') )   pLine++;
    while ( *pLine == ' ' )   pLine++;
    *ppLine = pLine;
    return Entry;
}
int Gls_ManParse( FILE * pFile, Gls_Man_t * p )
{
    char * pLine, * pBuffer = ABC_ALLOC(char, MAX_LINE); 
    int i, k, Type, iObj, Entry, iItem;  word Truth;
    for ( i = 0; fgets( pBuffer, MAX_LINE, pFile ) != NULL; i++ )
    {
        pLine = pBuffer;
        Type = Vec_StrEntry( p->vLines, i );
        iObj = Gls_ManParseOne( &pLine );
        Vec_StrWriteEntry( p->vTypes, iObj, (char)Type );
        if ( Type == GLS_PI )
        {
            Vec_IntPush( p->vOrderPis, iObj );
            Vec_IntWriteEntry( p->vIndexes, iObj, -1 );
            continue;
        }
        while ( *pLine )
            if ( *pLine++ == '(' )
                break;
        Entry = Gls_ManParseOne( &pLine );
        if ( Type == GLS_PO || Type == GLS_BAR || Type == GLS_SEQ || Type == GLS_SEL )
        {
            if ( Type == GLS_PO )
                Vec_IntPush( p->vOrderPos, iObj );
            else if ( Type == GLS_BAR )
                Vec_IntPush( p->vOrderLuts, iObj );
            else if ( Type == GLS_SEQ )
                Vec_IntPush( p->vOrderSeqs, iObj );
            else if ( Type == GLS_SEL )
            {
                if ( (int)Vec_StrEntry(p->vTypes, Entry) == GLS_DEL )
                {
                    Vec_Int_t * vOuts = Vec_WecEntry( p->vDelayOuts, Vec_IntEntry(p->vIndexes, Entry) );
                    Vec_IntPush( vOuts, iObj );
                }
                else if ( (int)Vec_StrEntry(p->vTypes, Entry) == GLS_BAR )
                    Vec_IntPush( p->vOrderLuts, iObj );
                else assert( 0 );
            }
            Vec_IntWriteEntry( p->vIndexes, iObj, Entry );
            continue;
        }
        if ( Type == GLS_LUT4 )
        {
            Vec_IntWriteEntry( p->vIndexes, iObj, Vec_IntSize(p->vLut4TTs) );
            Vec_IntPush( p->vLut4s, Entry );
            for ( k = 1; ; k++ )
            {
                if ( *pLine != ',' )     break;
                pLine++;
                Entry = Gls_ManParseOne( &pLine );
                Vec_IntPush( p->vLut4s, Entry );
            }
            assert( *pLine == ')' );
            assert( k == 4 );
            pLine++;
            while ( *pLine )
                if ( *pLine++ == '[' )
                    break;
            Abc_TtReadHex( &Truth, pLine );
            Vec_IntPush( p->vLut4TTs, (unsigned)Truth );
            Vec_IntPush( p->vOrderLuts, iObj );
        }
        else if ( Type == GLS_LUT6 )
        {
            Vec_IntWriteEntry( p->vIndexes, iObj, Vec_WrdSize(p->vLut6TTs) );
            Vec_IntPush( p->vLut6s, Entry );
            for ( k = 1; ; k++ )
            {
                if ( *pLine != ',' )     break;
                pLine++;
                Entry = Gls_ManParseOne( &pLine );
                Vec_IntPush( p->vLut6s, Entry );
            }
            assert( *pLine == ')' );
            assert( k == 4 );
            pLine++;
            while ( *pLine )
                if ( *pLine++ == '[' )
                    break;
            Abc_TtReadHex( &Truth, pLine );
            Vec_WrdPush( p->vLut6TTs, Truth );
            Vec_IntPush( p->vOrderLuts, iObj );
        }
        else if ( Type == GLS_BOX )
        {
            Vec_IntWriteEntry( p->vIndexes, iObj, Vec_IntSize(p->vBoxes)/5 );
            Vec_IntPush( p->vBoxes, Entry );
            for ( k = 1; ; k++ )
            {
                if ( *pLine != ',' )     break;
                pLine++;
                Entry = Gls_ManParseOne( &pLine );
                Vec_IntPush( p->vBoxes, Entry );
            }
            assert( *pLine == ')' );
            assert( k == 4 || k == 5 );
            if ( k == 4 )
                Vec_IntPush( p->vBoxes, GLS_NONE );
            Vec_IntPush( p->vOrderBoxes, iObj );
        }
        else if ( Type == GLS_DEL )
        {
            Vec_Int_t * vIns  = Vec_WecPushLevel( p->vDelayIns );
            Vec_Int_t * vOuts = Vec_WecPushLevel( p->vDelayOuts );
            Vec_IntWriteEntry( p->vIndexes, iObj, Vec_IntSize(p->vDelays) );
            Vec_IntPush( vIns, Entry );
            if ( *pLine != ')' )
            {
                for ( k = 1; ; k++ )
                {
                    if ( *pLine != ',' )     break;
                    pLine++;
                    Entry = Gls_ManParseOne( &pLine );
                    Vec_IntPush( vIns, Entry );
                }
            }
            assert( *pLine == ')' );
            pLine++;
            while ( *pLine )
                if ( *pLine++ == '[' )
                    break;
            iItem = atoi(pLine);
            Vec_IntPush( p->vDelays, iItem );
            Vec_IntPush( p->vOrderDelays, iObj );
            vOuts = vIns; // harmless use to prevent a compiler warning
        }
        else assert( 0 );
    }
    ABC_FREE( pBuffer );
    return 1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gls_ManConstruct( Gls_Man_t * p, char * pFileName )
{
    extern int Kit_TruthToGia( Gia_Man_t * pMan, unsigned * pTruth, int nVars, Vec_Int_t * vMemory, Vec_Int_t * vLeaves, int fHash );
    Gia_Man_t * pGia = NULL;  
    Vec_Int_t * vMap, * vArray;
    Vec_Int_t * vCover = Vec_IntAlloc(0);
    Vec_Int_t * vLeaves = Vec_IntAlloc(6);
    int  k, iObj, iLit, Index;  char Type;
    // create new manager
    pGia = Gia_ManStart( Vec_StrSize(p->vTypes) );
    pGia->pName = Abc_UtilStrsav( pFileName );
    pGia->pSpec = Abc_UtilStrsav( pFileName );
    // create constants
    vMap = Vec_IntStartFull( Vec_StrSize(p->vTypes) );
    Vec_IntWriteEntry( vMap, 0, 0 );
    Vec_IntWriteEntry( vMap, 1, 1 );
    // create primary inputs
    Vec_IntForEachEntry( p->vOrderPis, iObj, k )
        Vec_IntWriteEntry( vMap, iObj, Gia_ManAppendCi(pGia) );
    // create box outputs
    Vec_IntForEachEntry( p->vOrderBoxes, iObj, k )
        Vec_IntWriteEntry( vMap, iObj, Gia_ManAppendCi(pGia) );
    // create delay outputs
    Vec_IntForEachEntry( p->vOrderDelays, iObj, Index )
    {
        assert( Index == Vec_IntEntry(p->vIndexes, iObj) );
        vArray = Vec_WecEntry(p->vDelayOuts, Index);
        if ( Vec_IntSize(vArray) == 0 )
            Vec_IntWriteEntry( vMap, iObj, Gia_ManAppendCi(pGia) );
        else
            Vec_IntForEachEntry( vArray, iObj, k )
                Vec_IntWriteEntry( vMap, iObj, Gia_ManAppendCi(pGia) );
    }
    // construct LUTs
    Vec_IntForEachEntry( p->vOrderLuts, iObj, Index )
    {
        Type = Vec_StrEntry( p->vTypes, iObj );
        if ( Type == GLS_LUT4 || Type == GLS_LUT6 )
        {
            int Limit = Type == GLS_LUT4 ? 4 : 6;
            int Index = Vec_IntEntry(p->vIndexes, iObj);
            int * pFanins = Type == GLS_LUT4 ? Vec_IntEntryP(p->vLut4s, 4*Index) : Vec_IntEntryP(p->vLut6s, 6*Index);
            word Truth = Type == GLS_LUT4 ? (word)Vec_IntEntry(p->vLut4TTs, Index) : Vec_WrdEntry(p->vLut6TTs, Index);
            Vec_IntClear( vLeaves );
            for ( k = 0; k < Limit; k++ )
                Vec_IntPush( vLeaves, pFanins[k] == GLS_NONE ? 0 : Vec_IntEntry(vMap, pFanins[k]) );
            iLit = Kit_TruthToGia( pGia, (unsigned *)&Truth, Vec_IntSize(vLeaves), vCover, vLeaves, 0 );
            Vec_IntWriteEntry( vMap, iObj, iLit );
        }
        else if ( Type == GLS_BAR || Type == GLS_SEL )
        {
            iLit = Vec_IntEntry( vMap, Vec_IntEntry(p->vIndexes, iObj) );
            Vec_IntWriteEntry( vMap, iObj, iLit );
        }
    }
    // delay inputs
    Vec_IntForEachEntry( p->vOrderDelays, iObj, Index )
    {
        vArray = Vec_WecEntry(p->vDelayIns, Index);
        assert( Vec_IntSize(vArray) > 0 );
        Vec_IntForEachEntry( vArray, iObj, k )
            Gia_ManAppendCo( pGia, Vec_IntEntry(vMap, iObj) );
    }
    // create primary outputs
    Vec_IntForEachEntry( p->vOrderPos, iObj, k )
        Gia_ManAppendCo( pGia, Vec_IntEntry(vMap, Vec_IntEntry(p->vIndexes, iObj)) );
    // create sequential nodes
    Vec_IntForEachEntry( p->vOrderSeqs, iObj, k )
        Gia_ManAppendCo( pGia, Vec_IntEntry(vMap, Vec_IntEntry(p->vIndexes, iObj)) );
    Vec_IntFree( vMap );
    Vec_IntFree( vCover );
    Vec_IntFree( vLeaves );
    // print delay boxes
//    for ( k = 0; k < Vec_IntSize(p->vDelays); k++ )
//        printf( "%d:%d  ", Vec_IntSize(Vec_WecEntry(p->vDelayIns, k)), Vec_IntSize(Vec_WecEntry(p->vDelayOuts, k)) );
//    printf( "\n" );
    return pGia;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManReadGig( char * pFileName )
{
    abctime clk = Abc_Clock();
    Gls_Man_t * p = NULL;
    Gia_Man_t * pGia = NULL;
    Vec_Str_t * vLines;
    int i, pCounts[GLS_FINAL];
    FILE * pFile = fopen( pFileName, "rb" );
    if ( pFile == NULL )
    {
        printf( "Cannot read file \"%s\".\n", pFileName );
        return NULL;
    }
    vLines = Gls_ManCount( pFile, pCounts );
    rewind( pFile );
    // statistics
    for ( i = 0; i < GLS_FINAL; i++ )
        if ( pCounts[i] )
            printf( "%s=%d  ", s_Strs[i], pCounts[i] );
    Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
    // collect data and derive AIG
    p = Gls_ManAlloc( vLines, pCounts );
    if ( Gls_ManParse( pFile, p ) )
        pGia = Gls_ManConstruct( p, pFileName );
    Gls_ManStop( p );
    fclose( pFile );
    //printf( "\n" );
    return pGia;
}

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


ABC_NAMESPACE_IMPL_END