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

  FileName    [ioWriteSmv.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Command processing package.]

  Synopsis    [Procedures to write the network in SMV format.]

  Author      [Satrajit Chatterjee]
  
  Affiliation [UC Berkeley]

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

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

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

#include "ioAbc.h"

ABC_NAMESPACE_IMPL_START


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

static int Io_WriteSmvCheckNames( Abc_Ntk_t * pNtk );

static int Io_WriteSmvOne( FILE * pFile, Abc_Ntk_t * pNtk );
static int Io_WriteSmvOneNode( FILE * pFile, Abc_Obj_t * pNode );

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

// This returns a pointer to a static area, so be careful in using results
// of this function i.e. don't call this twice in the same printf call.
//
// This function replaces '|' with '_' I think abc introduces '|' when
// flattening hierarchy. The '|' is interpreted as a or function by nusmv
// which is unfortunate. This probably should be fixed elsewhere.
static char *cleanUNSAFE( const char *s )
{
    char *t;
    static char buffer[1024];
    assert (strlen(s) < 1024);
    strcpy(buffer, s);
    for (t = buffer; *t != 0; ++t) *t = (*t == '|') ? '_' : *t;
    return buffer;
}

static int hasPrefix(const char *needle, const char *haystack)
{
    return (strncmp(haystack, needle, strlen(needle)) == 0);
}

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

  Synopsis    [Writes the network in SMV format.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Io_WriteSmv( Abc_Ntk_t * pNtk, char * pFileName )
{
    Abc_Ntk_t * pExdc;
    FILE * pFile;
    assert( Abc_NtkIsSopNetlist(pNtk) );
    if ( !Io_WriteSmvCheckNames(pNtk) )
    {
        fprintf( stdout, "Io_WriteSmv(): Signal names in this benchmark contain parentheses making them impossible to reproduce in the SMV format. Use \"short_names\".\n" );
        return 0;
    }
    pFile = fopen( pFileName, "w" );
    if ( pFile == NULL )
    {
        fprintf( stdout, "Io_WriteSmv(): Cannot open the output file.\n" );
        return 0;
    }
    fprintf( pFile, "-- benchmark \"%s\" written by ABC on %s\n", pNtk->pName, Extra_TimeStamp() );
    // write the network
    Io_WriteSmvOne( pFile, pNtk );
    // write EXDC network if it exists
    pExdc = Abc_NtkExdc( pNtk );
    if ( pExdc )
        printf( "Io_WriteSmv: EXDC is not written (warning).\n" );
    // finalize the file
    fclose( pFile );
    return 1;
}

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

  Synopsis    [Writes the network in SMV format.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Io_WriteSmvOne( FILE * pFile, Abc_Ntk_t * pNtk )
{
    ProgressBar * pProgress;
    Abc_Obj_t * pNode;
    int i;

    // write the PIs/POs/latches
    fprintf( pFile, "MODULE main\n");   // nusmv needs top module to be main
    fprintf ( pFile, "\n" );

    fprintf( pFile, "VAR  -- inputs\n");
    Abc_NtkForEachPi( pNtk, pNode, i )
        fprintf( pFile, "    %s : boolean;\n", 
                cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(pNode))) );
    fprintf ( pFile, "\n" );

    fprintf( pFile, "VAR  -- state variables\n");
    Abc_NtkForEachLatch( pNtk, pNode, i )
        fprintf( pFile, "    %s : boolean;\n", 
                cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pNode)))) ); 
    fprintf ( pFile, "\n" );

    // No outputs needed for NuSMV: 
    // TODO: Add sepcs by recognizing assume_.* and assert_.*
    //
    // Abc_NtkForEachPo( pNtk, pNode, i )
    //    fprintf( pFile, "OUTPUT(%s)\n", Abc_ObjName(Abc_ObjFanin0(pNode)) );
    
    // write internal nodes
    fprintf( pFile, "DEFINE\n");
    pProgress = Extra_ProgressBarStart( stdout, Abc_NtkObjNumMax(pNtk) );
    Abc_NtkForEachNode( pNtk, pNode, i )
    {
        Extra_ProgressBarUpdate( pProgress, i, NULL );
        Io_WriteSmvOneNode( pFile, pNode );
    }
    Extra_ProgressBarStop( pProgress );
    fprintf ( pFile, "\n" );

    fprintf( pFile, "ASSIGN\n");
    Abc_NtkForEachLatch( pNtk, pNode, i )
    {
        int Reset = (int)(ABC_PTRUINT_T)Abc_ObjData( pNode );
        assert (Reset >= 1);
        assert (Reset <= 3);

        if (Reset != 3)
        {
            fprintf( pFile, "    init(%s) := %d;\n", 
                cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pNode)))),
                Reset - 1); 
        }
        fprintf( pFile, "    next(%s) := ", 
                cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(Abc_ObjFanout0(pNode)))) ); 
        fprintf( pFile, "%s;\n", 
                cleanUNSAFE(Abc_ObjName(Abc_ObjFanin0(Abc_ObjFanin0(pNode)))) );
    }
      
    fprintf ( pFile, "\n" );
    Abc_NtkForEachPo( pNtk, pNode, i )
    {
        const char *n = cleanUNSAFE(Abc_ObjName(Abc_ObjFanin0(pNode)));
        // fprintf( pFile, "-- output %s;\n", n );
        if (hasPrefix("assume_fair_", n))
        {
            fprintf( pFile, "FAIRNESS %s;\n", n );
        }
        else if (hasPrefix("Assert_", n) || 
                hasPrefix("assert_safety_", n))
        {
            fprintf( pFile, "INVARSPEC %s;\n", n );
        }
        else if (hasPrefix("assert_fair_", n))
        {
            fprintf( pFile, "LTLSPEC G F %s;\n", n );
        }
    }

    return 1;
}

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

  Synopsis    [Writes the network in SMV format.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Io_WriteSmvOneNode( FILE * pFile, Abc_Obj_t * pNode )
{
    int nFanins;

    assert( Abc_ObjIsNode(pNode) );
    nFanins = Abc_ObjFaninNum(pNode);
    if ( nFanins == 0 )
    {   // write the constant 1 node
        assert( Abc_NodeIsConst1(pNode) );
        fprintf( pFile, "    %s", cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(pNode)) ) );
        fprintf( pFile, " := 1;\n" );
    }
    else if ( nFanins == 1 )
    {   // write the interver/buffer
        if ( Abc_NodeIsBuf(pNode) )
        {
            fprintf( pFile, "    %s := ", cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(pNode))) );
            fprintf( pFile, "%s;\n",      cleanUNSAFE(Abc_ObjName(Abc_ObjFanin0(pNode))) );
        }
        else
        {
            fprintf( pFile, "    %s := !",  cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(pNode))) );
            fprintf( pFile, "%s;\n",       cleanUNSAFE(Abc_ObjName(Abc_ObjFanin0(pNode))) );
        }
    }
    else
    {   // write the AND gate
        fprintf( pFile, "    %s", cleanUNSAFE(Abc_ObjName(Abc_ObjFanout0(pNode))) );
        fprintf( pFile, " := %s & ", cleanUNSAFE(Abc_ObjName(Abc_ObjFanin0(pNode))) );
        fprintf( pFile, "%s;\n", cleanUNSAFE(Abc_ObjName(Abc_ObjFanin1(pNode))) );
    }
    return 1;
}

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

  Synopsis    [Returns 1 if the names cannot be written into the bench file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Io_WriteSmvCheckNames( Abc_Ntk_t * pNtk )
{
    Abc_Obj_t * pObj;
    char * pName;
    int i;
    Abc_NtkForEachObj( pNtk, pObj, i )
        for ( pName = Nm_ManFindNameById(pNtk->pManName, i); pName && *pName; pName++ )
            if ( *pName == '(' || *pName == ')' )
                return 0;
    return 1;
}

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


ABC_NAMESPACE_IMPL_END