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

  FileName    [ifSelect.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [FPGA mapping based on priority cuts.]

  Synopsis    [Cut selection procedures.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - November 21, 2006.]

  Revision    [$Id: ifSelect.c,v 1.00 2006/11/21 00:00:00 alanmi Exp $]

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

#include "if.h"
#include "sat/bsat/satSolver.h"

ABC_NAMESPACE_IMPL_START


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

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

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

  Synopsis    [Prints the logic cone with choices.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_ObjConePrint_rec( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Ptr_t * vVisited )
{
    If_Cut_t * pCut;
    pCut = If_ObjCutBest(pIfObj);
    if ( If_CutDataInt(pCut) )
        return;
    Vec_PtrPush( vVisited, pCut );
    // insert the worst case
    If_CutSetDataInt( pCut, ~0 );
    // skip in case of primary input
    if ( If_ObjIsCi(pIfObj) )
        return;
    // compute the functions of the children
    if ( pIfObj->pEquiv ) 
    If_ObjConePrint_rec( pIfMan, pIfObj->pEquiv, vVisited );
    If_ObjConePrint_rec( pIfMan, pIfObj->pFanin0, vVisited );
    If_ObjConePrint_rec( pIfMan, pIfObj->pFanin1, vVisited );
    printf( "%5d = %5d & %5d | %5d\n", pIfObj->Id, pIfObj->pFanin0->Id, pIfObj->pFanin1->Id, pIfObj->pEquiv ? pIfObj->pEquiv->Id : 0 );
}
void If_ObjConePrint( If_Man_t * pIfMan, If_Obj_t * pIfObj )
{
    If_Cut_t * pCut;
    If_Obj_t * pLeaf;
    int i;
    Vec_PtrClear( pIfMan->vTemp );
    If_ObjConePrint_rec( pIfMan, pIfObj, pIfMan->vTemp );
    Vec_PtrForEachEntry( If_Cut_t *, pIfMan->vTemp, pCut, i )
        If_CutSetDataInt( pCut, 0 );
    // print the leaf variables
    printf( "Cut " );
    pCut = If_ObjCutBest(pIfObj);
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        printf( "%d ", pLeaf->Id );
    printf( "\n" );
}


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

  Synopsis    [Recursively derives local AIG for the cut.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_ManNodeShapeMap_rec( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Ptr_t * vVisited, Vec_Int_t * vShape )
{
    If_Cut_t * pCut;
    If_Obj_t * pTemp;
    int i, iFunc0, iFunc1;
    // get the best cut
    pCut = If_ObjCutBest(pIfObj);
    // if the cut is visited, return the result
    if ( If_CutDataInt(pCut) )
        return If_CutDataInt(pCut);
    // mark the node as visited
    Vec_PtrPush( vVisited, pCut );
    // insert the worst case
    If_CutSetDataInt( pCut, ~0 );
    // skip in case of primary input
    if ( If_ObjIsCi(pIfObj) )
        return If_CutDataInt(pCut);
    // compute the functions of the children
    for ( i = 0, pTemp = pIfObj; pTemp; pTemp = pTemp->pEquiv, i++ )
    {
        iFunc0 = If_ManNodeShapeMap_rec( pIfMan, pTemp->pFanin0, vVisited, vShape );
        if ( iFunc0 == ~0 )
            continue;
        iFunc1 = If_ManNodeShapeMap_rec( pIfMan, pTemp->pFanin1, vVisited, vShape );
        if ( iFunc1 == ~0 )
            continue;
        // both branches are solved
        Vec_IntPush( vShape, pIfObj->Id );
        Vec_IntPush( vShape, pTemp->Id );
        If_CutSetDataInt( pCut, 1 );
        break;
    }
    return If_CutDataInt(pCut);
}
int If_ManNodeShapeMap( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape )
{
    If_Cut_t * pCut;
    If_Obj_t * pLeaf;
    int i, iRes;
    // get the best cut
    pCut = If_ObjCutBest(pIfObj);
    assert( pCut->nLeaves > 1 );
    // set the leaf variables
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
    {
        assert( If_CutDataInt( If_ObjCutBest(pLeaf) ) == 0 );
        If_CutSetDataInt( If_ObjCutBest(pLeaf), 1 );
    }
    // recursively compute the function while collecting visited cuts
    Vec_IntClear( vShape );
    Vec_PtrClear( pIfMan->vTemp );
    iRes = If_ManNodeShapeMap_rec( pIfMan, pIfObj, pIfMan->vTemp, vShape ); 
    if ( iRes == ~0 )
    {
        Abc_Print( -1, "If_ManNodeShapeMap(): Computing local AIG has failed.\n" );
        return 0;
    }
    // clean the cuts
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        If_CutSetDataInt( If_ObjCutBest(pLeaf), 0 );
    Vec_PtrForEachEntry( If_Cut_t *, pIfMan->vTemp, pCut, i )
        If_CutSetDataInt( pCut, 0 );
    return 1;
}


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

  Synopsis    [Recursively derives the local AIG for the cut.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int  If_WordCountOnes( unsigned uWord )
{
    uWord = (uWord & 0x55555555) + ((uWord>>1) & 0x55555555);
    uWord = (uWord & 0x33333333) + ((uWord>>2) & 0x33333333);
    uWord = (uWord & 0x0F0F0F0F) + ((uWord>>4) & 0x0F0F0F0F);
    uWord = (uWord & 0x00FF00FF) + ((uWord>>8) & 0x00FF00FF);
    return  (uWord & 0x0000FFFF) + (uWord>>16);
}
int If_ManNodeShapeMap2_rec( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Ptr_t * vVisited, Vec_Int_t * vShape )
{
    If_Cut_t * pCut;
    If_Obj_t * pTemp, * pTempBest = NULL;
    int i, iFunc, iFunc0, iFunc1, iBest = 0;
    // get the best cut
    pCut = If_ObjCutBest(pIfObj);
    // if the cut is visited, return the result
    if ( If_CutDataInt(pCut) )
        return If_CutDataInt(pCut);
    // mark the node as visited
    Vec_PtrPush( vVisited, pCut );
    // insert the worst case
    If_CutSetDataInt( pCut, ~0 );
    // skip in case of primary input
    if ( If_ObjIsCi(pIfObj) )
        return If_CutDataInt(pCut);
    // compute the functions of the children
    for ( i = 0, pTemp = pIfObj; pTemp; pTemp = pTemp->pEquiv, i++ )
    {
        iFunc0 = If_ManNodeShapeMap2_rec( pIfMan, pTemp->pFanin0, vVisited, vShape );
        if ( iFunc0 == ~0 )
            continue;
        iFunc1 = If_ManNodeShapeMap2_rec( pIfMan, pTemp->pFanin1, vVisited, vShape );
        if ( iFunc1 == ~0 )
            continue;
        iFunc = iFunc0 | iFunc1;
//        if ( If_WordCountOnes(iBest) <= If_WordCountOnes(iFunc) )
        if ( iBest < iFunc )
        {
            iBest = iFunc;
            pTempBest = pTemp;
        }
    }
    if ( pTempBest )
    {
        Vec_IntPush( vShape, pIfObj->Id );
        Vec_IntPush( vShape, pTempBest->Id );
        If_CutSetDataInt( pCut, iBest );
    }
    return If_CutDataInt(pCut);
}
int If_ManNodeShapeMap2( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape )
{
    If_Cut_t * pCut;
    If_Obj_t * pLeaf;
    int i, iRes;
    // get the best cut
    pCut = If_ObjCutBest(pIfObj);
    assert( pCut->nLeaves > 1 );
    // set the leaf variables
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        If_CutSetDataInt( If_ObjCutBest(pLeaf), (1 << i) );
    // recursively compute the function while collecting visited cuts
    Vec_IntClear( vShape );
    Vec_PtrClear( pIfMan->vTemp );
    iRes = If_ManNodeShapeMap2_rec( pIfMan, pIfObj, pIfMan->vTemp, vShape ); 
    if ( iRes == ~0 )
    {
        Abc_Print( -1, "If_ManNodeShapeMap2(): Computing local AIG has failed.\n" );
        return 0;
    }
    // clean the cuts
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        If_CutSetDataInt( If_ObjCutBest(pLeaf), 0 );
    Vec_PtrForEachEntry( If_Cut_t *, pIfMan->vTemp, pCut, i )
        If_CutSetDataInt( pCut, 0 );
    return 1;
}



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

  Synopsis    [Collects logic cone with choices]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_ManConeCollect_rec( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Ptr_t * vVisited, Vec_Ptr_t * vCone )
{
    If_Cut_t * pCut;
    If_Obj_t * pTemp;
    int iFunc0, iFunc1;
    int fRootAdded = 0;
    int fNodeAdded = 0;
    // get the best cut
    pCut = If_ObjCutBest(pIfObj);
    // if the cut is visited, return the result
    if ( If_CutDataInt(pCut) )
        return If_CutDataInt(pCut);
    // mark the node as visited
    Vec_PtrPush( vVisited, pCut );
    // insert the worst case
    If_CutSetDataInt( pCut, ~0 );
    // skip in case of primary input
    if ( If_ObjIsCi(pIfObj) )
        return If_CutDataInt(pCut);
    // compute the functions of the children
    for ( pTemp = pIfObj; pTemp; pTemp = pTemp->pEquiv )
    {
        iFunc0 = If_ManConeCollect_rec( pIfMan, pTemp->pFanin0, vVisited, vCone );
        if ( iFunc0 == ~0 )
            continue;
        iFunc1 = If_ManConeCollect_rec( pIfMan, pTemp->pFanin1, vVisited, vCone );
        if ( iFunc1 == ~0 )
            continue;
        fNodeAdded = 1;
        If_CutSetDataInt( pCut, 1 );
        Vec_PtrPush( vCone, pTemp );
        if ( fRootAdded == 0 && pTemp == pIfObj )
            fRootAdded = 1;
    }
    if ( fNodeAdded && !fRootAdded )
        Vec_PtrPush( vCone, pIfObj );
    return If_CutDataInt(pCut);
}
Vec_Ptr_t * If_ManConeCollect( If_Man_t * pIfMan, If_Obj_t * pIfObj, If_Cut_t * pCut )
{
    Vec_Ptr_t * vCone;
    If_Obj_t * pLeaf;
    int i, RetValue;
    // set the leaf variables
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
    {
        assert( If_CutDataInt( If_ObjCutBest(pLeaf) ) == 0 );
        If_CutSetDataInt( If_ObjCutBest(pLeaf), 1 );
    }
    // recursively compute the function while collecting visited cuts
    vCone = Vec_PtrAlloc( 100 );
    Vec_PtrClear( pIfMan->vTemp );
    RetValue = If_ManConeCollect_rec( pIfMan, pIfObj, pIfMan->vTemp, vCone ); 
    assert( RetValue );
    // clean the cuts
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        If_CutSetDataInt( If_ObjCutBest(pLeaf), 0 );
    Vec_PtrForEachEntry( If_Cut_t *, pIfMan->vTemp, pCut, i )
        If_CutSetDataInt( pCut, 0 );
    return vCone;
}

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

  Synopsis    [Adding clauses.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void sat_solver_add_choice( sat_solver * pSat, int iVar, Vec_Int_t * vVars )
{
    int * pVars = Vec_IntArray(vVars);
    int nVars = Vec_IntSize(vVars);
    int i, k, Lits[2], Value;
    assert( Vec_IntSize(vVars) < Vec_IntCap(vVars) );
    // create literals
    for ( i = 0; i < nVars; i++ )
        pVars[i] = Abc_Var2Lit( pVars[i], 0 );
    pVars[i] = Abc_Var2Lit( iVar, 1 );
    // add clause
    Value = sat_solver_addclause( pSat, pVars, pVars + nVars + 1 );
    assert( Value );
    // undo literals
    for ( i = 0; i < nVars; i++ )
        pVars[i] = Abc_Lit2Var( pVars[i] );
    // add !out => !in
    Lits[0] = Abc_Var2Lit( iVar, 0 );
    for ( i = 0; i < nVars; i++ )
    {
        Lits[1] = Abc_Var2Lit( pVars[i], 1 );
        Value = sat_solver_addclause( pSat, Lits, Lits + 2 );
        assert( Value );
    }
    // add excluvisity
    for ( i = 0; i < nVars; i++ )
    for ( k = i+1; k < nVars; k++ )
    {
        Lits[0] = Abc_Var2Lit( pVars[i], 1 );
        Lits[1] = Abc_Var2Lit( pVars[k], 1 );
        Value = sat_solver_addclause( pSat, Lits, Lits + 2 );
        assert( Value );
    }
}
static inline int  If_ObjSatVar( If_Obj_t * pIfObj )            { return If_CutDataInt(If_ObjCutBest(pIfObj));  }
static inline void If_ObjSetSatVar( If_Obj_t * pIfObj, int v )  { If_CutSetDataInt( If_ObjCutBest(pIfObj), v ); }


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

  Synopsis    [Recursively derives the local AIG for the cut.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void If_ManNodeShape2_rec( sat_solver * pSat, If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape )
{
    If_Obj_t * pTemp;
    assert( sat_solver_var_value(pSat, If_ObjSatVar(pIfObj)) == 1 );
    if ( pIfObj->fMark )
        return;
    pIfObj->fMark = 1;
    for ( pTemp = pIfObj; pTemp; pTemp = pTemp->pEquiv )
        if ( sat_solver_var_value(pSat, If_ObjSatVar(pTemp)+1) == 1 )
            break;
    assert( pTemp != NULL );
    If_ManNodeShape2_rec( pSat, pIfMan, pTemp->pFanin0, vShape );
    If_ManNodeShape2_rec( pSat, pIfMan, pTemp->pFanin1, vShape );
    Vec_IntPush( vShape, pIfObj->Id );
    Vec_IntPush( vShape, pTemp->Id );
}

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

  Synopsis    [Solve the problem of selecting choices for the given cut.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_ManNodeShapeSat( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape )
{
    sat_solver * pSat;
    If_Cut_t * pCut;
    Vec_Ptr_t * vCone;
    Vec_Int_t * vFanins;
    If_Obj_t * pObj, * pTemp;
    int i, Lit, Status;

    // get best cut
    pCut = If_ObjCutBest(pIfObj);
    assert( pCut->nLeaves > 1 );

    // collect the cone
    vCone = If_ManConeCollect( pIfMan, pIfObj, pCut );

    // assign SAT variables
    // EXTERNAL variable is even numbered
    // INTERNAL variable is odd numbered
    If_CutForEachLeaf( pIfMan, pCut, pObj, i )
    {
        assert( If_ObjSatVar(pObj) == 0 );
        If_ObjSetSatVar( pObj, 2*(i+1) );
    }
    Vec_PtrForEachEntry( If_Obj_t *, vCone, pObj, i )
    {
        assert( If_ObjSatVar(pObj) == 0 );
        If_ObjSetSatVar( pObj, 2*(i+1+pCut->nLeaves) );
    }

    // start SAT solver
    pSat = sat_solver_new();
    sat_solver_setnvars( pSat, 2 * (pCut->nLeaves + Vec_PtrSize(vCone) + 1) );

    // add constraints
    vFanins = Vec_IntAlloc( 100 );
    Vec_PtrForEachEntry( If_Obj_t *, vCone, pObj, i )
    {
        assert( If_ObjIsAnd(pObj) );
        Vec_IntClear( vFanins );
        for ( pTemp = pObj; pTemp; pTemp = pTemp->pEquiv )
            if ( If_ObjSatVar(pTemp) )
                Vec_IntPush( vFanins, If_ObjSatVar(pTemp)+1 ); // internal
        assert( Vec_IntSize(vFanins) > 0 );
        sat_solver_add_choice( pSat, If_ObjSatVar(pObj), vFanins ); // external
        assert( If_ObjSatVar(pObj) > 0 );
//        sat_solver_add_and( pSat, If_ObjSatVar(pObj)+1, If_ObjSatVar(pObj->pFanin0), If_ObjSatVar(pObj->pFanin1), 0, 0 );
        if ( If_ObjSatVar(pObj->pFanin0) > 0 && If_ObjSatVar(pObj->pFanin1) > 0 )
        {
            int Lits[2];
            Lits[0] = Abc_Var2Lit( If_ObjSatVar(pObj)+1,        1 );
            Lits[1] = Abc_Var2Lit( If_ObjSatVar(pObj->pFanin0), 0 );
            Status = sat_solver_addclause( pSat, Lits, Lits + 2 );
            assert( Status );

            Lits[0] = Abc_Var2Lit( If_ObjSatVar(pObj)+1,        1 );
            Lits[1] = Abc_Var2Lit( If_ObjSatVar(pObj->pFanin1), 0 );
            Status = sat_solver_addclause( pSat, Lits, Lits + 2 );
            assert( Status );
        }
    }
    Vec_IntFree( vFanins );

    // set cut variables to 1
    pCut = If_ObjCutBest(pIfObj);
    If_CutForEachLeaf( pIfMan, pCut, pObj, i )
    {
        Lit = Abc_Var2Lit( If_ObjSatVar(pObj), 0 ); // external
        Status = sat_solver_addclause( pSat, &Lit, &Lit + 1 );
        assert( Status );
    }
    // set output variable to 1
    Lit = Abc_Var2Lit( If_ObjSatVar(pIfObj), 0 ); // external
    Status = sat_solver_addclause( pSat, &Lit, &Lit + 1 );
    assert( Status );

    // solve the problem
    Status = sat_solver_solve( pSat, NULL, NULL, 0, 0, 0, 0 );
    assert( Status == l_True );

    // mark cut nodes
    If_CutForEachLeaf( pIfMan, pCut, pObj, i )
    {
        assert( pObj->fMark == 0 );
        pObj->fMark = 1;
    }

    // select the node's shape
    Vec_IntClear( vShape );
    assert( pIfObj->fMark == 0 );
    If_ManNodeShape2_rec( pSat, pIfMan, pIfObj, vShape ); 

    // cleanup
    sat_solver_delete( pSat );
    If_CutForEachLeaf( pIfMan, pCut, pObj, i )
    {
        If_ObjSetSatVar( pObj, 0 );
        pObj->fMark = 0;
    }
    Vec_PtrForEachEntry( If_Obj_t *, vCone, pObj, i )
    {
        If_ObjSetSatVar( pObj, 0 );
        pObj->fMark = 0;
    }
    Vec_PtrFree( vCone );
    return 1;
}


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

  Synopsis    [Verify that the shape is correct.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_ManCheckShape( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape )
{
    If_Cut_t * pCut;
    If_Obj_t * pLeaf;
    int i, Entry1, Entry2, RetValue = 1;
    // check that the marks are not set
    pCut = If_ObjCutBest(pIfObj);
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        assert( pLeaf->fMark == 0 );
    // set the marks of the shape
    Vec_IntForEachEntryDouble( vShape, Entry1, Entry2, i )
    {
        pLeaf = If_ManObj(pIfMan, Entry2);
        pLeaf->pFanin0->fMark = 1;
        pLeaf->pFanin1->fMark = 1;
    }
    // check that the leaves are marked
    If_CutForEachLeaf( pIfMan, pCut, pLeaf, i )
        if ( pLeaf->fMark == 0 )
            RetValue = 0;
        else
            pLeaf->fMark = 0;
    // clean the inner marks
    Vec_IntForEachEntryDouble( vShape, Entry1, Entry2, i )
    {
        pLeaf = If_ManObj(pIfMan, Entry2);
        pLeaf->pFanin0->fMark = 0;
        pLeaf->pFanin1->fMark = 0;
    }
    return RetValue;
}

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

  Synopsis    [Recursively compute the set of nodes supported by the cut.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int If_ManNodeShape( If_Man_t * pIfMan, If_Obj_t * pIfObj, Vec_Int_t * vShape, int fExact )
{
    int RetValue;
//    if ( pIfMan->nChoices == 0 )
    {
        RetValue = If_ManNodeShapeMap( pIfMan, pIfObj, vShape );
        assert( RetValue );
        if ( !fExact || If_ManCheckShape(pIfMan, pIfObj, vShape) )
            return 1;
    }
//    if ( pIfObj->Id == 1254 && If_ObjCutBest(pIfObj)->nLeaves == 7 )
//        If_ObjConePrint( pIfMan, pIfObj );
    RetValue = If_ManNodeShapeMap2( pIfMan, pIfObj, vShape );
    assert( RetValue );
    RetValue = If_ManCheckShape(pIfMan, pIfObj, vShape);
//    assert( RetValue );
//    printf( "%d", RetValue );
    return 1;
}

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


ABC_NAMESPACE_IMPL_END