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

  FileName    [sfmDec.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [SAT-based optimization using internal don't-cares.]

  Synopsis    [SAT-based decomposition.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "sfmInt.h"
#include "bool/kit/kit.h"
#include "base/abc/abc.h"

ABC_NAMESPACE_IMPL_START


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

#define SFM_FAN_MAX 6

typedef struct Sfm_Dec_t_ Sfm_Dec_t;
struct Sfm_Dec_t_
{
    // parameters
    Sfm_Par_t *       pPars;       // parameters
    // library
    Vec_Int_t         vGateSizes;  // fanin counts
    Vec_Wrd_t         vGateFuncs;  // gate truth tables
    Vec_Wec_t         vGateCnfs;   // gate CNFs
    // objects
    int               iTarget;     // target node
    Vec_Int_t         vObjTypes;   // PI (1), PO (2)
    Vec_Int_t         vObjGates;   // functionality
    Vec_Wec_t         vObjFanins;  // fanin IDs
    // solver
    sat_solver *      pSat;        // reusable solver 
    Vec_Wec_t         vClauses;    // CNF clauses for the node
    Vec_Int_t         vPols[2];    // onset/offset polarity
    Vec_Int_t         vImpls[2];   // onset/offset implications
    Vec_Wrd_t         vSets[2];    // onset/offset patterns
    int               nPats[3];
    // temporary
    Vec_Int_t         vTemp;
    Vec_Int_t         vTemp2;
    // statistics
    abctime           timeWin;
    abctime           timeCnf;
    abctime           timeSat;
};

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Sfm_Dec_t * Sfm_DecStart()
{
    Sfm_Dec_t * p = ABC_CALLOC( Sfm_Dec_t, 1 );
    p->pSat = sat_solver_new();
    return p;
}
void Sfm_DecStop( Sfm_Dec_t * p )
{
    // library
    Vec_IntErase( &p->vGateSizes );
    Vec_WrdErase( &p->vGateFuncs );
    Vec_WecErase( &p->vGateCnfs );
    // objects
    Vec_IntErase( &p->vObjTypes );
    Vec_IntErase( &p->vObjGates );
    Vec_WecErase( &p->vObjFanins );
    // solver
    sat_solver_delete( p->pSat );
    Vec_WecErase( &p->vClauses );
    Vec_IntErase( &p->vPols[0] );
    Vec_IntErase( &p->vPols[1] );
    Vec_IntErase( &p->vImpls[0] );
    Vec_IntErase( &p->vImpls[1] );
    Vec_WrdErase( &p->vSets[0] );
    Vec_WrdErase( &p->vSets[1] );
    // temporary
    Vec_IntErase( &p->vTemp );
    Vec_IntErase( &p->vTemp2 );
    ABC_FREE( p );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sfm_DecCreateCnf( Sfm_Dec_t * p )
{
    Vec_Str_t * vCnf, * vCnfBase;
    Vec_Int_t * vCover;
    word uTruth;
    int i, nCubes;
    vCnf = Vec_StrAlloc( 100 );
    vCover = Vec_IntAlloc( 100 );
    Vec_WecInit( &p->vGateCnfs, Vec_IntSize(&p->vGateSizes) );
    Vec_WrdForEachEntry( &p->vGateFuncs, uTruth, i )
    {
        nCubes = Sfm_TruthToCnf( uTruth, Vec_IntEntry(&p->vGateSizes, i), vCover, vCnf );
        vCnfBase = (Vec_Str_t *)Vec_WecEntry( &p->vGateCnfs, i );
        Vec_StrGrow( vCnfBase, Vec_StrSize(vCnf) );
        memcpy( Vec_StrArray(vCnfBase), Vec_StrArray(vCnf), Vec_StrSize(vCnf) );
        vCnfBase->nSize = Vec_StrSize(vCnf);
    }
    Vec_IntFree( vCover );
    Vec_StrFree( vCnf );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Sfm_DecCreateAigLibrary( Sfm_Dec_t * p )
{
    // const0
    Vec_IntPush( &p->vGateSizes, 0 );
    Vec_WrdPush( &p->vGateFuncs, 0 );
    // const1
    Vec_IntPush( &p->vGateSizes, 0 );
    Vec_WrdPush( &p->vGateFuncs, ~(word)0 );
    // buffer
    Vec_IntPush( &p->vGateSizes, 1 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // inverter
    Vec_IntPush( &p->vGateSizes, 1 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0x5555555555555555) );
    // and00
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0xCCCCCCCCCCCCCCCC) & ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // and01
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0xCCCCCCCCCCCCCCCC) &~ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // and10
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs,~ABC_CONST(0xCCCCCCCCCCCCCCCC) & ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // and11
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs,~ABC_CONST(0xCCCCCCCCCCCCCCCC) &~ABC_CONST(0xAAAAAAAAAAAAAAAA) );
/*
    // xor
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0xCCCCCCCCCCCCCCCC) ^ ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // xnor
    Vec_IntPush( &p->vGateSizes, 2 );
    Vec_WrdPush( &p->vGateFuncs, ABC_CONST(0xCCCCCCCCCCCCCCCC) ^~ABC_CONST(0xAAAAAAAAAAAAAAAA) );
    // mux
    Vec_IntPush( &p->vGateSizes, 3 );
    Vec_WrdPush( &p->vGateFuncs, (ABC_CONST(0xF0F0F0F0F0F0F0F0) & ABC_CONST(0xCCCCCCCCCCCCCCCC)) | (ABC_CONST(0x0F0F0F0F0F0F0F0F) & ABC_CONST(0xAAAAAAAAAAAAAAAA)) );
*/
    // derive CNF for these functions
    Sfm_DecCreateCnf( p );
}

void Vec_IntLift( Vec_Int_t * p, int Amount )
{
    int i;
    for ( i = 0; i < p->nSize; i++ )
        p->pArray[i] += Amount;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sfm_DecPrepareSolver( Sfm_Dec_t * p )
{
    abctime clk = Abc_Clock();
    Vec_Int_t * vLevel, * vClause;
    int i, k, Type, Gate, iObj, RetValue;
    int nSatVars = 2 * Vec_IntSize(&p->vObjTypes) - p->iTarget - 1;
    assert( Vec_IntSize(&p->vObjTypes) == Vec_IntSize(&p->vObjGates) );
    assert( p->iTarget < Vec_IntSize(&p->vObjTypes) );
    // collect variables of root nodes
    Vec_IntClear( &p->vTemp );
    Vec_IntForEachEntryStart( &p->vObjTypes, Type, i, p->iTarget )
        if ( Type == 2 )
            Vec_IntPush( &p->vTemp, i );
    assert( Vec_IntSize(&p->vTemp) > 0 );
    // create SAT solver
    sat_solver_restart( p->pSat );
    sat_solver_setnvars( p->pSat, nSatVars + Vec_IntSize(&p->vTemp) );
    // add CNF clauses for the TFI
    Vec_IntForEachEntryStop( &p->vObjTypes, Type, i, p->iTarget + 1 )
    {
        if ( Type == 1 )
            continue;
        // generate CNF 
        Gate = Vec_IntEntry( &p->vObjGates, i );
        vLevel = Vec_WecEntry( &p->vObjFanins, i );
        Sfm_TranslateCnf( &p->vClauses, (Vec_Str_t *)Vec_WecEntry(&p->vGateCnfs, Gate), vLevel, -1 );
        // add clauses
        Vec_WecForEachLevel( &p->vClauses, vClause, k )
        {
            if ( Vec_IntSize(vClause) == 0 )
                break;
            RetValue = sat_solver_addclause( p->pSat, Vec_IntArray(vClause), Vec_IntArray(vClause) + Vec_IntSize(vClause) );
            if ( RetValue == 0 )
                return 0;
        }
    }
    // add CNF clauses for the TFO
    Vec_IntForEachEntryStart( &p->vObjTypes, Type, i, p->iTarget + 1 )
    {
        assert( Type != 1 );
        // generate CNF 
        Gate = Vec_IntEntry( &p->vObjGates, i );
        vLevel = Vec_WecEntry( &p->vObjFanins, i );
        Vec_IntLift( vLevel, Vec_IntSize(&p->vObjTypes) );
        Sfm_TranslateCnf( &p->vClauses, (Vec_Str_t *)Vec_WecEntry(&p->vGateCnfs, Gate), vLevel, p->iTarget );
        Vec_IntLift( vLevel, Vec_IntSize(&p->vObjTypes) );
        // add clauses
        Vec_WecForEachLevel( &p->vClauses, vClause, k )
        {
            if ( Vec_IntSize(vClause) == 0 )
                break;
            RetValue = sat_solver_addclause( p->pSat, Vec_IntArray(vClause), Vec_IntArray(vClause) + Vec_IntSize(vClause) );
            if ( RetValue == 0 )
                return 0;
        }
    }
    if ( p->iTarget + 1 < Vec_IntSize(&p->vObjTypes) )
    {
        // create XOR clauses for the roots
        Vec_IntForEachEntry( &p->vTemp, iObj, i )
        {
            sat_solver_add_xor( p->pSat, iObj, 2*iObj + Vec_IntSize(&p->vObjTypes) - p->iTarget - 1, nSatVars++, 0 );
            Vec_IntWriteEntry( &p->vTemp, i, Abc_Var2Lit(nSatVars-1, 0) );
        }
        // make OR clause for the last nRoots variables
        RetValue = sat_solver_addclause( p->pSat, Vec_IntArray(&p->vTemp), Vec_IntLimit(&p->vTemp) );
        if ( RetValue == 0 )
            return 0;
        assert( nSatVars == sat_solver_nvars(p->pSat) );
    }
    else assert( Vec_IntSize(&p->vTemp) == 1 );
    // finalize
    RetValue = sat_solver_simplify( p->pSat );
    p->timeCnf += Abc_Clock() - clk;
    return 1;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Sfm_DecPeformDec( Sfm_Dec_t * p )
{
    int fVerbose = 1;
    int nBTLimit = 0;
    abctime clk = Abc_Clock();
    int i, k, c, status, Lits[2];
    // check stuck-at-0/1 (on/off-set empty)
    p->nPats[0] = p->nPats[1] = 0;
    for ( c = 0; c < 2; c++ )
    {
        Lits[0] = Abc_Var2Lit( p->iTarget, c );
        status = sat_solver_solve( p->pSat, Lits, Lits + 1, nBTLimit, 0, 0, 0 );
        if ( status == l_Undef )
            return 0;
        if ( status == l_False )
        {
            Vec_IntPush( &p->vObjTypes, 0 );
            Vec_IntPush( &p->vObjGates, c );
            Vec_WecPushLevel( &p->vObjFanins );
            return 1;
        }
        assert( status == l_True );
        // record this status
        for ( i = 0; i < p->iTarget; i++ )
        {
            Vec_IntPush( &p->vPols[c], sat_solver_var_value(p->pSat, i) );
            Vec_WrdPush( &p->vSets[c], 0 );
        }
        p->nPats[c]++;
    }
    // proceed checking divisors based on their values
    for ( c = 0; c < 2; c++ )
    {
        Lits[0] = Abc_Var2Lit( p->iTarget, c );
        for ( i = 0; i < p->iTarget; i++ )
        {
            if ( Vec_WrdEntry(&p->vSets[c], i) ) // diff value is possible
                continue;
            Lits[1] = Abc_Var2Lit( i, Vec_IntEntry(&p->vPols[c], i) );
            status = sat_solver_solve( p->pSat, Lits, Lits + 2, nBTLimit, 0, 0, 0 );
            if ( status == l_Undef )
                return 0;
            if ( status == l_False )
            {
                Vec_IntPush( &p->vImpls[c], i );
                continue;
            }
            assert( status == l_True );
            // record this status
            for ( i = 0; i < p->iTarget; i++ )
                if ( sat_solver_var_value(p->pSat, i) ^ Vec_IntEntry(&p->vPols[c], i) )
                    *Vec_WrdEntryP(&p->vSets[c], i) |= ((word)1 << p->nPats[c]);
            p->nPats[c]++;
        }
    }
    // print the results
    if ( fVerbose )
    for ( c = 0; c < 2; c++ )
    {
        printf( "\nON-SET reference vertex:\n" );
        for ( i = 0; i < p->iTarget; i++ )
            printf( "%d", Vec_IntEntry(&p->vPols[c], i) );
        printf( "\n" );
        printf( "     " );
        for ( i = 0; i < p->iTarget; i++ )
            printf( "%d", i % 10 );
        printf( "\n" );
        for ( k = 0; k < p->nPats[c]; k++ )
        {
            printf( "%2d : ", k );
            for ( i = 0; i < p->iTarget; i++ )
                printf( "%d", (int)((Vec_WrdEntry(&p->vSets[c], i) >> k) & 1) );
            printf( "\n" );
        }
    }
    p->timeSat += Abc_Clock() - clk;
    return 1;
}

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

  Synopsis    [Testbench for the new AIG minimization.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkDfsReverseOne_rec( Abc_Obj_t * pNode, Vec_Int_t * vNodes )
{
    Abc_Obj_t * pFanout; int i;
    if ( Abc_NodeIsTravIdCurrent( pNode ) )
        return;
    Abc_NodeSetTravIdCurrent( pNode );
    if ( Abc_ObjIsCo(pNode) )
    {
        Vec_IntPush( vNodes, Abc_ObjId(pNode) );
        return;
    }
    assert( Abc_ObjIsNode( pNode ) );
    Abc_ObjForEachFanout( pNode, pFanout, i )
        Abc_NtkDfsReverseOne_rec( pFanout, vNodes );
    Vec_IntPush( vNodes, Abc_ObjId(pNode) );
}
void Abc_NtkDfsOne_rec( Abc_Obj_t * pNode, Vec_Int_t * vMap, Vec_Int_t * vTypes )
{
    Abc_Obj_t * pFanin; int i;
    if ( Abc_NodeIsTravIdCurrent( pNode ) )
        return;
    Abc_NodeSetTravIdCurrent( pNode );
    if ( Abc_ObjIsCi(pNode) )
    {
        pNode->iTemp = Vec_IntSize(vMap);
        Vec_IntPush( vMap, Abc_ObjId(pNode) );
        Vec_IntPush( vTypes, 1 );
        return;
    }
    assert( Abc_ObjIsNode(pNode) );
    Abc_ObjForEachFanin( pNode, pFanin, i )
        Abc_NtkDfsOne_rec( pFanin, vMap, vTypes );
    pNode->iTemp = Vec_IntSize(vMap);
    Vec_IntPush( vMap, Abc_ObjId(pNode) );
    Vec_IntPush( vTypes, 0 );
}
int Sfm_DecExtract( Abc_Ntk_t * pNtk, int iNode, Vec_Int_t * vTypes, Vec_Int_t * vGates, Vec_Wec_t * vFanins, Vec_Int_t * vMap, Vec_Int_t * vTemp )
{
    Abc_Obj_t * pNode = Abc_NtkObj( pNtk, iNode );
    Vec_Int_t * vLevel;
    int i, iObj, iTarget;
    assert( Abc_ObjIsNode(pNode) );
    // collect transitive fanout
    Vec_IntClear( vTemp );
    Abc_NtkIncrementTravId( pNtk );
    Abc_NtkDfsReverseOne_rec( pNode, vTemp );
    // collect transitive fanin
    Vec_IntClear( vMap );
    Vec_IntClear( vTypes );
    Abc_NtkDfsOne_rec( pNode, vMap, vTypes );
    Vec_IntPop( vMap );
    Vec_IntPop( vTypes );
    assert( Vec_IntSize(vMap) == Vec_IntSize(vTypes) );
    // remember target node
    iTarget = Vec_IntSize( vMap );
    // add transitive fanout
    Vec_IntForEachEntryReverse( vTemp, iObj, i )
    {
        pNode = Abc_NtkObj( pNtk, iObj );
        if ( Abc_ObjIsCo(pNode) )
        {
            assert( Vec_IntEntry(vTypes, Abc_ObjFanin0(pNode)->iTemp) == 0 );
            Vec_IntWriteEntry( vTypes, Abc_ObjFanin0(pNode)->iTemp, 2 );
            continue;
        }
        pNode->iTemp = Vec_IntSize(vMap);
        Vec_IntPush( vMap, Abc_ObjId(pNode) );
        Vec_IntPush( vTypes, 0 );
    }
    assert( Vec_IntSize(vMap) == Vec_IntSize(vTypes) );
    // create gates and fanins
    Vec_IntClear( vGates );
    Vec_WecClear( vFanins );
    Vec_IntForEachEntry( vMap, iObj, i )
    {
        vLevel = Vec_WecPushLevel( vFanins );
        pNode = Abc_NtkObj( pNtk, iObj );
        if ( Abc_ObjIsCi(pNode) )
        {
            Vec_IntPush( vGates, -1 );
            continue;
        }
        assert( Abc_ObjFaninNum(pNode) == 2 );
             if ( !Abc_ObjFaninC0(pNode) && !Abc_ObjFaninC1(pNode) )
            Vec_IntPush( vGates, 4 );
        else if ( !Abc_ObjFaninC0(pNode) &&  Abc_ObjFaninC1(pNode) )
            Vec_IntPush( vGates, 5 );
        else if (  Abc_ObjFaninC0(pNode) && !Abc_ObjFaninC1(pNode) )
            Vec_IntPush( vGates, 6 );
        else //if ( Abc_ObjFaninC0(pNode) && Abc_ObjFaninC1(pNode) )
            Vec_IntPush( vGates, 7 );
        Vec_IntPush( vLevel, Abc_ObjFanin0(pNode)->iTemp );
        Vec_IntPush( vLevel, Abc_ObjFanin1(pNode)->iTemp );
    }
    return iTarget;
}
void Sfm_DecInsert( Abc_Ntk_t * pNtk, int iNode, int Limit, Vec_Int_t * vTypes, Vec_Int_t * vGates, Vec_Wec_t * vFanins, Vec_Int_t * vMap )
{
    Abc_Obj_t * pTarget = Abc_NtkObj( pNtk, iNode );
    Vec_Int_t * vLevel;
    Abc_Obj_t * pObjNew = NULL; 
    int i, k, iObj, Gate;
    assert( Limit < Vec_IntSize(vTypes) );
    Vec_IntForEachEntryStart( vGates, Gate, i, Limit )
    {
        assert( Gate >= 0 && Gate <= 7 );
        vLevel = Vec_WecEntry( vFanins, i );
        if ( Gate == 0 )
            pObjNew = Abc_NtkCreateNodeConst0( pNtk );
        else if ( Gate == 1 )
            pObjNew = Abc_NtkCreateNodeConst1( pNtk );
        else if ( Gate == 2 )
            pObjNew = Abc_NtkCreateNodeBuf( pNtk, Abc_NtkObj(pNtk, Vec_IntEntry(vMap, Vec_IntEntry(vLevel,0))) );
        else if ( Gate == 3 )
            pObjNew = Abc_NtkCreateNodeInv( pNtk, Abc_NtkObj(pNtk, Vec_IntEntry(vMap, Vec_IntEntry(vLevel,0))) );
        else // if ( Gate >= 4 )
        {
            pObjNew = Abc_NtkCreateNode( pNtk );
            Vec_IntForEachEntry( vLevel, iObj, k )
                Abc_ObjAddFanin( pObjNew, Abc_NtkObj(pNtk, Vec_IntEntry(vMap, iObj)) );
            pObjNew->pData = NULL; // SELECTION FUNCTION
        }
        // transfer the fanout
        Abc_ObjTransferFanout( pTarget, pObjNew );
        assert( Abc_ObjFanoutNum(pTarget) == 0 );
        Abc_NtkDeleteObj_rec( pTarget, 1 );
    }
}
void Sfm_DecTestBench( Abc_Ntk_t * pNtk )
{
    Vec_Int_t * vMap, * vTemp;
    Abc_Obj_t * pObj; int i, Limit;
    Sfm_Dec_t * p = Sfm_DecStart();
    Sfm_DecCreateAigLibrary( p );
    assert( Abc_NtkIsSopLogic(pNtk) );
    assert( Abc_NtkGetFaninMax(pNtk) <= 2 );
    vMap  = Vec_IntAlloc( Abc_NtkObjNumMax(pNtk) ); // Sfm->Ntk
    vTemp = Vec_IntAlloc( Abc_NtkObjNumMax(pNtk) );
    Abc_NtkForEachNode( pNtk, pObj, i )
    {
        p->iTarget = Sfm_DecExtract( pNtk, i, &p->vObjTypes, &p->vObjGates, &p->vObjFanins, vMap, vTemp );
        Limit = Vec_IntSize( &p->vObjTypes );
        if ( !Sfm_DecPrepareSolver( p ) )
            continue;
        if ( !Sfm_DecPeformDec( p ) )
            continue;
        Sfm_DecInsert( pNtk, p->iTarget, Limit, &p->vObjTypes, &p->vObjGates, &p->vObjFanins, vMap );
    }
    Vec_IntFree( vMap );
    Vec_IntFree( vTemp );
    Sfm_DecStop( p );
}

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


ABC_NAMESPACE_IMPL_END