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

  FileName    [giaSupp.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Scalable AIG package.]

  Synopsis    [Support minimization for AIGs.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

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

#ifdef ABC_USE_CUDD
#include "bdd/extrab/extraBdd.h"
#endif

ABC_NAMESPACE_IMPL_START

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

#ifdef ABC_USE_CUDD

struct Gia_ManMin_t_ 
{
    // problem formulation
    Gia_Man_t *     pGia;
    int             iLits[2];
    // structural information
    Vec_Int_t *     vCis[2];
    Vec_Int_t *     vObjs[2];
    Vec_Int_t *     vCleared;  
    // intermediate functions
    DdManager *     dd;
    Vec_Ptr_t *     vFuncs;
    Vec_Int_t *     vSupp;
};

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

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

  Synopsis    [Create/delete the data representation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_ManMin_t * Gia_ManSuppStart( Gia_Man_t * pGia )
{
    Gia_ManMin_t * p;
    p = ABC_CALLOC( Gia_ManMin_t, 1 );
    p->pGia     = pGia;
    p->vCis[0]  = Vec_IntAlloc( 512 );
    p->vCis[1]  = Vec_IntAlloc( 512 );
    p->vObjs[0] = Vec_IntAlloc( 512 );
    p->vObjs[1] = Vec_IntAlloc( 512 );
    p->vCleared = Vec_IntAlloc( 512 );
    p->dd = Cudd_Init( 0, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 );
//    Cudd_AutodynEnable( p->dd,  CUDD_REORDER_SYMM_SIFT );
    Cudd_AutodynDisable( p->dd );
    p->vFuncs   = Vec_PtrAlloc( 10000 );
    p->vSupp    = Vec_IntAlloc( 10000 );
    return p;
}
void Gia_ManSuppStop( Gia_ManMin_t * p )
{
    Vec_IntFreeP( &p->vCis[0] );
    Vec_IntFreeP( &p->vCis[1] );
    Vec_IntFreeP( &p->vObjs[0] );
    Vec_IntFreeP( &p->vObjs[1] );
    Vec_IntFreeP( &p->vCleared );
    Vec_PtrFreeP( &p->vFuncs );
    Vec_IntFreeP( &p->vSupp );
    printf( "Refs = %d. \n", Cudd_CheckZeroRef( p->dd ) );
    Cudd_Quit( p->dd );
    ABC_FREE( p );
}

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

  Synopsis    [Compute variables, which are not in the support.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManFindRemoved( Gia_ManMin_t * p ) 
{
    extern void ddSupportStep2( DdNode * f, int * support );
    extern void ddClearFlag2( DdNode * f );

    //int fVerbose = 1;
    int nBddLimit = 100000;
    int nPart0 = Vec_IntSize(p->vCis[0]);
    int n, i, iObj, nVars = 0;
    DdNode * bFunc0, * bFunc1, * bFunc;
    Vec_PtrFillExtra( p->vFuncs, Gia_ManObjNum(p->pGia), NULL );
    // assign variables
    for ( n = 0; n < 2; n++ )
        Vec_IntForEachEntry( p->vCis[n], iObj, i )
            Vec_PtrWriteEntry( p->vFuncs, iObj, Cudd_bddIthVar(p->dd, nVars++) );
    // create nodes
    for ( n = 0; n < 2; n++ )
        Vec_IntForEachEntry( p->vObjs[n], iObj, i )
        {
            Gia_Obj_t * pObj = Gia_ManObj( p->pGia, iObj );
            bFunc0 = Cudd_NotCond( (DdNode *)Vec_PtrEntry(p->vFuncs, Gia_ObjFaninId0(pObj, iObj)), Gia_ObjFaninC0(pObj) );
            bFunc1 = Cudd_NotCond( (DdNode *)Vec_PtrEntry(p->vFuncs, Gia_ObjFaninId1(pObj, iObj)), Gia_ObjFaninC1(pObj) );
            bFunc  = Cudd_bddAndLimit( p->dd, bFunc0, bFunc1, nBddLimit );  
            assert( bFunc != NULL );
            Cudd_Ref( bFunc );
            Vec_PtrWriteEntry( p->vFuncs, iObj, bFunc );
        }
    // create new node
    bFunc0 = Cudd_NotCond( (DdNode *)Vec_PtrEntry(p->vFuncs, Abc_Lit2Var(p->iLits[0])), Abc_LitIsCompl(p->iLits[0]) );
    bFunc1 = Cudd_NotCond( (DdNode *)Vec_PtrEntry(p->vFuncs, Abc_Lit2Var(p->iLits[1])), Abc_LitIsCompl(p->iLits[1]) );
    bFunc  = Cudd_bddAndLimit( p->dd, bFunc0, bFunc1, nBddLimit );  
    assert( bFunc != NULL );
    Cudd_Ref( bFunc );
    //if ( fVerbose ) Extra_bddPrint( p->dd, bFunc ), printf( "\n" );
    // collect support
    Vec_IntFill( p->vSupp, nVars, 0 );
    ddSupportStep2( Cudd_Regular(bFunc), Vec_IntArray(p->vSupp) );
    ddClearFlag2( Cudd_Regular(bFunc) );
    // find variables not present in the support
    Vec_IntClear( p->vCleared );
    for ( i = 0; i < nVars; i++ )
        if ( Vec_IntEntry(p->vSupp, i) == 0 )
            Vec_IntPush( p->vCleared, i < nPart0 ? Vec_IntEntry(p->vCis[0], i) : Vec_IntEntry(p->vCis[1], i-nPart0) );
    //printf( "%d(%d)%d  ", Cudd_SupportSize(p->dd, bFunc), Vec_IntSize(p->vCleared), Cudd_DagSize(bFunc) );
    // deref results
    Cudd_RecursiveDeref( p->dd, bFunc );
    for ( n = 0; n < 2; n++ )
        Vec_IntForEachEntry( p->vObjs[n], iObj, i )
            Cudd_RecursiveDeref( p->dd, (DdNode *)Vec_PtrEntry(p->vFuncs, iObj) );
    //Vec_IntPrint( p->vCleared );
    return Vec_IntSize(p->vCleared);
}

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

  Synopsis    [Compute variables, which are not in the support.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManRebuildOne( Gia_ManMin_t * p, int n ) 
{
    int i, iObj, iGiaLitNew = -1;
    Vec_Int_t * vTempIns = p->vCis[n]; 
    Vec_Int_t * vTempNds = p->vObjs[n];
    Vec_Int_t * vCopies  = &p->pGia->vCopies;
    Vec_IntFillExtra( vCopies, Gia_ManObjNum(p->pGia), -1 );
    assert( p->iLits[n] >= 2 );
    // process inputs
    Vec_IntForEachEntry( vTempIns, iObj, i )
        Vec_IntWriteEntry( vCopies, iObj, Abc_Var2Lit(iObj, 0) );
    // process constants
    assert( Vec_IntSize(p->vCleared) > 0 );
    Vec_IntForEachEntry( p->vCleared, iObj, i )
        Vec_IntWriteEntry( vCopies, iObj, 0 );
    if ( Vec_IntSize(vTempNds) == 0 )
        iGiaLitNew = Vec_IntEntry( vCopies, Abc_Lit2Var(p->iLits[n]) );
    else
    {
        Vec_IntForEachEntry( vTempNds, iObj, i )
        {
            Gia_Obj_t * pObj = Gia_ManObj( p->pGia, iObj );
            int iGiaLit0 = Vec_IntEntry( vCopies, Gia_ObjFaninId0p(p->pGia, pObj) );
            int iGiaLit1 = Vec_IntEntry( vCopies, Gia_ObjFaninId1p(p->pGia, pObj) );
            iGiaLit0   = Abc_LitNotCond( iGiaLit0, Gia_ObjFaninC0(pObj) );
            iGiaLit1   = Abc_LitNotCond( iGiaLit1, Gia_ObjFaninC1(pObj) );
            iGiaLitNew = Gia_ManHashAnd( p->pGia, iGiaLit0, iGiaLit1 );
            Vec_IntWriteEntry( vCopies, iObj, iGiaLitNew );
        }
        assert( Abc_Lit2Var(p->iLits[n]) == iObj );
    }
    return Abc_LitNotCond( iGiaLitNew, Abc_LitIsCompl(p->iLits[n]) );
}

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

  Synopsis    [Collect nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Gia_ManGatherSupp_rec( Gia_Man_t * p, int iObj, Vec_Int_t * vCis, Vec_Int_t * vObjs )
{
    int Val0, Val1;
    Gia_Obj_t * pObj;
    if ( Gia_ObjIsTravIdPreviousId(p, iObj) )
        return 1;
    if ( Gia_ObjIsTravIdCurrentId(p, iObj) )
        return 0;
    Gia_ObjSetTravIdCurrentId(p, iObj);
    pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsCi(pObj) )
    {
        Vec_IntPush( vCis, iObj );
        return 0;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Val0 = Gia_ManGatherSupp_rec( p, Gia_ObjFaninId0(pObj, iObj), vCis, vObjs );
    Val1 = Gia_ManGatherSupp_rec( p, Gia_ObjFaninId1(pObj, iObj), vCis, vObjs );
    Vec_IntPush( vObjs, iObj );
    return Val0 || Val1;
}
int Gia_ManGatherSupp( Gia_ManMin_t * p )
{
    int n, Overlap = 0;
    Gia_ManIncrementTravId( p->pGia );
    for ( n = 0; n < 2; n++ )
    {
        Vec_IntClear( p->vCis[n] );
        Vec_IntClear( p->vObjs[n] );
        Gia_ManIncrementTravId( p->pGia );
        Overlap = Gia_ManGatherSupp_rec( p->pGia, Abc_Lit2Var(p->iLits[n]), p->vCis[n], p->vObjs[n] );
        assert( n || !Overlap );
    }
    return Overlap;
}

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

  Synopsis    [Takes a literal and returns a support-minized literal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_ManSupportAnd( Gia_ManMin_t * p, int iLit0, int iLit1 )
{
    int iLitNew0, iLitNew1;
    p->iLits[0] = iLit0;
    p->iLits[1] = iLit1;
    if ( iLit0 < 2 || iLit1 < 2 || !Gia_ManGatherSupp(p) || !Gia_ManFindRemoved(p) )
        return Gia_ManHashAnd( p->pGia, iLit0, iLit1 );
    iLitNew0 = Gia_ManRebuildOne( p, 0 );
    iLitNew1 = Gia_ManRebuildOne( p, 1 );
    return Gia_ManHashAnd( p->pGia, iLitNew0, iLitNew1 );
}


#else

Gia_ManMin_t * Gia_ManSuppStart( Gia_Man_t * pGia )              { return NULL; }
int Gia_ManSupportAnd( Gia_ManMin_t * p, int iLit0, int iLit1 )  { return 0;    }
void Gia_ManSuppStop( Gia_ManMin_t * p )                         {}

#endif


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

  Synopsis    [Testbench.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_ManSupportAndTest( Gia_Man_t * pGia )
{
    Gia_ManMin_t * pMan;
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj;
    int i;
    Gia_ManFillValue( pGia );
    pNew = Gia_ManStart( Gia_ManObjNum(pGia) );
    pNew->pName = Abc_UtilStrsav( pGia->pName );
    pNew->pSpec = Abc_UtilStrsav( pGia->pSpec );
    Gia_ManHashAlloc( pNew );
    Gia_ManConst0(pGia)->Value = 0;
    pMan = Gia_ManSuppStart( pNew );
    Gia_ManForEachObj1( pGia, pObj, i )
    {
        if ( Gia_ObjIsAnd(pObj) )
        {
//            pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
            pObj->Value = Gia_ManSupportAnd( pMan, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
        }
        else if ( Gia_ObjIsCi(pObj) )
            pObj->Value = Gia_ManAppendCi( pNew );
        else if ( Gia_ObjIsCo(pObj) )
            pObj->Value = Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
        else assert( 0 );

        if ( i % 10000 == 0 )
            printf( "%d\n", i );
    }
    Gia_ManSuppStop( pMan );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(pGia) );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}




struct Gia_Man2Min_t_ 
{
    // problem formulation
    Gia_Man_t *     pGia;
    int             iLits[2];
    // structural information
    Vec_Int_t *     vCis[2];
    Vec_Int_t *     vObjs[2];
    // SAT solving
    satoko_t *      pSat;           // SAT solver
    Vec_Wrd_t *     vSims;          // simulation
    Vec_Ptr_t *     vFrontier;      // CNF construction
    Vec_Ptr_t *     vFanins;        // CNF construction
    Vec_Int_t *     vSatVars;       // nodes
    int             nCisOld;        // previous number of CIs
    int             iPattern;       // the next pattern to write
    int             nSatSat;
    int             nSatUnsat;
    int             nCalls;
    int             nSims;
    int             nSupps;
};

static inline int    Gia_Min2ObjSatId( Gia_Man_t * p, Gia_Obj_t * pObj )             { return Gia_ObjCopyArray(p, Gia_ObjId(p, pObj));                                                        }
static inline int    Gia_Min2ObjSetSatId( Gia_Man_t * p, Gia_Obj_t * pObj, int Num ) { assert(Gia_Min2ObjSatId(p, pObj) == -1); Gia_ObjSetCopyArray(p, Gia_ObjId(p, pObj), Num); return Num;  }
static inline void   Gia_Min2ObjCleanSatId( Gia_Man_t * p, Gia_Obj_t * pObj )        { assert(Gia_Min2ObjSatId(p, pObj) != -1); Gia_ObjSetCopyArray(p, Gia_ObjId(p, pObj), -1);               }


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

  Synopsis    [Create/delete the data representation manager.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man2Min_t * Gia_Man2SuppStart( Gia_Man_t * pGia )
{
    Gia_Man2Min_t * p;
    p = ABC_CALLOC( Gia_Man2Min_t, 1 );
    p->pGia      = pGia;
    p->vCis[0]   = Vec_IntAlloc( 512 );
    p->vCis[1]   = Vec_IntAlloc( 512 );
    p->vObjs[0]  = Vec_IntAlloc( 512 );
    p->vObjs[1]  = Vec_IntAlloc( 512 );
    // SAT solving
    p->pSat      = satoko_create();
    p->vSims     = Vec_WrdAlloc( 1000 );
    p->vFrontier = Vec_PtrAlloc( 1000 );
    p->vFanins   = Vec_PtrAlloc( 100 );
    p->vSatVars  = Vec_IntAlloc( 100 );
    p->iPattern  = 1;
    satoko_options(p->pSat)->learnt_ratio = 0; // prevent garbage collection
    return p;
}
void Gia_Man2SuppStop( Gia_Man2Min_t * p )
{
//    printf( "Total calls = %8d.  Supps = %6d.  Sims = %6d.   SAT = %6d.  UNSAT = %6d.\n", 
//        p->nCalls, p->nSupps, p->nSims, p->nSatSat, p->nSatUnsat );
    Vec_IntFreeP( &p->vCis[0] );
    Vec_IntFreeP( &p->vCis[1] );
    Vec_IntFreeP( &p->vObjs[0] );
    Vec_IntFreeP( &p->vObjs[1] );
    Gia_ManCleanMark01( p->pGia );
    satoko_destroy( p->pSat );
    Vec_WrdFreeP( &p->vSims );
    Vec_PtrFreeP( &p->vFrontier );
    Vec_PtrFreeP( &p->vFanins );
    Vec_IntFreeP( &p->vSatVars );
    ABC_FREE( p );
}

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

  Synopsis    [Adds clauses to the solver.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_Min2AddClausesMux( Gia_Man_t * p, Gia_Obj_t * pNode, satoko_t * pSat )
{
    int fPolarFlip = 0;
    Gia_Obj_t * pNodeI, * pNodeT, * pNodeE;
    int pLits[4], RetValue, VarF, VarI, VarT, VarE, fCompT, fCompE;

    assert( !Gia_IsComplement( pNode ) );
    assert( pNode->fMark0 );
    // get nodes (I = if, T = then, E = else)
    pNodeI = Gia_ObjRecognizeMux( pNode, &pNodeT, &pNodeE );
    // get the variable numbers
    VarF = Gia_Min2ObjSatId(p, pNode);
    VarI = Gia_Min2ObjSatId(p, pNodeI);
    VarT = Gia_Min2ObjSatId(p, Gia_Regular(pNodeT));
    VarE = Gia_Min2ObjSatId(p, Gia_Regular(pNodeE));
    // get the complementation flags
    fCompT = Gia_IsComplement(pNodeT);
    fCompE = Gia_IsComplement(pNodeE);

    // f = ITE(i, t, e)

    // i' + t' + f
    // i' + t  + f'
    // i  + e' + f
    // i  + e  + f'

    // create four clauses
    pLits[0] = Abc_Var2Lit(VarI, 1);
    pLits[1] = Abc_Var2Lit(VarT, 1^fCompT);
    pLits[2] = Abc_Var2Lit(VarF, 0);
    if ( fPolarFlip )
    {
        if ( pNodeI->fPhase )               pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeT)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );
    pLits[0] = Abc_Var2Lit(VarI, 1);
    pLits[1] = Abc_Var2Lit(VarT, 0^fCompT);
    pLits[2] = Abc_Var2Lit(VarF, 1);
    if ( fPolarFlip )
    {
        if ( pNodeI->fPhase )               pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeT)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );
    pLits[0] = Abc_Var2Lit(VarI, 0);
    pLits[1] = Abc_Var2Lit(VarE, 1^fCompE);
    pLits[2] = Abc_Var2Lit(VarF, 0);
    if ( fPolarFlip )
    {
        if ( pNodeI->fPhase )               pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeE)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );
    pLits[0] = Abc_Var2Lit(VarI, 0);
    pLits[1] = Abc_Var2Lit(VarE, 0^fCompE);
    pLits[2] = Abc_Var2Lit(VarF, 1);
    if ( fPolarFlip )
    {
        if ( pNodeI->fPhase )               pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeE)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );

    // two additional clauses
    // t' & e' -> f'
    // t  & e  -> f 

    // t  + e   + f'
    // t' + e'  + f 

    if ( VarT == VarE )
    {
//        assert( fCompT == !fCompE );
        return;
    }

    pLits[0] = Abc_Var2Lit(VarT, 0^fCompT);
    pLits[1] = Abc_Var2Lit(VarE, 0^fCompE);
    pLits[2] = Abc_Var2Lit(VarF, 1);
    if ( fPolarFlip )
    {
        if ( Gia_Regular(pNodeT)->fPhase )  pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeE)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );
    pLits[0] = Abc_Var2Lit(VarT, 1^fCompT);
    pLits[1] = Abc_Var2Lit(VarE, 1^fCompE);
    pLits[2] = Abc_Var2Lit(VarF, 0);
    if ( fPolarFlip )
    {
        if ( Gia_Regular(pNodeT)->fPhase )  pLits[0] = Abc_LitNot( pLits[0] );
        if ( Gia_Regular(pNodeE)->fPhase )  pLits[1] = Abc_LitNot( pLits[1] );
        if ( pNode->fPhase )                pLits[2] = Abc_LitNot( pLits[2] );
    }
    RetValue = satoko_add_clause( pSat, pLits, 3 );
    assert( RetValue );
}
void Gia_Min2AddClausesSuper( Gia_Man_t * p, Gia_Obj_t * pNode, Vec_Ptr_t * vSuper, satoko_t * pSat )
{
    int fPolarFlip = 0;
    Gia_Obj_t * pFanin;
    int * pLits, nLits, RetValue, i;
    assert( !Gia_IsComplement(pNode) );
    assert( Gia_ObjIsAnd( pNode ) );
    // create storage for literals
    nLits = Vec_PtrSize(vSuper) + 1;
    pLits = ABC_ALLOC( int, nLits );
    // suppose AND-gate is A & B = C
    // add !A => !C   or   A + !C
    Vec_PtrForEachEntry( Gia_Obj_t *, vSuper, pFanin, i )
    {
        pLits[0] = Abc_Var2Lit(Gia_Min2ObjSatId(p, Gia_Regular(pFanin)), Gia_IsComplement(pFanin));
        pLits[1] = Abc_Var2Lit(Gia_Min2ObjSatId(p, pNode), 1);
        if ( fPolarFlip )
        {
            if ( Gia_Regular(pFanin)->fPhase )  pLits[0] = Abc_LitNot( pLits[0] );
            if ( pNode->fPhase )                pLits[1] = Abc_LitNot( pLits[1] );
        }
        RetValue = satoko_add_clause( pSat, pLits, 2 );
        assert( RetValue );
    }
    // add A & B => C   or   !A + !B + C
    Vec_PtrForEachEntry( Gia_Obj_t *, vSuper, pFanin, i )
    {
        pLits[i] = Abc_Var2Lit(Gia_Min2ObjSatId(p, Gia_Regular(pFanin)), !Gia_IsComplement(pFanin));
        if ( fPolarFlip )
        {
            if ( Gia_Regular(pFanin)->fPhase )  pLits[i] = Abc_LitNot( pLits[i] );
        }
    }
    pLits[nLits-1] = Abc_Var2Lit(Gia_Min2ObjSatId(p, pNode), 0);
    if ( fPolarFlip )
    {
        if ( pNode->fPhase )  pLits[nLits-1] = Abc_LitNot( pLits[nLits-1] );
    }
    RetValue = satoko_add_clause( pSat, pLits, nLits );
    assert( RetValue );
    ABC_FREE( pLits );
}

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

  Synopsis    [Adds clauses and returns CNF variable of the node.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Gia_Min2CollectSuper_rec( Gia_Obj_t * pObj, Vec_Ptr_t * vSuper, int fFirst, int fUseMuxes )
{
    // if the new node is complemented or a PI, another gate begins
    if ( Gia_IsComplement(pObj) || Gia_ObjIsCi(pObj) || 
         (!fFirst && Gia_ObjValue(pObj) > 1) || 
         (fUseMuxes && pObj->fMark0) )
    {
        Vec_PtrPushUnique( vSuper, pObj );
        return;
    }
    // go through the branches
    Gia_Min2CollectSuper_rec( Gia_ObjChild0(pObj), vSuper, 0, fUseMuxes );
    Gia_Min2CollectSuper_rec( Gia_ObjChild1(pObj), vSuper, 0, fUseMuxes );
}
void Gia_Min2CollectSuper( Gia_Obj_t * pObj, int fUseMuxes, Vec_Ptr_t * vSuper )
{
    assert( !Gia_IsComplement(pObj) );
    assert( !Gia_ObjIsCi(pObj) );
    Vec_PtrClear( vSuper );
    Gia_Min2CollectSuper_rec( pObj, vSuper, 1, fUseMuxes );
}
void Gia_Min2ObjAddToFrontier( Gia_Man_t * p, Gia_Obj_t * pObj, Vec_Ptr_t * vFrontier, satoko_t * pSat, Vec_Int_t * vSatVars )
{
    assert( !Gia_IsComplement(pObj) );
    assert( !Gia_ObjIsConst0(pObj) );
    if ( Gia_Min2ObjSatId(p, pObj) >= 0 )
        return;
    assert( Gia_Min2ObjSatId(p, pObj) == -1 );
    Vec_IntPush( vSatVars, Gia_ObjId(p, pObj) );
    Gia_Min2ObjSetSatId( p, pObj, satoko_add_variable(pSat, 0) );
    if ( Gia_ObjIsAnd(pObj) )
        Vec_PtrPush( vFrontier, pObj );
}
int Gia_Min2ObjGetCnfVar( Gia_Man2Min_t * p, int iObj )
{ 
    Gia_Obj_t * pObj = Gia_ManObj(p->pGia, iObj);
    Gia_Obj_t * pNode, * pFanin;
    int i, k, fUseMuxes = 1;
    if ( Gia_Min2ObjSatId(p->pGia, pObj) >= 0 )
        return Gia_Min2ObjSatId(p->pGia, pObj);
    if ( Gia_ObjIsCi(pObj) )
    {
        Vec_IntPush( p->vSatVars, iObj );
        return Gia_Min2ObjSetSatId( p->pGia, pObj, satoko_add_variable(p->pSat, 0) );
    }
    assert( Gia_ObjIsAnd(pObj) );
    // start the frontier
    Vec_PtrClear( p->vFrontier );
    Gia_Min2ObjAddToFrontier( p->pGia, pObj, p->vFrontier, p->pSat, p->vSatVars );
    // explore nodes in the frontier
    Vec_PtrForEachEntry( Gia_Obj_t *, p->vFrontier, pNode, i )
    {
        assert( Gia_Min2ObjSatId(p->pGia,pNode) >= 0 );
        if ( fUseMuxes && pNode->fMark0 )
        {
            Vec_PtrClear( p->vFanins );
            Vec_PtrPushUnique( p->vFanins, Gia_ObjFanin0( Gia_ObjFanin0(pNode) ) );
            Vec_PtrPushUnique( p->vFanins, Gia_ObjFanin0( Gia_ObjFanin1(pNode) ) );
            Vec_PtrPushUnique( p->vFanins, Gia_ObjFanin1( Gia_ObjFanin0(pNode) ) );
            Vec_PtrPushUnique( p->vFanins, Gia_ObjFanin1( Gia_ObjFanin1(pNode) ) );
            Vec_PtrForEachEntry( Gia_Obj_t *, p->vFanins, pFanin, k )
                Gia_Min2ObjAddToFrontier( p->pGia, Gia_Regular(pFanin), p->vFrontier, p->pSat, p->vSatVars );
            Gia_Min2AddClausesMux( p->pGia, pNode, p->pSat );
        }
        else
        {
            Gia_Min2CollectSuper( pNode, fUseMuxes, p->vFanins );
            Vec_PtrForEachEntry( Gia_Obj_t *, p->vFanins, pFanin, k )
                Gia_Min2ObjAddToFrontier( p->pGia, Gia_Regular(pFanin), p->vFrontier, p->pSat, p->vSatVars );
            Gia_Min2AddClausesSuper( p->pGia, pNode, p->vFanins, p->pSat );
        }
        assert( Vec_PtrSize(p->vFanins) > 1 );
    }
    return Gia_Min2ObjSatId(p->pGia,pObj);
}

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

  Synopsis    [Returns 0 if the node is not a constant.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_Min2ManSimulate( Gia_Man2Min_t * p )
{
    word Sim0, Sim1; int n, i, iObj;
    p->nSims++;
    // add random values to new CIs
    Vec_WrdFillExtra( p->vSims, Gia_ManObjNum(p->pGia), 0 );
    for ( i = p->nCisOld; i < Gia_ManCiNum(p->pGia); i++ )
        Vec_WrdWriteEntry( p->vSims, Gia_ManCiIdToId(p->pGia, i), Gia_ManRandomW( 0 ) << 1 );
    p->nCisOld = Gia_ManCiNum(p->pGia);
    // simulate internal nodes
    for ( n = 0; n < 2; n++ )
        Vec_IntForEachEntry( p->vObjs[n], iObj, i )
        {
            Gia_Obj_t * pObj = Gia_ManObj( p->pGia, iObj );
            Sim0 = Vec_WrdEntry( p->vSims, Gia_ObjFaninId0p(p->pGia, pObj) );
            Sim1 = Vec_WrdEntry( p->vSims, Gia_ObjFaninId1p(p->pGia, pObj) );
            Sim0 = Gia_ObjFaninC0(pObj) ? ~Sim0 : Sim0;
            Sim1 = Gia_ObjFaninC1(pObj) ? ~Sim1 : Sim1;
            Vec_WrdWriteEntry( p->vSims, iObj, Sim0 & Sim1 );
        }
    Sim0 = Vec_WrdEntry( p->vSims, Abc_Lit2Var(p->iLits[0]) );
    Sim1 = Vec_WrdEntry( p->vSims, Abc_Lit2Var(p->iLits[1]) );
    Sim0 = Abc_LitIsCompl(p->iLits[0]) ? ~Sim0 : Sim0;
    Sim1 = Abc_LitIsCompl(p->iLits[1]) ? ~Sim1 : Sim1;
//    assert( (Sim0 & Sim1) != ~0 );
    return (Sim0 & Sim1) == 0;
}

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

  Synopsis    [Internal simulation APIs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Gia_Min2SimSetInputBit( Gia_Man2Min_t * p, int iObj, int Bit, int iPattern )
{
    word * pSim = Vec_WrdEntryP( p->vSims, iObj );
    assert( iPattern > 0 && iPattern < 64 );
    if ( Abc_InfoHasBit( (unsigned*)pSim, iPattern ) != Bit )
        Abc_InfoXorBit( (unsigned*)pSim, iPattern );
}
int Gia_Min2ManSolve( Gia_Man2Min_t * p )
{
    int iObj0 = Abc_Lit2Var(p->iLits[0]);
    int iObj1 = Abc_Lit2Var(p->iLits[1]);
    int n, i, status, iVar0, iVar1, iTemp;
    assert( iObj0 > 0 && iObj1 > 0 );
//    Vec_IntForEachEntry( &p->pGia->vCopies, iTemp, i )
//        assert( iTemp == -1 );
    Vec_IntFillExtra( &p->pGia->vCopies, Gia_ManObjNum(p->pGia), -1 );
    Vec_IntClear( p->vSatVars );
    assert( satoko_varnum(p->pSat) == 0 );
    iVar0 = Gia_Min2ObjGetCnfVar( p, iObj0 );
    iVar1 = Gia_Min2ObjGetCnfVar( p, iObj1 );
    satoko_assump_push( p->pSat, Abc_Var2Lit(iVar0, Abc_LitIsCompl(p->iLits[0])) );
    satoko_assump_push( p->pSat, Abc_Var2Lit(iVar1, Abc_LitIsCompl(p->iLits[1])) );
    status = satoko_solve( p->pSat );
    satoko_assump_pop( p->pSat );
    satoko_assump_pop( p->pSat );
    if ( status == SATOKO_SAT )
    {
        //printf( "Disproved %d %d\n", p->iLits[0], p->iLits[1] );
        assert( Gia_Min2ManSimulate(p) == 1 );
        for ( n = 0; n < 2; n++ )
            Vec_IntForEachEntry( p->vCis[n], iTemp, i )
                Gia_Min2SimSetInputBit( p, iTemp, satoko_var_polarity(p->pSat, Gia_Min2ObjSatId(p->pGia, Gia_ManObj(p->pGia, iTemp))) == SATOKO_LIT_TRUE, p->iPattern );
        //assert( Gia_Min2ManSimulate(p) == 0 );
        p->iPattern = p->iPattern == 63 ? 1 : p->iPattern + 1;
        p->nSatSat++;
    }
    //printf( "supp %d  node %d  vars %d\n", 
    //    Vec_IntSize(p->vCis[0]) + Vec_IntSize(p->vCis[1]), 
    //    Vec_IntSize(p->vObjs[0]) + Vec_IntSize(p->vObjs[1]),
    //    Vec_IntSize(p->vSatVars) );
    satoko_rollback( p->pSat );
    Vec_IntForEachEntry( p->vSatVars, iTemp, i )
        Gia_Min2ObjCleanSatId( p->pGia, Gia_ManObj(p->pGia, iTemp) );
    return status == SATOKO_UNSAT;
}

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

  Synopsis    [Collect nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Gia_Min2ManGatherSupp_rec( Gia_Man_t * p, int iObj, Vec_Int_t * vCis, Vec_Int_t * vObjs )
{
    int Val0, Val1;
    Gia_Obj_t * pObj;
    if ( Gia_ObjIsTravIdPreviousId(p, iObj) )
        return 1;
    if ( Gia_ObjIsTravIdCurrentId(p, iObj) )
        return 0;
    Gia_ObjSetTravIdCurrentId(p, iObj);
    pObj = Gia_ManObj( p, iObj );
    if ( Gia_ObjIsCi(pObj) )
    {
        Vec_IntPush( vCis, iObj );
        return 0;
    }
    assert( Gia_ObjIsAnd(pObj) );
    Val0 = Gia_Min2ManGatherSupp_rec( p, Gia_ObjFaninId0(pObj, iObj), vCis, vObjs );
    Val1 = Gia_Min2ManGatherSupp_rec( p, Gia_ObjFaninId1(pObj, iObj), vCis, vObjs );
    Vec_IntPush( vObjs, iObj );
    return Val0 || Val1;
}
int Gia_Min2ManGatherSupp( Gia_Man2Min_t * p )
{
    int n, Overlap = 0;
    p->nSupps++;
    Gia_ManIncrementTravId( p->pGia );
    for ( n = 0; n < 2; n++ )
    {
        Vec_IntClear( p->vCis[n] );
        Vec_IntClear( p->vObjs[n] );
        Gia_ManIncrementTravId( p->pGia );
        Overlap = Gia_Min2ManGatherSupp_rec( p->pGia, Abc_Lit2Var(p->iLits[n]), p->vCis[n], p->vObjs[n] );
        assert( n || !Overlap );
    }
    return Overlap;
}

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

  Synopsis    [Takes a literal and returns a support-minized literal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Gia_Man2SupportAnd( Gia_Man2Min_t * p, int iLit0, int iLit1 )
{
    p->nCalls++;
    //return Gia_ManHashAnd( p->pGia, iLit0, iLit1 );
    p->iLits[0] = iLit0;
    p->iLits[1] = iLit1;
    if ( iLit0 < 2 || iLit1 < 2 || Abc_Lit2Var(iLit0) == Abc_Lit2Var(iLit1) || Gia_ManHashLookupInt(p->pGia, iLit0, iLit1) || 
         !Gia_Min2ManGatherSupp(p) || !Gia_Min2ManSimulate(p) || !Gia_Min2ManSolve(p) )
        return Gia_ManHashAnd( p->pGia, iLit0, iLit1 );
    //printf( "%d %d\n", iLit0, iLit1 );
    p->nSatUnsat++;
    return 0;
}

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

  Synopsis    [Testbench.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Gia_Man_t * Gia_Man2SupportAndTest( Gia_Man_t * pGia )
{
    Gia_Man2Min_t * pMan;
    Gia_Man_t * pNew, * pTemp;
    Gia_Obj_t * pObj;
    int i;
    Gia_ManRandomW( 1 );
    Gia_ManFillValue( pGia );
    pNew = Gia_ManStart( Gia_ManObjNum(pGia) );
    pNew->pName = Abc_UtilStrsav( pGia->pName );
    pNew->pSpec = Abc_UtilStrsav( pGia->pSpec );
    Gia_ManHashAlloc( pNew );
    Gia_ManConst0(pGia)->Value = 0;
    pMan = Gia_Man2SuppStart( pNew );
    Gia_ManForEachObj1( pGia, pObj, i )
    {
        if ( Gia_ObjIsAnd(pObj) )
        {
//            pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
            pObj->Value = Gia_Man2SupportAnd( pMan, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) );
        }
        else if ( Gia_ObjIsCi(pObj) )
            pObj->Value = Gia_ManAppendCi( pNew );
        else if ( Gia_ObjIsCo(pObj) )
            pObj->Value = Gia_ManAppendCo( pNew, Gia_ObjFanin0Copy(pObj) );
        else assert( 0 );

        //if ( i % 10000 == 0 )
        //    printf( "%d\n", i );
    }
    Gia_Man2SuppStop( pMan );
    Gia_ManSetRegNum( pNew, Gia_ManRegNum(pGia) );
    pNew = Gia_ManCleanup( pTemp = pNew );
    Gia_ManStop( pTemp );
    return pNew;
}




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


ABC_NAMESPACE_IMPL_END