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

  FileName    [bmcMesh.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT-based bounded model checking.]

  Synopsis    [Synthesis for mesh of LUTs.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "bmc.h"
#include "sat/satoko/satoko.h"

ABC_NAMESPACE_IMPL_START


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

#define NCPARS 16

static inline int Bmc_MeshTVar( int Me[102][102], int x, int y ) { return Me[x][y];                                         }
static inline int Bmc_MeshGVar( int Me[102][102], int x, int y ) { return Me[x][y] + Me[101][100];                          }
static inline int Bmc_MeshCVar( int Me[102][102], int x, int y ) { return Me[x][y] + Me[101][100] + Me[101][101];           }
static inline int Bmc_MeshUVar( int Me[102][102], int x, int y ) { return Me[x][y] + Me[101][100] + Me[101][101] + NCPARS;  }

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Bmc_MeshVarValue( satoko_t * p, int v )
{
//    int value = var_value(p, v) != SATOKO_VAR_UNASSING ? var_value(p, v) : satoko_var_polarity(p, v);
//    return value == SATOKO_LIT_TRUE;
    return satoko_var_polarity(p, v) == SATOKO_LIT_TRUE;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Bmc_MeshAddOneHotness( satoko_t * pSat, int iFirst, int iLast )
{
    int i, j, v, pVars[100], nVars  = 0, nCount = 0;
    assert( iFirst < iLast && iFirst + 110 > iLast );
    for ( v = iFirst; v < iLast; v++ )
        if ( Bmc_MeshVarValue(pSat, v) ) // v = 1
        {
            assert( nVars < 100 );
            pVars[ nVars++ ] = v;
        }
    if ( nVars <= 1 )
        return 0;
    for ( i = 0;   i < nVars; i++ )
    for ( j = i+1; j < nVars; j++ )
    {
        int pLits[2], RetValue;
        pLits[0] = Abc_Var2Lit( pVars[i], 1 );
        pLits[1] = Abc_Var2Lit( pVars[j], 1 );
        RetValue = satoko_add_clause( pSat, pLits, 2 );  assert( RetValue );
        nCount++;
    }
    return nCount;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Bmc_MeshTest( Gia_Man_t * p, int X, int Y, int T, int fVerbose )
{
    abctime clk = Abc_Clock();
    satoko_t * pSat = satoko_create();
    Gia_Obj_t * pObj;
    int Me[102][102] = {{0}};
    int pN[102][2] = {{0}};
    int I = Gia_ManPiNum(p);
    int G = I + Gia_ManAndNum(p);
    int i, x, y, t, g, c, status, RetValue, Lit, iVar, nClauses = 0;

    assert( X <= 100 && Y <= 100 && T <= 100 && G <= 100 );

    // init the graph
    for ( i = 0; i < I; i++ )
        pN[i][0] = pN[i][1] = -1;
    Gia_ManForEachAnd( p, pObj, i )
    {
        pN[i-1][0] = Gia_ObjFaninId0(pObj, i)-1;
        pN[i-1][1] = Gia_ObjFaninId1(pObj, i)-1;
    }
    if ( fVerbose )
    {
        printf( "The graph has %d inputs: ", Gia_ManPiNum(p) );
        for ( i = 0; i < I; i++ )
            printf( "%c ", 'a' + i );
        printf( "  and %d nodes: ", Gia_ManAndNum(p) );
        for ( i = I; i < G; i++ )
            printf( "%c=%c%c ", 'a' + i, 'a' + pN[i][0] , 'a' + pN[i][1] );
        printf( "\n" );
    }

    // init SAT variables (time vars + graph vars + config vars)
    // config variables: 16 = 4 buff vars + 12 node vars
    iVar = 0;
    for ( y = 0; y < Y; y++ )
    for ( x = 0; x < X; x++ )
    {
        //printf( "%3d %3d %3d    %s", iVar, iVar+T, iVar+T+G, x == X-1 ? "\n":"" );
        Me[x][y] = iVar;
        iVar += T + G + NCPARS + 1;
    }
    Me[101][100] = T;
    Me[101][101] = G;
    if ( fVerbose )
        printf( "SAT variable count is %d (%d time vars + %d graph vars + %d config vars + %d aux vars)\n", iVar, X*Y*T, X*Y*G, X*Y*NCPARS, X*Y );

    // add constraints

    // time 0 and primary inputs only on the boundary
    for ( x = 0; x < X; x++ )
    for ( y = 0; y < Y; y++ )
    {
        int iTVar = Bmc_MeshTVar( Me, x, y );
        int iGVar = Bmc_MeshGVar( Me, x, y );

        if ( x == 0 || x == X-1 || y == 0 || y == Y-1 ) // boundary
        {
            // time 0 is required
            for ( t = 0; t < T; t++ )
            {
                Lit = Abc_Var2Lit( iTVar+t, (int)(t > 0) );
                RetValue = satoko_add_clause( pSat, &Lit, 1 );  assert( RetValue );
            }
            // internal nodes are not allowed
            for ( g = I; g < G; g++ )
            {
                Lit = Abc_Var2Lit( iGVar+g, 1 );
                RetValue = satoko_add_clause( pSat, &Lit, 1 );  assert( RetValue );
            }
        }
        else // not a boundary
        {
            Lit = Abc_Var2Lit( iTVar, 1 );  // cannot have time 0
            RetValue = satoko_add_clause( pSat, &Lit, 1 );  assert( RetValue );
        }
    }
    for ( x = 1; x < X-1; x++ )
    for ( y = 1; y < Y-1; y++ )
    {
        int pLits[100], nLits;

        int iTVar = Bmc_MeshTVar( Me, x, y );
        int iGVar = Bmc_MeshGVar( Me, x, y );
        int iCVar = Bmc_MeshCVar( Me, x, y );
        int iUVar = Bmc_MeshUVar( Me, x, y );

        // 0=left  1=up  2=right  3=down
        int iTVars[4]; 
        int iGVars[4];

        iTVars[0] = Bmc_MeshTVar( Me, x-1, y );
        iGVars[0] = Bmc_MeshGVar( Me, x-1, y );

        iTVars[1] = Bmc_MeshTVar( Me, x, y-1 );
        iGVars[1] = Bmc_MeshGVar( Me, x, y-1 );

        iTVars[2] = Bmc_MeshTVar( Me, x+1, y );
        iGVars[2] = Bmc_MeshGVar( Me, x+1, y );

        iTVars[3] = Bmc_MeshTVar( Me, x, y+1 );
        iGVars[3] = Bmc_MeshGVar( Me, x, y+1 );

        // condition when cell is used
        for ( g = 0; g < G; g++ )
        {
            pLits[0] = Abc_Var2Lit( iGVar+g, 1 );
            pLits[1] = Abc_Var2Lit( iUVar, 0 );
            RetValue = satoko_add_clause( pSat, pLits, 2 );  assert( RetValue );
            nClauses++;
        }

        // at least one time is used
        pLits[0] = Abc_Var2Lit( iUVar, 1 );
        for ( t = 1; t < T; t++ )
            pLits[t] = Abc_Var2Lit( iTVar+t, 0 );
        RetValue = satoko_add_clause( pSat, pLits, T );  assert( RetValue );
        nClauses++;

        // at least one config is used
        pLits[0] = Abc_Var2Lit( iUVar, 1 );
        for ( c = 0; c < NCPARS; c++ )
            pLits[c+1] = Abc_Var2Lit( iCVar+c, 0 );
        RetValue = satoko_add_clause( pSat, pLits, NCPARS+1 );  assert( RetValue );
        nClauses++;

        // constraints for each time
        for ( t = 1; t < T; t++ )
        {
            int Conf[12][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3},   {1, 0}, {2, 0}, {3, 0}, {2, 1}, {3, 1}, {3, 2}};
            // buffer
            for ( g = 0; g < G; g++ )
            for ( c = 0; c < 4; c++ )
            {
                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iTVars[c]+t-1, 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );

                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVars[c]+g, 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );

                nClauses += 2;
            }
            for ( g = 0; g < I; g++ )
            for ( c = 4; c < NCPARS; c++ )
            {
                pLits[0] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[1] = Abc_Var2Lit( iCVar+c, 1 );
                RetValue = satoko_add_clause( pSat, pLits, 2 );  assert( RetValue );
                nClauses++;
            }
            // node
            for ( g = I; g < G; g++ )
            for ( c = 0; c < 12; c++ )
            {
                assert( pN[g][0] >= 0 && pN[g][1] >= 0 );

                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c+4, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iTVars[Conf[c][0]]+t-1, 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );

                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c+4, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iTVars[Conf[c][1]]+t-1, 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );


                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c+4, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVars[Conf[c][0]]+pN[g][0], 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );

                nLits = 0;
                pLits[ nLits++ ] = Abc_Var2Lit( iTVar+t, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVar+g, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iCVar+c+4, 1 );
                pLits[ nLits++ ] = Abc_Var2Lit( iGVars[Conf[c][1]]+pN[g][1], 0 );
                RetValue = satoko_add_clause( pSat, pLits, nLits );  assert( RetValue );

                nClauses += 4;
            }
        }
    }

    // final condition
    {
        int iGVar = Bmc_MeshGVar( Me, 1, 1 ) + G-1;
        Lit = Abc_Var2Lit( iGVar, 0 );
        RetValue = satoko_add_clause( pSat, &Lit, 1 );  
        if ( RetValue == 0 )
        {
            printf( "Problem has no solution. " );
            Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
            satoko_destroy( pSat );
            return;
        }
    }

    if ( fVerbose )
        printf( "Finished adding %d clauses. Started solving...\n", nClauses );

    while ( 1 )
    {
        int nAddClauses = 0;
        status = satoko_solve( pSat );
        if ( status == SATOKO_UNSAT )
        {
            printf( "Problem has no solution. " );
            break;
        }
        if ( status == SATOKO_UNDEC )
        {
            printf( "Computation timed out. " );
            break;
        }
        assert( status == SATOKO_SAT );
        // check if the solution is valid and add constraints
        for ( x = 0; x < X; x++ )
        for ( y = 0; y < Y; y++ )
        {
            if ( x == 0 || x == X-1 || y == 0 || y == Y-1 ) // boundary
            {
                int iGVar = Bmc_MeshGVar( Me, x, y );
                nAddClauses += Bmc_MeshAddOneHotness( pSat, iGVar, iGVar + G );
            }
            else
            {
                int iTVar = Bmc_MeshTVar( Me, x, y );
                int iGVar = Bmc_MeshGVar( Me, x, y );
                int iCVar = Bmc_MeshCVar( Me, x, y );
                nAddClauses += Bmc_MeshAddOneHotness( pSat, iTVar, iTVar + T );
                nAddClauses += Bmc_MeshAddOneHotness( pSat, iGVar, iGVar + G );
                nAddClauses += Bmc_MeshAddOneHotness( pSat, iCVar, iCVar + NCPARS );
            }
        }
        if ( nAddClauses > 0 )
        {
            printf( "Adding %d one-hotness clauses.\n", nAddClauses );
            continue;
        }
        printf( "Satisfying solution found. " );
/*
        iVar = satoko_varnum(pSat);
        for ( i = 0; i < iVar; i++ )
            if ( Bmc_MeshVarValue(pSat, i) )
                printf( "%d ", i );
        printf( "\n" );
*/
        break;
    }
    Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
    if ( status == SATOKO_SAT )
    {
        // count the number of nodes and buffers
        int nBuffs = 0, nNodes = 0;
        for ( y = 1; y < Y-1; y++ )
        for ( x = 1; x < X-1; x++ )
        {
            int iCVar = Bmc_MeshCVar( Me, x, y );
            for ( c = 0; c < 4; c++ )
                if ( Bmc_MeshVarValue(pSat, iCVar+c) )
                {
                    //printf( "Buffer y=%d x=%d  (var = %d; config = %d)\n", y, x, iCVar+c, c );
                    nBuffs++;
                }
            for ( c = 4; c < NCPARS; c++ )
                if ( Bmc_MeshVarValue(pSat, iCVar+c) )
                {
                    //printf( "Node   y=%d x=%d  (var = %d; config = %d)\n", y, x, iCVar+c, c );
                    nNodes++;
                }
        }
        printf( "The %d x %d mesh with latency %d with %d active cells (%d nodes and %d buffers):\n", X, Y, T, nNodes+nBuffs, nNodes, nBuffs );
        // print mesh
        printf( " Y\\X " );
        for ( x = 0; x < X; x++ )
            printf( "  %-2d ", x );
        printf( "\n" );
        for ( y = 0; y < Y; y++ )
        {
            printf( " %-2d  ", y );
            for ( x = 0; x < X; x++ )
            {
                int iTVar  = Bmc_MeshTVar( Me, x, y );
                int iGVar  = Bmc_MeshGVar( Me, x, y );

                int fFound = 0;                ;
                for ( t = 0; t < T; t++ )
                for ( g = 0; g < G; g++ )
                    if ( Bmc_MeshVarValue(pSat, iTVar+t) && Bmc_MeshVarValue(pSat, iGVar+g) )
                    {
                        printf( " %c%-2d ", 'a' + g, t );
                        fFound = 1;
                    }
                if ( fFound )
                    continue;
                if ( x == 0 || x == X-1 || y == 0 || y == Y-1 ) // boundary
                    printf( "  *  " );
                else
                    printf( "     " );
            }
            printf( "\n" );
        }
    }
    satoko_destroy( pSat );
}

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


ABC_NAMESPACE_IMPL_END