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

  FileName    [fraHot.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [New FRAIG package.]

  Synopsis    [Computing and using one-hotness conditions.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 30, 2007.]

  Revision    [$Id: fraHot.c,v 1.00 2007/06/30 00:00:00 alanmi Exp $]

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

#include "fra.h"

ABC_NAMESPACE_IMPL_START


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

static inline int  Fra_RegToLit( int n, int c )   { return c? -n-1 : n+1;       }
static inline int  Fra_LitReg( int n )            { return (n>0)? n-1 : -n-1;   }
static inline int  Fra_LitSign( int n )           { return (n<0);               }

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

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

  Synopsis    [Returns 1 if simulation info is composed of all zeros.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fra_OneHotNodeIsConst( Fra_Sml_t * pSeq, Aig_Obj_t * pObj )
{
    unsigned * pSims;
    int i;
    pSims = Fra_ObjSim(pSeq, pObj->Id);
    for ( i = pSeq->nWordsPref; i < pSeq->nWordsTotal; i++ )
        if ( pSims[i] )
            return 0;
    return 1;
}

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

  Synopsis    [Returns 1 if simulation infos are equal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fra_OneHotNodesAreEqual( Fra_Sml_t * pSeq, Aig_Obj_t * pObj0, Aig_Obj_t * pObj1 )
{
    unsigned * pSims0, * pSims1;
    int i;
    pSims0 = Fra_ObjSim(pSeq, pObj0->Id);
    pSims1 = Fra_ObjSim(pSeq, pObj1->Id);
    for ( i = pSeq->nWordsPref; i < pSeq->nWordsTotal; i++ )
        if ( pSims0[i] != pSims1[i] )
            return 0;
    return 1;
}

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

  Synopsis    [Returns 1 if implications holds.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fra_OneHotNodesAreClause( Fra_Sml_t * pSeq, Aig_Obj_t * pObj1, Aig_Obj_t * pObj2, int fCompl1, int fCompl2 )
{
    unsigned * pSim1, * pSim2;
    int k;
    pSim1 = Fra_ObjSim(pSeq, pObj1->Id);
    pSim2 = Fra_ObjSim(pSeq, pObj2->Id);
    if ( fCompl1 && fCompl2 )
    {
        for ( k = pSeq->nWordsPref; k < pSeq->nWordsTotal; k++ )
            if ( pSim1[k] & pSim2[k] )
                return 0;
    }
    else if ( fCompl1 )
    {
        for ( k = pSeq->nWordsPref; k < pSeq->nWordsTotal; k++ )
            if ( pSim1[k] & ~pSim2[k] )
                return 0;
    }
    else if ( fCompl2 )
    {
        for ( k = pSeq->nWordsPref; k < pSeq->nWordsTotal; k++ )
            if ( ~pSim1[k] & pSim2[k] )
                return 0;
    }
    else
        assert( 0 );
    return 1;
}

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

  Synopsis    [Computes one-hot implications.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Fra_OneHotCompute( Fra_Man_t * p, Fra_Sml_t * pSim )
{
    int fSkipConstEqu = 1;
    Vec_Int_t * vOneHots;
    Aig_Obj_t * pObj1, * pObj2;
    int i, k;
    int nTruePis = Aig_ManCiNum(pSim->pAig) - Aig_ManRegNum(pSim->pAig);
    assert( pSim->pAig == p->pManAig );
    vOneHots = Vec_IntAlloc( 100 );
    Aig_ManForEachLoSeq( pSim->pAig, pObj1, i )
    {
        if ( fSkipConstEqu && Fra_OneHotNodeIsConst(pSim, pObj1) )
            continue;
        assert( i-nTruePis >= 0 );
//        Aig_ManForEachLoSeq( pSim->pAig, pObj2, k )
//        Vec_PtrForEachEntryStart( Aig_Obj_t *, pSim->pAig->vPis, pObj2, k, Aig_ManCiNum(p)-Aig_ManRegNum(p) )
        Vec_PtrForEachEntryStart( Aig_Obj_t *, pSim->pAig->vCis, pObj2, k, i+1 )
        {
            if ( fSkipConstEqu && Fra_OneHotNodeIsConst(pSim, pObj2) )
                continue;
            if ( fSkipConstEqu && Fra_OneHotNodesAreEqual( pSim, pObj1, pObj2 ) )
                continue;
            assert( k-nTruePis >= 0 );
            if ( Fra_OneHotNodesAreClause( pSim, pObj1, pObj2, 1, 1 ) )
            {
                Vec_IntPush( vOneHots, Fra_RegToLit(i-nTruePis, 1) );
                Vec_IntPush( vOneHots, Fra_RegToLit(k-nTruePis, 1) );
                continue;
            }
            if ( Fra_OneHotNodesAreClause( pSim, pObj1, pObj2, 0, 1 ) )
            {
                Vec_IntPush( vOneHots, Fra_RegToLit(i-nTruePis, 0) );
                Vec_IntPush( vOneHots, Fra_RegToLit(k-nTruePis, 1) );
                continue;
            }
            if ( Fra_OneHotNodesAreClause( pSim, pObj1, pObj2, 1, 0 ) )
            {
                Vec_IntPush( vOneHots, Fra_RegToLit(i-nTruePis, 1) );
                Vec_IntPush( vOneHots, Fra_RegToLit(k-nTruePis, 0) );
                continue;
            }
        }
    }
    return vOneHots;
}

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

  Synopsis    [Assumes one-hot implications in the SAT solver.]

  Description []
               
  SideEffects []

  SeeAlso     []

**********************************************************************/
void Fra_OneHotAssume( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    Aig_Obj_t * pObj1, * pObj2;
    int i, Out1, Out2, pLits[2];
    int nPiNum = Aig_ManCiNum(p->pManFraig) - Aig_ManRegNum(p->pManFraig);
    assert( p->pPars->nFramesK == 1 ); // add to only one frame
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
        pObj1 = Aig_ManCi( p->pManFraig, nPiNum + Fra_LitReg(Out1) );
        pObj2 = Aig_ManCi( p->pManFraig, nPiNum + Fra_LitReg(Out2) );
        pLits[0] = toLitCond( Fra_ObjSatNum(pObj1), Fra_LitSign(Out1) );
        pLits[1] = toLitCond( Fra_ObjSatNum(pObj2), Fra_LitSign(Out2) );
        // add constraint to solver
        if ( !sat_solver_addclause( p->pSat, pLits, pLits + 2 ) )
        {
            printf( "Fra_OneHotAssume(): Adding clause makes SAT solver unsat.\n" );
            sat_solver_delete( p->pSat );
            p->pSat = NULL;
            return;
        }
    }
}

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

  Synopsis    [Checks one-hot implications.]

  Description []
               
  SideEffects []

  SeeAlso     []

**********************************************************************/
void Fra_OneHotCheck( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    Aig_Obj_t * pObj1, * pObj2;
    int RetValue, i, Out1, Out2;
    int nTruePos = Aig_ManCoNum(p->pManFraig) - Aig_ManRegNum(p->pManFraig);
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
        pObj1 = Aig_ManCo( p->pManFraig, nTruePos + Fra_LitReg(Out1) );
        pObj2 = Aig_ManCo( p->pManFraig, nTruePos + Fra_LitReg(Out2) );
        RetValue = Fra_NodesAreClause( p, pObj1, pObj2, Fra_LitSign(Out1), Fra_LitSign(Out2) );
        if ( RetValue != 1 )
        {
            p->pCla->fRefinement = 1;
            if ( RetValue == 0 )
                Fra_SmlResimulate( p );
            if ( Vec_IntEntry(vOneHots, i) != 0 )
                printf( "Fra_OneHotCheck(): Clause is not refined!\n" );
            assert( Vec_IntEntry(vOneHots, i) == 0 );
        }
    }
}

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

  Synopsis    [Removes those implications that no longer hold.]

  Description [Returns 1 if refinement has happened.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fra_OneHotRefineUsingCex( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    Aig_Obj_t * pObj1, * pObj2;
    int i, Out1, Out2, RetValue = 0;
    int nPiNum = Aig_ManCiNum(p->pManAig) - Aig_ManRegNum(p->pManAig);
    assert( p->pSml->pAig == p->pManAig );
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
        // get the corresponding nodes
        pObj1 = Aig_ManCi( p->pManAig, nPiNum + Fra_LitReg(Out1) );
        pObj2 = Aig_ManCi( p->pManAig, nPiNum + Fra_LitReg(Out2) );
        // check if implication holds using this simulation info
        if ( !Fra_OneHotNodesAreClause( p->pSml, pObj1, pObj2, Fra_LitSign(Out1), Fra_LitSign(Out2) ) )
        {
            Vec_IntWriteEntry( vOneHots, i, 0 );
            Vec_IntWriteEntry( vOneHots, i+1, 0 );
            RetValue = 1;
        }
    }
    return RetValue;
}

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

  Synopsis    [Removes those implications that no longer hold.]

  Description [Returns 1 if refinement has happened.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Fra_OneHotCount( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    int i, Out1, Out2, Counter = 0;
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
        Counter++;
    }
    return Counter;
}

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

  Synopsis    [Estimates the coverage of state space by clauses.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Fra_OneHotEstimateCoverage( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    int nSimWords = (1<<14);
    int nRegs = Aig_ManRegNum(p->pManAig);
    Vec_Ptr_t * vSimInfo;
    unsigned * pSim1, * pSim2, * pSimTot;
    int i, w, Out1, Out2, nCovered, Counter = 0;
    abctime clk = Abc_Clock();

    // generate random sim-info at register outputs
    vSimInfo = Vec_PtrAllocSimInfo( nRegs + 1, nSimWords );
//    srand( 0xAABBAABB );
    Aig_ManRandom(1);
    for ( i = 0; i < nRegs; i++ )
    {
        pSim1 = (unsigned *)Vec_PtrEntry( vSimInfo, i );
        for ( w = 0; w < nSimWords; w++ )
            pSim1[w] = Fra_ObjRandomSim();
    }
    pSimTot = (unsigned *)Vec_PtrEntry( vSimInfo, nRegs );

    // collect simulation info
    memset( pSimTot, 0, sizeof(unsigned) * nSimWords );
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
//printf( "(%c%d,%c%d) ", 
//Fra_LitSign(Out1)? '-': '+', Fra_LitReg(Out1), 
//Fra_LitSign(Out2)? '-': '+', Fra_LitReg(Out2) ); 
        Counter++;
        pSim1 = (unsigned *)Vec_PtrEntry( vSimInfo, Fra_LitReg(Out1) );
        pSim2 = (unsigned *)Vec_PtrEntry( vSimInfo, Fra_LitReg(Out2) );
        if ( Fra_LitSign(Out1) && Fra_LitSign(Out2) )
            for ( w = 0; w < nSimWords; w++ )
                pSimTot[w] |=  pSim1[w] &  pSim2[w];
        else if ( Fra_LitSign(Out1) )
            for ( w = 0; w < nSimWords; w++ )
                pSimTot[w] |=  pSim1[w] & ~pSim2[w];
        else if ( Fra_LitSign(Out2) )
            for ( w = 0; w < nSimWords; w++ )
                pSimTot[w] |= ~pSim1[w] &  pSim2[w];
        else
            assert( 0 );
    }
//printf( "\n" );
    // count the total number of patterns contained in the don't-care
    nCovered = 0;
    for ( w = 0; w < nSimWords; w++ )
        nCovered += Aig_WordCountOnes( pSimTot[w] );
    Vec_PtrFree( vSimInfo );
    // print the result
    printf( "Care states ratio = %f. ", 1.0 * (nSimWords * 32 - nCovered) / (nSimWords * 32) );
    printf( "(%d out of %d patterns)  ", nSimWords * 32 - nCovered, nSimWords * 32 );
    ABC_PRT( "Time", Abc_Clock() - clk );
}

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

  Synopsis    [Creates one-hotness EXDC.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Aig_Man_t * Fra_OneHotCreateExdc( Fra_Man_t * p, Vec_Int_t * vOneHots )
{
    Aig_Man_t * pNew;
    Aig_Obj_t * pObj1, * pObj2, * pObj;
    int i, Out1, Out2, nTruePis;
    pNew = Aig_ManStart( Vec_IntSize(vOneHots)/2 );
//    for ( i = 0; i < Aig_ManRegNum(p->pManAig); i++ )
//        Aig_ObjCreateCi(pNew);
    Aig_ManForEachCi( p->pManAig, pObj, i )
        Aig_ObjCreateCi(pNew);
    nTruePis = Aig_ManCiNum(p->pManAig) - Aig_ManRegNum(p->pManAig);
    for ( i = 0; i < Vec_IntSize(vOneHots); i += 2 )
    {
        Out1 = Vec_IntEntry( vOneHots, i );
        Out2 = Vec_IntEntry( vOneHots, i+1 );
        if ( Out1 == 0 && Out2 == 0 )
            continue;
        pObj1 = Aig_ManCi( pNew, nTruePis + Fra_LitReg(Out1) );
        pObj2 = Aig_ManCi( pNew, nTruePis + Fra_LitReg(Out2) );
        pObj1 = Aig_NotCond( pObj1, Fra_LitSign(Out1) );
        pObj2 = Aig_NotCond( pObj2, Fra_LitSign(Out2) );
        pObj  = Aig_Or( pNew, pObj1, pObj2 );
        Aig_ObjCreateCo( pNew, pObj );
    }
    Aig_ManCleanup(pNew);
//    printf( "Created AIG with %d nodes and %d outputs.\n", Aig_ManNodeNum(pNew), Aig_ManCoNum(pNew) );
    return pNew;
}


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

  Synopsis    [Assumes one-hot implications in the SAT solver.]

  Description []
               
  SideEffects []

  SeeAlso     []

**********************************************************************/
void Fra_OneHotAddKnownConstraint( Fra_Man_t * p, Vec_Ptr_t * vOnehots )
{
    Vec_Int_t * vGroup;
    Aig_Obj_t * pObj1, * pObj2;
    int k, i, j, Out1, Out2, pLits[2];
    //
    // these constrants should be added to different timeframes!
    // (also note that PIs follow first - then registers)
    //
    Vec_PtrForEachEntry( Vec_Int_t *, vOnehots, vGroup, k )
    {
        Vec_IntForEachEntry( vGroup, Out1, i )
        Vec_IntForEachEntryStart( vGroup, Out2, j, i+1 )
        {
            pObj1 = Aig_ManCi( p->pManFraig, Out1 );
            pObj2 = Aig_ManCi( p->pManFraig, Out2 );
            pLits[0] = toLitCond( Fra_ObjSatNum(pObj1), 1 );
            pLits[1] = toLitCond( Fra_ObjSatNum(pObj2), 1 );
            // add constraint to solver
            if ( !sat_solver_addclause( p->pSat, pLits, pLits + 2 ) )
            {
                printf( "Fra_OneHotAddKnownConstraint(): Adding clause makes SAT solver unsat.\n" );
                sat_solver_delete( p->pSat );
                p->pSat = NULL;
                return;
            }
        }
    }
}


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


ABC_NAMESPACE_IMPL_END