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

  FileName    [minilut.h]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Minimalistic representation of LUT mapped network.]

  Synopsis    [External declarations.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - September 29, 2012.]

  Revision    [$Id: minilut.h,v 1.00 2012/09/29 00:00:00 alanmi Exp $]

***********************************************************************/
 
#ifndef MINI_LUT__mini_lut_h
#define MINI_LUT__mini_lut_h

////////////////////////////////////////////////////////////////////////
///                          INCLUDES                                ///
////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

ABC_NAMESPACE_HEADER_START

////////////////////////////////////////////////////////////////////////
///                         PARAMETERS                               ///
////////////////////////////////////////////////////////////////////////

#define MINI_LUT_NULL       (0x7FFFFFFF)
#define MINI_LUT_NULL2      (0x7FFFFFFE)
#define MINI_LUT_START_SIZE (0x000000FF)

////////////////////////////////////////////////////////////////////////
///                         BASIC TYPES                              ///
////////////////////////////////////////////////////////////////////////

typedef struct Mini_Lut_t_       Mini_Lut_t;
struct Mini_Lut_t_ 
{
    int           nCap;
    int           nSize;
    int           nRegs;
    int           LutSize;
    int *         pArray;
    unsigned *    pTruths;
};

////////////////////////////////////////////////////////////////////////
///                      MACRO DEFINITIONS                           ///
////////////////////////////////////////////////////////////////////////

// memory management
#define MINI_LUT_ALLOC(type, num)     ((type *) malloc(sizeof(type) * (num)))
#define MINI_LUT_CALLOC(type, num)    ((type *) calloc((num), sizeof(type)))
#define MINI_LUT_FALLOC(type, num)    ((type *) memset(malloc(sizeof(type) * (num)), 0xff, sizeof(type) * (num)))
#define MINI_LUT_FREE(obj)            ((obj) ? (free((char *) (obj)), (obj) = 0) : 0)
#define MINI_LUT_REALLOC(type, obj, num) \
        ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (num))) : \
         ((type *) malloc(sizeof(type) * (num))))

// compute truth table size measured in unsigned's
static int  Mini_LutWordNum( int LutSize ) 
{
    return LutSize > 5 ? 1 << (LutSize-5) : 1;
}

// internal procedures
static void Mini_LutGrow( Mini_Lut_t * p, int nCapMin )
{
    if ( p->nCap >= nCapMin )
        return;
    p->pArray  = MINI_LUT_REALLOC( int,      p->pArray,  nCapMin * p->LutSize ); 
    p->pTruths = MINI_LUT_REALLOC( unsigned, p->pTruths, nCapMin * Mini_LutWordNum(p->LutSize) ); 
    p->nCap   = nCapMin;
    assert( p->pArray );
    assert( p->pTruths );
}
static void Mini_LutPush( Mini_Lut_t * p, int nVars, int * pVars, unsigned * pTruth )
{
    int i, nWords = Mini_LutWordNum(p->LutSize);
    if ( p->nSize == p->nCap )
    {
        assert( p->LutSize*p->nSize < MINI_LUT_NULL/2 );
        if ( p->nCap < MINI_LUT_START_SIZE )
            Mini_LutGrow( p, MINI_LUT_START_SIZE );
        else
            Mini_LutGrow( p, 2 * p->nCap );
    }
    for ( i = 0; i < nVars; i++ )
        assert( pVars[i] >= 0 && pVars[i] < p->nSize );
    for ( i = 0; i < nVars; i++ )
        p->pArray[p->LutSize * p->nSize + i] = pVars[i];
    for ( ; i < p->LutSize; i++ )
        p->pArray[p->LutSize * p->nSize + i] = MINI_LUT_NULL;
    for ( i = 0; i < nWords; i++ )
        p->pTruths[nWords * p->nSize + i] = pTruth? pTruth[i] : 0;
    p->nSize++;
}

// accessing fanins
static int Mini_LutNodeFanin( Mini_Lut_t * p, int Id, int k )
{
    assert( Id >= 0 && Id < p->nSize );
    return p->pArray[p->LutSize*Id+k];
}
static unsigned * Mini_LutNodeTruth( Mini_Lut_t * p, int Id )    
{ 
    assert( Id >= 0 && Id < p->nSize ); 
    return p->pTruths + Id * Mini_LutWordNum(p->LutSize);
}

// working with LUTs
static int      Mini_LutNodeConst0()                           { return 0;                    }
static int      Mini_LutNodeConst1()                           { return 1;                    }

