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

  FileName    [amapRead.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Technology mapper for standard cells.]

  Synopsis    []

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "amapInt.h"
#include "base/io/ioAbc.h"

ABC_NAMESPACE_IMPL_START


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

#define AMAP_STRING_GATE       "GATE"
#define AMAP_STRING_PIN        "PIN"
#define AMAP_STRING_NONINV     "NONINV"
#define AMAP_STRING_INV        "INV"
#define AMAP_STRING_UNKNOWN    "UNKNOWN"

// these symbols (and no other) can appear in the formulas
#define AMAP_SYMB_AND    '*'
#define AMAP_SYMB_AND2   '&'
#define AMAP_SYMB_OR1    '+'
#define AMAP_SYMB_OR2    '|'
#define AMAP_SYMB_XOR    '^'
#define AMAP_SYMB_NOT    '!'
#define AMAP_SYMB_AFTNOT '\''
#define AMAP_SYMB_OPEN   '('
#define AMAP_SYMB_CLOSE  ')'

typedef enum { 
    AMAP_PHASE_UNKNOWN, 
    AMAP_PHASE_INV, 
    AMAP_PHASE_NONINV 
} Amap_PinPhase_t;

static inline Amap_Gat_t * Amap_ParseGateAlloc( Aig_MmFlex_t * p, int nPins ) 
{ return (Amap_Gat_t *)Aig_MmFlexEntryFetch( p, sizeof(Amap_Gat_t)+sizeof(Amap_Pin_t)*nPins ); }
static inline char * Amap_ParseStrsav( Aig_MmFlex_t * p, char * pStr ) 
{ return pStr ? strcpy(Aig_MmFlexEntryFetch(p, strlen(pStr)+1), pStr) : NULL; }

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

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

  Synopsis    [Loads the file into temporary buffer.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
char * Amap_LoadFile( char * pFileName )
{
//    extern FILE * Io_FileOpen( const char * FileName, const char * PathVar, const char * Mode, int fVerbose );
    FILE * pFile;
    char * pBuffer;
    int nFileSize;
    int RetValue;
    // open the BLIF file for binary reading
    pFile = Io_FileOpen( pFileName, "open_path", "rb", 1 );
//    pFile = fopen( FileName, "rb" );
    // if we got this far, file should be okay otherwise would
    // have been detected by caller
    if ( pFile == NULL )
    {
        printf( "Cannot open file \"%s\".\n", pFileName );
        return NULL;
    }
    assert ( pFile != NULL );
    // get the file size, in bytes
    fseek( pFile, 0, SEEK_END );  
    nFileSize = ftell( pFile );  
    // move the file current reading position to the beginning
    rewind( pFile ); 
    // load the contents of the file into memory
    pBuffer = ABC_ALLOC( char, nFileSize + 10 );
    RetValue = fread( pBuffer, nFileSize, 1, pFile );
    // terminate the string with '\0'
    pBuffer[ nFileSize ] = '\0';
    strcat( pBuffer, "\n.end\n" );
    // close file
    fclose( pFile );
    return pBuffer;
}

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

  Synopsis    [Eliminates comments from the input file.]

  Description [As a byproduct, this procedure also counts the number
  lines and dot-statements in the input file. This also joins non-comment 
  lines that are joined with a backspace '\']
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Amap_RemoveComments( char * pBuffer, int * pnDots, int * pnLines )
{
    char * pCur;
    int nDots, nLines;
    // scan through the buffer and eliminate comments
    // (in the BLIF file, comments are lines starting with "#")
    nDots = nLines = 0;
    for ( pCur = pBuffer; *pCur; pCur++ )
      {
        // if this is the beginning of comment
        // clean it with spaces until the new line statement
        if ( *pCur == '#' ) {
          while ( *pCur != '\n' ) {
            *pCur++ = ' ';
          }
        }    
        // count the number of new lines and dots
        if ( *pCur == '\n' ) {
          if (pCur > pBuffer) {
            if (*(pCur - 1) == '\r') {
              // DOS(R) file support
              if (pCur > (pBuffer + 1)) {
                if (*(pCur - 2)!='\\') {
                  nLines++;
                }
                else {
                  // rewind to backslash and overwrite with a space
                  *(pCur - 2) = ' ';
                  *(pCur - 1) = ' ';
                  *pCur = ' ';
                }
              }
            } else {
              // UNIX(TM) file support
              if (*(pCur - 1) != '\\') {
                nLines++;
              }
              else {
                // rewind to backslash and overwrite with a space
                *(pCur-1) = ' ';
                *pCur = ' ';
              }
            }
          }
        }
        else if ( *pCur == '.' ) {
          nDots++;
        }
      }

    if ( pnDots )
      *pnDots = nDots; 
    if ( pnLines )
      *pnLines = nLines; 
}

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

  Synopsis    [Splits the stream into tokens.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Amap_DeriveTokens( char * pBuffer )
{
    Vec_Ptr_t * vTokens;
    char * pToken;
    vTokens = Vec_PtrAlloc( 1000 );
    pToken = strtok( pBuffer, " =\t\r\n" );
    while ( pToken )
    {
        Vec_PtrPush( vTokens, pToken );
        pToken = strtok( NULL, " =\t\r\n" );
        // skip latches
        if ( pToken && strcmp( pToken, "LATCH" ) == 0 )
            while ( pToken && strcmp( pToken, "GATE" ) != 0 )
                pToken = strtok( NULL, " =\t\r\n" );
    }
    return vTokens;
}

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

  Synopsis    [Finds the number of pins.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Amap_ParseCountPins( Vec_Ptr_t * vTokens, int iPos )
{
    char * pToken;
    int i, Counter = 0;
    Vec_PtrForEachEntryStart( char *, vTokens, pToken, i, iPos )
        if ( !strcmp( pToken, AMAP_STRING_PIN ) )
            Counter++;
        else if ( !strcmp( pToken, AMAP_STRING_GATE ) )
            return Counter;
    return Counter;
}

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

  Synopsis    [Collect the pin names used in the formula.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Amap_GateCollectNames( Aig_MmFlex_t * pMem, char * pForm, char * pPinNames[] )
{
    char Buffer[1000];
    char * pTemp;
    int nPins, i;
    // save the formula as it was
    strcpy( Buffer, pForm );
    // remove the non-name symbols
    for ( pTemp = Buffer; *pTemp; pTemp++ )
        if ( *pTemp == AMAP_SYMB_AND || *pTemp == AMAP_SYMB_OR1 || *pTemp == AMAP_SYMB_OR2 
          || *pTemp == AMAP_SYMB_XOR || *pTemp == AMAP_SYMB_NOT || *pTemp == AMAP_SYMB_OPEN 
          || *pTemp == AMAP_SYMB_CLOSE || *pTemp == AMAP_SYMB_AFTNOT || *pTemp == AMAP_SYMB_AND2 )
            *pTemp = ' ';
    // save the names
    nPins = 0;
    pTemp = strtok( Buffer, " " );
    while ( pTemp )
    {
        for ( i = 0; i < nPins; i++ )
            if ( strcmp( pTemp, pPinNames[i] ) == 0 )
                break;
        if ( i == nPins )
        { // cannot find this name; save it
            pPinNames[nPins++] = Amap_ParseStrsav( pMem, pTemp );
        }
        // get the next name
        pTemp = strtok( NULL, " " );
    }
    return nPins;
}

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

  Synopsis    [Creates a duplicate gate with pins specified.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Amap_Gat_t * Amap_ParseGateWithSamePins( Amap_Gat_t * p )
{
    Amap_Gat_t * pGate;
    Amap_Pin_t * pPin;
    char * pPinNames[128];
    int nPinNames;
    assert( p->nPins == 1 && !strcmp( p->Pins->pName, "*" ) );
    nPinNames = Amap_GateCollectNames( p->pLib->pMemGates, p->pForm, pPinNames );
    pGate = Amap_ParseGateAlloc( p->pLib->pMemGates, nPinNames );
    *pGate = *p;
    pGate->nPins = nPinNames;
    Amap_GateForEachPin( pGate, pPin )
    {
        *pPin = *p->Pins;
        pPin->pName = pPinNames[pPin - pGate->Pins];
    }
    return pGate;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Amap_CollectFormulaTokens( Vec_Ptr_t * vTokens, char * pToken, int iPos )
{
    char * pNext, * pPrev;
    pPrev = pToken + strlen(pToken);
    while ( *(pPrev-1) != ';' )
    {
        *pPrev++ = ' ';
        pNext = (char *)Vec_PtrEntry(vTokens, iPos++);
        while ( *pNext )
            *pPrev++ = *pNext++;
    }
    *(pPrev-1) = 0;
    return iPos;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Amap_Lib_t * Amap_ParseTokens( Vec_Ptr_t * vTokens, int fVerbose )
{
    Amap_Lib_t * p;
    Amap_Gat_t * pGate, * pPrev;
    Amap_Pin_t * pPin;
    char * pToken, * pMoGate = NULL;
    int i, nPins, iPos = 0, Count = 0;
    p = Amap_LibAlloc();
    pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
    do 
    {
        if ( strcmp( pToken, AMAP_STRING_GATE ) )
        {
            Amap_LibFree( p );
            printf( "The first line should begin with %s.\n", AMAP_STRING_GATE );
            return NULL;
        }
        // start gate
        nPins = Amap_ParseCountPins( vTokens, iPos );
        pGate = Amap_ParseGateAlloc( p->pMemGates, nPins );
        memset( pGate, 0, sizeof(Amap_Gat_t) );
        pGate->Id = Vec_PtrSize( p->vGates );
        Vec_PtrPush( p->vGates, pGate );
        pGate->pLib = p;
        pGate->nPins = nPins;
        // read gate
        pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
        pGate->pName = Amap_ParseStrsav( p->pMemGates, pToken );    
        pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
        pGate->dArea = atof( pToken );
        pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
        pGate->pOutName = Amap_ParseStrsav( p->pMemGates, pToken ); 
        pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
        iPos = Amap_CollectFormulaTokens( vTokens, pToken, iPos );
        pGate->pForm = Amap_ParseStrsav( p->pMemGates, pToken ); 
        // read pins
        Amap_GateForEachPin( pGate, pPin )
        {
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            if ( strcmp( pToken, AMAP_STRING_PIN ) )
            {
                Amap_LibFree( p );
                printf( "Cannot parse gate %s.\n", pGate->pName );
                return NULL;
            }
            // read pin
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->pName = Amap_ParseStrsav( p->pMemGates, pToken );   
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            if ( strcmp( pToken, AMAP_STRING_UNKNOWN ) == 0 )
                pPin->Phase = AMAP_PHASE_UNKNOWN;
            else if ( strcmp( pToken, AMAP_STRING_INV ) == 0 )
                pPin->Phase = AMAP_PHASE_INV;
            else if ( strcmp( pToken, AMAP_STRING_NONINV ) == 0 )
                pPin->Phase = AMAP_PHASE_NONINV;
            else 
            {
                Amap_LibFree( p );
                printf( "Cannot read phase of pin %s of gate %s\n", pPin->pName, pGate->pName );
                return NULL;
            }
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dLoadInput = atof( pToken );
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dLoadMax = atof( pToken );
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dDelayBlockRise = atof( pToken );
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dDelayFanoutRise = atof( pToken );
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dDelayBlockFall = atof( pToken );
            pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
            pPin->dDelayFanoutFall = atof( pToken );
            if ( pPin->dDelayBlockRise > pPin->dDelayBlockFall )
                pPin->dDelayBlockMax = pPin->dDelayBlockRise;
            else
                pPin->dDelayBlockMax = pPin->dDelayBlockFall;
        }
        // fix the situation when all pins are represented as one
        if ( pGate->nPins == 1 && !strcmp( pGate->Pins->pName, "*" ) )
        {
            pGate = Amap_ParseGateWithSamePins( pGate );
            Vec_PtrPop( p->vGates );
            Vec_PtrPush( p->vGates, pGate );
        }
        pToken = (char *)Vec_PtrEntry(vTokens, iPos++);
//printf( "Finished reading gate %s (%s)\n", pGate->pName, pGate->pOutName );
    }
    while ( strcmp( pToken, ".end" ) );

    // check if there are gates with identical names
    pPrev = NULL;
    Amap_LibForEachGate( p, pGate, i )
    {
        if ( pPrev && !strcmp(pPrev->pName, pGate->pName) )
        {
            pPrev->pTwin = pGate, pGate->pTwin = pPrev;
//            printf( "Warning: Detected multi-output gate \"%s\".\n", pGate->pName );
            if ( pMoGate == NULL )
                pMoGate = pGate->pName;
            Count++;
        }
        pPrev = pGate;
    }
    if ( Count )
        printf( "Warning: Detected %d multi-output gates (for example, \"%s\").\n", Count, pMoGate );
    return p;
}

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

  Synopsis    [Reads the library from the input file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Amap_Lib_t * Amap_LibReadBuffer( char * pBuffer, int fVerbose )
{
    Amap_Lib_t * pLib;
    Vec_Ptr_t * vTokens;
    Amap_RemoveComments( pBuffer, NULL, NULL );
    vTokens = Amap_DeriveTokens( pBuffer );
    pLib = Amap_ParseTokens( vTokens, fVerbose );
    if ( pLib == NULL )
    {
        Vec_PtrFree( vTokens );
        return NULL;
    }
    Vec_PtrFree( vTokens );
    return pLib;
}

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

  Synopsis    [Reads the library from the input file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Amap_Lib_t * Amap_LibReadFile( char * pFileName, int fVerbose )
{
    Amap_Lib_t * pLib;
    char * pBuffer;
    pBuffer = Amap_LoadFile( pFileName );
    if ( pBuffer == NULL )
        return NULL;
    pLib = Amap_LibReadBuffer( pBuffer, fVerbose );
    if ( pLib )
        pLib->pName = Abc_UtilStrsav( pFileName );
    ABC_FREE( pBuffer );
    return pLib;
}

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


ABC_NAMESPACE_IMPL_END
