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

  FileName    [plaSimple.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SOP manager.]

  Synopsis    [Scalable SOP transformations.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - March 18, 2015.]

  Revision    [$Id: plaSimple.c,v 1.00 2014/09/12 00:00:00 alanmi Exp $]

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

#include "pla.h"

ABC_NAMESPACE_IMPL_START

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

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

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

  Synopsis    [Dump PLA manager into a BLIF file.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Pla_ManDumpPla( Pla_Man_t * p, char * pFileName )
{
    // find the number of original variables
    //int nVarsInit = Pla_ManDivNum(p) ? Vec_IntCountZero(&p->vDivs) : Pla_ManInNum(p);
    FILE * pFile = fopen( pFileName, "wb" );
    if ( pFile == NULL )
        printf( "Cannot open file \"%s\" for writing.\n", pFileName );
    else
    {
        //char * pLits = "-01?";
        Vec_Str_t * vStr;
        Vec_Int_t * vCube;
        int i, k, Lit;
        // comment
        fprintf( pFile, "# PLA file written via PLA package in ABC on " );
        fprintf( pFile, "%s", Extra_TimeStamp() );
        fprintf( pFile, "\n\n" );
        // header
        fprintf( pFile, ".i %d\n", Pla_ManInNum(p) );
        fprintf( pFile, ".o %d\n", 1 );
        fprintf( pFile, ".p %d\n", Vec_WecSize(&p->vCubeLits) );
        // SOP
        vStr = Vec_StrStart( Pla_ManInNum(p) + 1 );
        Vec_WecForEachLevel( &p->vCubeLits, vCube, i )
        {
            if ( !Vec_IntSize(vCube) )
                continue;
            for ( k = 0; k < Pla_ManInNum(p); k++ )
                Vec_StrWriteEntry( vStr, k, '-' );
            Vec_IntForEachEntry( vCube, Lit, k )
                Vec_StrWriteEntry( vStr, Abc_Lit2Var(Lit), (char)(Abc_LitIsCompl(Lit) ? '0' : '1') );
            fprintf( pFile, "%s 1\n", Vec_StrArray(vStr) );
        }
        Vec_StrFree( vStr );
        fprintf( pFile, ".e\n\n" );
        fclose( pFile );
        printf( "Written file \"%s\".\n", pFileName );
    }
}
void Pla_ManDumpBlif( Pla_Man_t * p, char * pFileName )
{
    // find the number of original variables
    int nVarsInit = Pla_ManDivNum(p) ? Vec_IntCountZero(&p->vDivs) : Pla_ManInNum(p);
    FILE * pFile = fopen( pFileName, "wb" );
    if ( pFile == NULL )
        printf( "Cannot open file \"%s\" for writing.\n", pFileName );
    else
    {
        //char * pLits = "-01?";
        Vec_Str_t * vStr;
        Vec_Int_t * vCube;
        int i, k, Lit, Div;
        // comment
        fprintf( pFile, "# BLIF file written via PLA package in ABC on " );
        fprintf( pFile, "%s", Extra_TimeStamp() );
        fprintf( pFile, "\n\n" );
        // header
        fprintf( pFile, ".model %s\n", Pla_ManName(p) );
        fprintf( pFile, ".inputs" );
        for ( i = 0; i < nVarsInit; i++ )
            fprintf( pFile, " i%d", i );
        fprintf( pFile, "\n" );
        fprintf( pFile, ".outputs o" );
        fprintf( pFile, "\n" );
        // SOP
        fprintf( pFile, ".names" );
        for ( i = 0; i < Pla_ManInNum(p); i++ )
            fprintf( pFile, " i%d", i );
        fprintf( pFile, " o\n" );
        vStr = Vec_StrStart( Pla_ManInNum(p) + 1 );
        Vec_WecForEachLevel( &p->vCubeLits, vCube, i )
        {
            for ( k = 0; k < Pla_ManInNum(p); k++ )
                Vec_StrWriteEntry( vStr, k, '-' );
            Vec_IntForEachEntry( vCube, Lit, k )
                Vec_StrWriteEntry( vStr, Abc_Lit2Var(Lit), (char)(Abc_LitIsCompl(Lit) ? '0' : '1') );
            fprintf( pFile, "%s 1\n", Vec_StrArray(vStr) );
        }
        Vec_StrFree( vStr );
        // divisors
        Vec_IntForEachEntryStart( &p->vDivs, Div, i, nVarsInit )
        {
            int pLits[3] = { (Div >> 2) & 0x3FF, (Div >> 12) & 0x3FF, (Div >> 22) & 0x3FF };
            fprintf( pFile, ".names" );
            fprintf( pFile, " i%d", Abc_Lit2Var(pLits[0]) );
            fprintf( pFile, " i%d", Abc_Lit2Var(pLits[1]) );
            if ( (Div & 3) == 3 ) // MUX
                fprintf( pFile, " i%d", Abc_Lit2Var(pLits[2]) );
            fprintf( pFile, " i%d\n", i );
            if ( (Div & 3) == 1 ) // AND
                fprintf( pFile, "%d%d 1\n", !Abc_LitIsCompl(pLits[0]), !Abc_LitIsCompl(pLits[1]) );
            else if ( (Div & 3) == 2 ) // XOR
            {
                assert( !Abc_LitIsCompl(pLits[0]) );
                assert( !Abc_LitIsCompl(pLits[1]) );
                fprintf( pFile, "10 1\n01 1\n" );
            }
            else if ( (Div & 3) == 3 ) // MUX
            {
                assert( !Abc_LitIsCompl(pLits[1]) );
                assert( !Abc_LitIsCompl(pLits[2]) );
                fprintf( pFile, "%d-0 1\n-11 1\n", !Abc_LitIsCompl(pLits[0]) );
            }
            else assert( 0 );
        }
        fprintf( pFile, ".end\n\n" );
        fclose( pFile );
        printf( "Written file \"%s\".\n", pFileName );
    }
}

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

  Synopsis    [Transforms truth table into an SOP manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Pla_ManExpendDirNum( word * pOn, int nBits, int iMint, int * pVars )
{
    int v, nVars = 0;
    for ( v = 0; v < nBits; v++ )
        if ( Pla_TtGetBit(pOn, iMint ^ (1 << v)) )
            pVars[nVars++] = v;
    return nVars;
}
void Pla_PrintBinary( word * pT, int nBits )
{
    int v;
    for ( v = 0; v < nBits; v++ )
        printf( "%d", Pla_TtGetBit(pT, v) );
    printf( "\n" );
}
Vec_Wrd_t * Pla_ManFxMinimize( word * pOn, int nVars )
{
    int i, v, iMint, iVar, nMints = (1 << nVars);
    int nWords = Abc_Bit6WordNum( nMints );
    Vec_Wrd_t * vCubes = Vec_WrdAlloc( 1000 );
    word * pDc = ABC_CALLOC( word, nWords );
    int Count[32] = {0};
    int Cubes[32] = {0};
    Vec_Int_t * vStore = Vec_IntAlloc( 1000 );

    // count the number of expansion directions
    for ( i = 0; i < nMints; i++ )
        if ( Pla_TtGetBit(pOn, i) && !Pla_TtGetBit(pDc, i) )
        {
            int pDirs[32], nDirs = Pla_ManExpendDirNum(pOn, nVars, i, pDirs);
            Count[nDirs]++;
            if ( nDirs == 0 )
            {
                Pla_TtSetBit(pDc, i);
                Cubes[0]++;
                // save
                Vec_IntPushTwo( vStore, i, -1 );
                continue;
            }
            if ( nDirs == 1 )
            {
                //Pla_PrintBinary( (word *)&i, nVars );
                //printf( " %d \n", pDirs[0] );

                Pla_TtSetBit(pDc, i);
                Pla_TtSetBit(pDc, i ^ (1 << pDirs[0]));
                Cubes[1]++;
                // save
                Vec_IntPushTwo( vStore, i, pDirs[0] );
                continue;
            }
            if ( nDirs == 2 && Pla_TtGetBit(pOn, i ^ (1 << pDirs[0]) ^ (1 << pDirs[1])) )
            {
                assert( 0 );
                Pla_TtSetBit(pDc, i);
                Pla_TtSetBit(pDc, i ^ (1 << pDirs[0]));
                Pla_TtSetBit(pDc, i ^ (1 << pDirs[1]));
                Pla_TtSetBit(pDc, i ^ (1 << pDirs[0]) ^ (1 << pDirs[1]));
                Cubes[2]++;
                continue;
            }
        }

    // go through the remaining cubes
    for ( i = 0; i < nMints; i++ )
        if ( Pla_TtGetBit(pOn, i) && !Pla_TtGetBit(pDc, i) )
        {
            int pDirs[32], nDirs = Pla_ManExpendDirNum(pOn, nVars, i, pDirs);
            // find direction, which is not taken
            for ( v = 0; v < nDirs; v++ )
                if ( Pla_TtGetBit(pOn, i ^ (1 << pDirs[v])) && !Pla_TtGetBit(pDc, i ^ (1 << pDirs[v])) )
                    break;
            // if there is no open directions, use any one
            if ( v == nDirs )
                v = 0;
            // mark this one
            Pla_TtSetBit(pDc, i);
            Pla_TtSetBit(pDc, i ^ (1 << pDirs[v]));
            Cubes[10]++;
            // save
            Vec_IntPushTwo( vStore, i, pDirs[v] );
            continue;
        }

    printf( "\n" );
    printf( "Truth = %d. ", Pla_TtCountOnes(pOn, nWords) );
    printf( "Cover = %d. ", Pla_TtCountOnes(pDc, nWords) );
    printf( "\n" );

    printf( "Count: " );
    for ( i = 0; i < 16; i++ )
        if ( Count[i] )
            printf( "%d=%d ", i, Count[i] );
    printf( "\n" );

    printf( "Cubes: " );
    for ( i = 0; i < 16; i++ )
        if ( Cubes[i] )
            printf( "%d=%d ", i, Cubes[i] );
    printf( "\n" );

/*
    // extract cubes one at a time
    for ( i = 0; i < nMints; i++ )
        if ( Pla_TtGetBit(pOn, i) )
        {
            word Cube = 0;
            for ( v = 0; v < nVars; v++ )
                if ( (i >> v) & 1 )
                    Pla_CubeSetLit( &Cube, v, PLA_LIT_ONE );
                else
                    Pla_CubeSetLit( &Cube, v, PLA_LIT_ZERO );
            Vec_WrdPush( vCubes, Cube );
        }
*/
    Vec_IntForEachEntryDouble( vStore, iMint, iVar, i )
    {
        word Cube = 0;
        for ( v = 0; v < nVars; v++ )
        {
            if ( v == iVar )
                continue;
            if ( (iMint >> v) & 1 )
                Pla_CubeSetLit( &Cube, v, PLA_LIT_ONE );
            else
                Pla_CubeSetLit( &Cube, v, PLA_LIT_ZERO );
        }
        Vec_WrdPush( vCubes, Cube );
    }
    Vec_IntFree( vStore );

    // collect the minterms
    ABC_FREE( pDc );
    return vCubes;
}
Pla_Man_t * Pla_ManFxPrepare( int nVars )
{
    Pla_Man_t * p; char Buffer[1000]; 
    Vec_Bit_t * vFunc = Pla_ManPrimesTable( nVars );
    Vec_Wrd_t * vSop = Pla_ManFxMinimize( (word *)Vec_BitArray(vFunc), nVars );
    word Cube, * pCube = &Cube; int i, k, Lit;
    sprintf( Buffer, "primes%02d", nVars );
    p = Pla_ManAlloc( Buffer, nVars, 1, Vec_WrdSize(vSop) );
    Vec_WecInit( &p->vCubeLits, Pla_ManCubeNum(p) );
    Vec_WecInit( &p->vOccurs, 2*Pla_ManInNum(p) );
    Vec_WrdForEachEntry( vSop, Cube, i )
        Pla_CubeForEachLit( nVars, pCube, Lit, k )
            if ( Lit != PLA_LIT_DASH )
            {
                Lit = Abc_Var2Lit( k, Lit == PLA_LIT_ZERO );
                Vec_WecPush( &p->vCubeLits, i, Lit );
                Vec_WecPush( &p->vOccurs, Lit, i );
            }
    Vec_BitFree( vFunc );
    Vec_WrdFree( vSop );
    return p;
}
int Pla_ManFxPerformSimple( int nVars )
{
    char Buffer[100];
    Pla_Man_t * p = Pla_ManFxPrepare( nVars );
    sprintf( Buffer, "primesmin%02d.pla", nVars );
    Pla_ManDumpPla( p, Buffer );
    Pla_ManFree( p );
    return 1;
}

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


ABC_NAMESPACE_IMPL_END