static int      Mini_LutNodeNum( Mini_Lut_t * p )              { return p->nSize;             }
static int      Mini_LutNodeIsConst( Mini_Lut_t * p, int Id )  { assert( Id >= 0 ); return Id == 0 || Id == 1; }
static int      Mini_LutNodeIsPi( Mini_Lut_t * p, int Id )     { assert( Id >= 0 ); return Id > 1 && Mini_LutNodeFanin( p, Id, 0 ) == MINI_LUT_NULL; }
static int      Mini_LutNodeIsPo( Mini_Lut_t * p, int Id )     { assert( Id >= 0 ); return Id > 1 && Mini_LutNodeFanin( p, Id, 0 ) != MINI_LUT_NULL && Mini_LutNodeFanin( p, Id, 1 ) == MINI_LUT_NULL2; }
static int      Mini_LutNodeIsNode( Mini_Lut_t * p, int Id )   { assert( Id >= 0 ); return Id > 1 && Mini_LutNodeFanin( p, Id, 0 ) != MINI_LUT_NULL && Mini_LutNodeFanin( p, Id, 1 ) != MINI_LUT_NULL2; }

static int      Mini_LutSize( Mini_Lut_t * p )                 { return p->LutSize;           }

// working with sequential AIGs
static int      Mini_LutRegNum( Mini_Lut_t * p )               { return p->nRegs;             }
static void     Mini_LutSetRegNum( Mini_Lut_t * p, int n )     { p->nRegs = n;                }

// iterators through objects
#define Mini_LutForEachPi( p, i )    for (i = 2; i < Mini_LutNodeNum(p); i++) if ( !Mini_LutNodeIsPi(p, i) )   {} else 
#define Mini_LutForEachPo( p, i )    for (i = 2; i < Mini_LutNodeNum(p); i++) if ( !Mini_LutNodeIsPo(p, i) )   {} else 
#define Mini_LutForEachNode( p, i )  for (i = 2; i < Mini_LutNodeNum(p); i++) if ( !Mini_LutNodeIsNode(p, i) ) {} else

// iterator through fanins
#define Mini_LutForEachFanin( p, i, Fan, k ) for (k = 0; (k < p->LutSize) && (Fan = Mini_LutNodeFanin(p, i, k)) < MINI_LUT_NULL2; k++) 

// constructor/destructor
static Mini_Lut_t * Mini_LutStart( int LutSize )
{
    Mini_Lut_t * p; int i;
    assert( LutSize >= 2 && LutSize <= 16 );
    p = MINI_LUT_CALLOC( Mini_Lut_t, 1 );
    p->LutSize = LutSize;
    p->nCap    = MINI_LUT_START_SIZE;
    p->pArray  = MINI_LUT_ALLOC( int,      p->nCap * p->LutSize );
    p->pTruths = MINI_LUT_ALLOC( unsigned, p->nCap * Mini_LutWordNum(p->LutSize) );
    Mini_LutPush( p, 0, NULL, NULL ); // const0
    Mini_LutPush( p, 0, NULL, NULL ); // const1
    for ( i = 0; i < Mini_LutWordNum(p->LutSize); i++ )
        p->pTruths[i] = 0;
    for ( i = 0; i < Mini_LutWordNum(p->LutSize); i++ )
        p->pTruths[Mini_LutWordNum(p->LutSize) + i] = ~0;
    return p;
}
static void Mini_LutStop( Mini_Lut_t * p )
{
    MINI_LUT_FREE( p->pArray );
    MINI_LUT_FREE( p->pTruths );
    MINI_LUT_FREE( p );
}
static void Mini_LutPrintStats( Mini_Lut_t * p )
{
    int i, nPis, nPos, nNodes;
    nPis = 0;
    Mini_LutForEachPi( p, i )
        nPis++;
    nPos = 0;
    Mini_LutForEachPo( p, i )
        nPos++;
    nNodes = 0;
    Mini_LutForEachNode( p, i )
        nNodes++;
    printf( "PI = %d. PO = %d. LUT = %d. FF = %d.\n", nPis, nPos, nNodes, p->nRegs );
}
static void Mini_LutPrint( Mini_Lut_t * p )
{
    int i, k, Fan;
    printf( "MiniLUT statistics: " );
    Mini_LutPrintStats( p );
    printf( "Printout of nodes:\n" );
    for ( i = 0; i < p->nSize; i++ )
    {
        printf( "%6d : ", i );
        if ( Mini_LutNodeIsConst(p, i) )
            printf( "Const%d", i );
        else if ( Mini_LutNodeIsPi(p, i) )
            printf( "PI" );
        else if ( Mini_LutNodeIsPo(p, i) )
            printf( "PO" );
        else if ( Mini_LutNodeIsNode(p, i) )
        {
            printf( "LUT%d  Fanins:", p->LutSize );
            Mini_LutForEachFanin( p, i, Fan, k )
                printf( " %6d", Fan );
            while ( k++ < p->LutSize )
                printf( "       " );
            printf( "   Function: " );
            for ( k = 31; k >= 0; k-- )
                printf( "%c", '0' + ((p->pTruths[i] >> k) & 1) );
        }
        printf( "\n" );
    }
    printf( "End of printout.\n" );
}

// serialization
static void Mini_LutDump( Mini_Lut_t * p, char * pFileName )
{
    FILE * pFile;
    int RetValue;
    pFile = fopen( pFileName, "wb" );
    if ( pFile == NULL )
    {
        printf( "Cannot open file for writing \"%s\".\n", pFileName );
        return;
    }
    RetValue = (int)fwrite( &p->nSize,   sizeof(int), 1, pFile );
    RetValue = (int)fwrite( &p->nRegs,   sizeof(int), 1, pFile );
    RetValue = (int)fwrite( &p->LutSize, sizeof(int), 1, pFile );
    RetValue = (int)fwrite( p->pArray,   sizeof(int), p->nSize * p->LutSize, pFile );
    RetValue = (int)fwrite( p->pTruths,  sizeof(int), p->nSize * Mini_LutWordNum(p->LutSize), pFile );
    fclose( pFile );
}
static Mini_Lut_t * Mini_LutLoad( char * pFileName )
{
    Mini_Lut_t * p;
    FILE * pFile;
    int RetValue, nSize;
    pFile = fopen( pFileName, "rb" );
    if ( pFile == NULL )
    {
        printf( "Cannot open file for reading \"%s\".\n", pFileName );
        return NULL;
    }
    RetValue = (int)fread( &nSize, sizeof(int), 1, pFile );
    p = MINI_LUT_CALLOC( Mini_Lut_t, 1 );
    p->nSize   = p->nCap = nSize;
    RetValue = (int)fread( &p->nRegs,   sizeof(int), 1, pFile );
    RetValue = (int)fread( &p->LutSize, sizeof(int), 1, pFile );
    p->pArray  = MINI_LUT_ALLOC( int,      p->nCap * p->LutSize );
    p->pTruths = MINI_LUT_ALLOC( unsigned, p->nCap * Mini_LutWordNum(p->LutSize) );
    RetValue = (int)fread( p->pArray,   sizeof(int), p->nCap * p->LutSize, pFile );
    RetValue = (int)fread( p->pTruths,  sizeof(int), p->nCap * Mini_LutWordNum(p->LutSize), pFile );
    fclose( pFile );
    return p;
}


// creating nodes 
// (constant nodes are created when LUT manager is created)
static int Mini_LutCreatePi( Mini_Lut_t * p )
{
    Mini_LutPush( p, 0, NULL, NULL );
    return p->nSize - 1;
}
static int Mini_LutCreatePo( Mini_Lut_t * p, int Var0 )
{
    assert( Var0 >= 0 && Var0 < p->nSize );
    Mini_LutPush( p, 1, &Var0, NULL );
    // mark PO by setting its 2nd fanin to the special number
    p->pArray[p->LutSize*(p->nSize - 1)+1] = MINI_LUT_NULL2;
    return p->nSize - 1;
}

// create LUT
static int Mini_LutCreateNode( Mini_Lut_t * p, int nVars, int * pVars, unsigned * pTruth )
{
    assert( nVars >= 0 && nVars <= p->LutSize );
    Mini_LutPush( p, nVars, pVars, pTruth );
    return p->nSize - 1;
}

// procedure to check the topological order during AIG construction
static int Mini_LutCheck( Mini_Lut_t * p )
{
    int status = 1;
    int i, k, iFaninVar;
    Mini_LutForEachNode( p, i )
    {
        for ( k = 0; k < p->LutSize; k++ )
        {
            iFaninVar = Mini_LutNodeFanin( p, i, k );
            if ( iFaninVar == MINI_LUT_NULL )
                continue;
            if ( iFaninVar >= p->LutSize * i )
                printf( "Fanin %d of LUT node %d is not in a topological order.\n", k, i ), status = 0;
        }
    }
    Mini_LutForEachPo( p, i )
    {
        iFaninVar = Mini_LutNodeFanin( p, i, 0 );
        if ( iFaninVar >= p->LutSize * i )
            printf( "Fanin %d of PO node %d is not in a topological order.\n", k, i ), status = 0;
    }
    return status;
}



////////////////////////////////////////////////////////////////////////
///                    FUNCTION DECLARATIONS                         ///
////////////////////////////////////////////////////////////////////////

ABC_NAMESPACE_HEADER_END

#endif

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

