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

  FileName    [xsatSolver.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [xSAT - A SAT solver written in C.
               Read the license file for more info.]

  Synopsis    [Solver internal functions implementation.]

  Author      [Bruno Schmitt <boschmitt@inf.ufrgs.br>]

  Affiliation [UC Berkeley / UFRGS]

  Date        [Ver. 1.0. Started - November 10, 2016.]

  Revision    []

***********************************************************************/
////////////////////////////////////////////////////////////////////////
///                          INCLUDES                                ///
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <math.h>

#include "xsatHeap.h"
#include "xsatSolver.h"
#include "xsatUtils.h"

ABC_NAMESPACE_IMPL_START

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DECLARATIONS                        ///
////////////////////////////////////////////////////////////////////////
/**Function*************************************************************

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int xSAT_SolverDecide( xSAT_Solver_t * s )
{
    int NextVar = VarUndef;

    while ( NextVar == VarUndef || Vec_StrEntry( s->vAssigns, NextVar ) != VarX )
    {
        if ( xSAT_HeapSize( s->hOrder ) == 0 )
        {
            NextVar = VarUndef;
            break;
        }
        else
            NextVar = xSAT_HeapRemoveMin( s->hOrder );
    }
    return NextVar;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void xSAT_SolverRebuildOrderHeap( xSAT_Solver_t * s )
{
    Vec_Int_t * vTemp = Vec_IntAlloc( Vec_StrSize( s->vAssigns ) );
    int Var;

    for ( Var = 0; Var < Vec_StrSize( s->vAssigns ); Var++ )
        if ( Vec_StrEntry( s->vAssigns, Var ) == VarX )
            Vec_IntPush( vTemp, Var );

    xSAT_HeapBuild( s->hOrder, vTemp );
    Vec_IntFree( vTemp );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverVarActRescale( xSAT_Solver_t * s )
{
    int i;
    unsigned * pActivity = ( unsigned * ) Vec_IntArray( s->vActivity );

    for ( i = 0; i < Vec_IntSize( s->vActivity ); i++ )
        pActivity[i] >>= 19;

    s->nVarActInc >>= 19;
    s->nVarActInc = Abc_MaxInt( s->nVarActInc, ( 1 << 5 ) );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverVarActBump( xSAT_Solver_t * s, int Var )
{
    unsigned * pActivity = ( unsigned * ) Vec_IntArray( s->vActivity );

    pActivity[Var] += s->nVarActInc;
    if ( pActivity[Var] & 0x80000000 )
        xSAT_SolverVarActRescale( s );

    if ( xSAT_HeapInHeap( s->hOrder, Var ) )
        xSAT_HeapDecrease( s->hOrder, Var );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverVarActDecay( xSAT_Solver_t * s )
{
    s->nVarActInc += ( s->nVarActInc >> 4 );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverClaActRescale( xSAT_Solver_t * s )
{
    xSAT_Clause_t * pC;
    int i, CRef;

    Vec_IntForEachEntry( s->vLearnts, CRef, i )
    {
        pC = xSAT_SolverReadClause( s, ( unsigned ) CRef );
        pC->pData[pC->nSize].Act >>= 14;
    }
    s->nClaActInc >>= 14;
    s->nClaActInc = Abc_MaxInt( s->nClaActInc, ( 1 << 10 ) );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverClaActBump( xSAT_Solver_t* s, xSAT_Clause_t * pCla )
{
    pCla->pData[pCla->nSize].Act += s->nClaActInc;
    if ( pCla->pData[pCla->nSize].Act & 0x80000000 )
        xSAT_SolverClaActRescale( s );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverClaActDecay( xSAT_Solver_t * s )
{
    s->nClaActInc += ( s->nClaActInc >> 10 );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int xSAT_SolverClaCalcLBD( xSAT_Solver_t * s, xSAT_Clause_t * pCla )
{
    int i;
    int nLBD = 0;

    s->nStamp++;
    for ( i = 0; i < pCla->nSize; i++ )
    {
        int Level = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( pCla->pData[i].Lit ) );
        if ( ( unsigned ) Vec_IntEntry( s->vStamp, Level ) != s->nStamp )
        {
            Vec_IntWriteEntry( s->vStamp, Level, ( int ) s->nStamp );
            nLBD++;
        }
    }
    return nLBD;
}

static inline int xSAT_SolverClaCalcLBD2( xSAT_Solver_t * s, Vec_Int_t * vLits )
{
    int i;
    int nLBD = 0;

    s->nStamp++;
    for ( i = 0; i < Vec_IntSize( vLits ); i++ )
    {
        int Level = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( Vec_IntEntry( vLits, i ) ) );
        if ( ( unsigned ) Vec_IntEntry( s->vStamp, Level ) != s->nStamp )
        {
            Vec_IntWriteEntry( s->vStamp, Level, ( int ) s->nStamp );
            nLBD++;
        }
    }
    return nLBD;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
unsigned xSAT_SolverClaNew( xSAT_Solver_t * s, Vec_Int_t * vLits , int fLearnt )
{
    unsigned CRef;
    xSAT_Clause_t * pCla;
    xSAT_Watcher_t w1;
    xSAT_Watcher_t w2;
    unsigned nWords;

    assert( Vec_IntSize( vLits ) > 1);
    assert( fLearnt == 0 || fLearnt == 1 );

    nWords = 3 + fLearnt + Vec_IntSize( vLits );
    CRef = xSAT_MemAppend( s->pMemory, nWords );
    pCla = xSAT_SolverReadClause( s, CRef );
    pCla->fLearnt = fLearnt;
    pCla->fMark = 0;
    pCla->fReallocd = 0;
    pCla->fCanBeDel = fLearnt;
    pCla->nSize = Vec_IntSize( vLits );
    memcpy( &( pCla->pData[0].Lit ), Vec_IntArray( vLits ), sizeof( int ) * Vec_IntSize( vLits ) );

    if ( fLearnt )
    {
        Vec_IntPush( s->vLearnts, CRef );
        pCla->nLBD = xSAT_SolverClaCalcLBD2( s, vLits );
        pCla->pData[pCla->nSize].Act = 0;
        s->Stats.nLearntLits += Vec_IntSize( vLits );
        xSAT_SolverClaActBump(s, pCla);
    }
    else
    {
        Vec_IntPush( s->vClauses, CRef );
        s->Stats.nClauseLits += Vec_IntSize( vLits );
    }

    w1.CRef = CRef;
    w2.CRef = CRef;
    w1.Blocker = pCla->pData[1].Lit;
    w2.Blocker = pCla->pData[0].Lit;

    if ( Vec_IntSize( vLits ) == 2 )
    {
        xSAT_WatchListPush( xSAT_VecWatchListEntry( s->vBinWatches, xSAT_NegLit( pCla->pData[0].Lit ) ), w1 );
        xSAT_WatchListPush( xSAT_VecWatchListEntry( s->vBinWatches, xSAT_NegLit( pCla->pData[1].Lit ) ), w2 );
    }
    else
    {
        xSAT_WatchListPush( xSAT_VecWatchListEntry( s->vWatches, xSAT_NegLit( pCla->pData[0].Lit ) ), w1 );
        xSAT_WatchListPush( xSAT_VecWatchListEntry( s->vWatches, xSAT_NegLit( pCla->pData[1].Lit ) ), w2 );
    }
    return CRef;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
int xSAT_SolverEnqueue( xSAT_Solver_t * s, int Lit, unsigned Reason )
{
    int Var = xSAT_Lit2Var( Lit );

    Vec_StrWriteEntry( s->vAssigns, Var, (char)xSAT_LitSign( Lit ) );
    Vec_IntWriteEntry( s->vLevels, Var, xSAT_SolverDecisionLevel( s ) );
    Vec_IntWriteEntry( s->vReasons, Var, ( int ) Reason );
    Vec_IntPush( s->vTrail, Lit );

    return true;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void xSAT_SolverNewDecision( xSAT_Solver_t * s, int Lit )
{
    assert( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lit ) ) == VarX );
    s->Stats.nDecisions++;
    Vec_IntPush( s->vTrailLim, Vec_IntSize( s->vTrail ) );
    xSAT_SolverEnqueue( s, Lit, CRefUndef );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void xSAT_SolverCancelUntil( xSAT_Solver_t * s, int Level )
{
    int c;

    if ( xSAT_SolverDecisionLevel( s ) <= Level )
        return;

    for ( c = Vec_IntSize( s->vTrail ) - 1; c >= Vec_IntEntry( s->vTrailLim, Level ); c-- )
    {
        int Var = xSAT_Lit2Var( Vec_IntEntry( s->vTrail, c ) );

        Vec_StrWriteEntry( s->vAssigns, Var, VarX );
        Vec_IntWriteEntry( s->vReasons, Var, ( int ) CRefUndef );
        Vec_StrWriteEntry( s->vPolarity, Var, ( char )xSAT_LitSign( Vec_IntEntry( s->vTrail, c ) ) );

        if ( !xSAT_HeapInHeap( s->hOrder, Var ) )
            xSAT_HeapInsert( s->hOrder, Var );
    }

    s->iQhead = Vec_IntEntry( s->vTrailLim, Level );
    Vec_IntShrink( s->vTrail, Vec_IntEntry( s->vTrailLim, Level ) );
    Vec_IntShrink( s->vTrailLim, Level );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static int xSAT_SolverIsLitRemovable( xSAT_Solver_t* s, int Lit, int MinLevel )
{
    int top = Vec_IntSize( s->vTagged );

    assert( ( unsigned ) Vec_IntEntry( s->vReasons, xSAT_Lit2Var( Lit ) ) != CRefUndef );
    Vec_IntClear( s->vStack );
    Vec_IntPush( s->vStack, xSAT_Lit2Var( Lit ) );

    while ( Vec_IntSize( s->vStack ) )
    {
        int i;
        int v = Vec_IntPop(  s->vStack );
        xSAT_Clause_t* c = xSAT_SolverReadClause(s, ( unsigned ) Vec_IntEntry( s->vReasons, v ) );
        int * Lits = &( c->pData[0].Lit );

        assert( (unsigned) Vec_IntEntry( s->vReasons, v ) != CRefUndef);
        if( c->nSize == 2 && Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[0] ) ) == xSAT_LitSign( xSAT_NegLit( Lits[0] ) ) )
        {
            assert( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[1] ) ) == xSAT_LitSign( ( Lits[1] ) ) );
            ABC_SWAP( int, Lits[0], Lits[1] );
        }

        for ( i = 1; i < c->nSize; i++ )
        {
            int v = xSAT_Lit2Var( Lits[i] );
            if ( !Vec_StrEntry( s->vSeen, v ) && Vec_IntEntry( s->vLevels, v ) )
            {
                if ( ( unsigned ) Vec_IntEntry( s->vReasons, v ) != CRefUndef && ( ( 1 << (Vec_IntEntry( s->vLevels, v ) & 31 ) ) & MinLevel ) )
                {
                    Vec_IntPush( s->vStack, v );
                    Vec_IntPush( s->vTagged, Lits[i] );
                    Vec_StrWriteEntry( s->vSeen, v, 1 );
                }
                else
                {
                    int Lit;
                    Vec_IntForEachEntryStart( s->vTagged, Lit, i, top )
                        Vec_StrWriteEntry( s->vSeen, xSAT_Lit2Var( Lit ), 0 );
                    Vec_IntShrink( s->vTagged, top );
                    return 0;
                }
            }
        }
    }
    return 1;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static void xSAT_SolverClaMinimisation( xSAT_Solver_t * s, Vec_Int_t * vLits )
{
    int * pLits = Vec_IntArray( vLits );
    int MinLevel = 0;
    int i, j;

    for ( i = 1; i < Vec_IntSize( vLits ); i++ )
    {
        int Level = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( pLits[i] ) );
        MinLevel |= 1 << ( Level & 31 );
    }

    /* Remove reduntant literals */
    Vec_IntAppend( s->vTagged, vLits );
    for ( i = j = 1; i < Vec_IntSize( vLits ); i++ )
        if ( ( unsigned ) Vec_IntEntry( s->vReasons, xSAT_Lit2Var( pLits[i] ) ) == CRefUndef || !xSAT_SolverIsLitRemovable( s, pLits[i], MinLevel ) )
            pLits[j++] = pLits[i];
    Vec_IntShrink( vLits, j );

    /* Binary Resolution */
    if( Vec_IntSize( vLits ) <= 30 && xSAT_SolverClaCalcLBD2( s, vLits ) <= 6 )
    {
        int nb, l;
        int Lit;
        int FlaseLit = xSAT_NegLit( pLits[0] );
        xSAT_WatchList_t * ws = xSAT_VecWatchListEntry( s->vBinWatches, FlaseLit );
        xSAT_Watcher_t * begin = xSAT_WatchListArray( ws );
        xSAT_Watcher_t * end   = begin + xSAT_WatchListSize( ws );
        xSAT_Watcher_t * pWatcher;

        s->nStamp++;
        Vec_IntForEachEntry( vLits, Lit, i )
            Vec_IntWriteEntry( s->vStamp, xSAT_Lit2Var( Lit ), ( int ) s->nStamp );

        nb = 0;
        for ( pWatcher = begin; pWatcher < end; pWatcher++ )
        {
            int ImpLit = pWatcher->Blocker;

            if ( ( unsigned ) Vec_IntEntry( s->vStamp, xSAT_Lit2Var( ImpLit ) ) == s->nStamp && Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( ImpLit ) ) == xSAT_LitSign( ImpLit ) )
            {
                nb++;
                Vec_IntWriteEntry( s->vStamp, xSAT_Lit2Var( ImpLit ), ( int )( s->nStamp - 1 ) );
            }
        }

        l = Vec_IntSize( vLits ) - 1;
        if ( nb > 0 )
        {
            for ( i = 1; i < Vec_IntSize( vLits ) - nb; i++ )
                if ( ( unsigned ) Vec_IntEntry( s->vStamp, xSAT_Lit2Var( pLits[i] ) ) != s->nStamp )
                {
                    int TempLit = pLits[l];
                    pLits[l] = pLits[i];
                    pLits[i] = TempLit;
                    i--; l--;
                }

            Vec_IntShrink( vLits, Vec_IntSize( vLits ) - nb );
        }
    }
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
static void xSAT_SolverAnalyze( xSAT_Solver_t* s, unsigned ConfCRef, Vec_Int_t * vLearnt, int * OutBtLevel, unsigned * nLBD )
{
    int * trail = Vec_IntArray( s->vTrail );
    int Count   = 0;
    int p       = LitUndef;
    int Idx     = Vec_IntSize( s->vTrail ) - 1;
    int * Lits;
    int Lit;
    int i, j;

    Vec_IntPush( vLearnt, LitUndef );
    do
    {
        xSAT_Clause_t * pCla;

        assert( ConfCRef != CRefUndef );
        pCla = xSAT_SolverReadClause(s, ConfCRef);
        Lits = &( pCla->pData[0].Lit );

        if( p != LitUndef && pCla->nSize == 2 && Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[0] ) ) == xSAT_LitSign( xSAT_NegLit( Lits[0] ) ) )
        {
            assert( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[1] ) ) == xSAT_LitSign( ( Lits[1] ) ) );
            ABC_SWAP( int, Lits[0], Lits[1] );
        }

        if ( pCla->fLearnt )
            xSAT_SolverClaActBump( s, pCla );

        if ( pCla->fLearnt && pCla->nLBD > 2 )
        {
            unsigned int nLevels = xSAT_SolverClaCalcLBD( s, pCla );
            if ( nLevels + 1 < pCla->nLBD )
            {
                if ( pCla->nLBD <= s->Config.nLBDFrozenClause )
                    pCla->fCanBeDel = 0;
                pCla->nLBD = nLevels;
            }
        }

        for ( j = ( p == LitUndef ? 0 : 1 ); j < pCla->nSize; j++ )
        {
            int Var = xSAT_Lit2Var( Lits[j] );

            if ( Vec_StrEntry( s->vSeen, Var ) == 0 && Vec_IntEntry( s->vLevels, Var ) > 0 )
            {
                Vec_StrWriteEntry( s->vSeen, Var, 1 );
                xSAT_SolverVarActBump( s, Var );
                if ( Vec_IntEntry( s->vLevels, Var ) >= xSAT_SolverDecisionLevel( s ) )
                {
                    Count++;
                    if (  Vec_IntEntry( s->vReasons, Var ) != CRefUndef && xSAT_SolverReadClause( s, Vec_IntEntry( s->vReasons, Var ) )->fLearnt )
                        Vec_IntPush( s->vLastDLevel, Var );
                }
                else
                    Vec_IntPush( vLearnt, Lits[j] );
            }
        }

        while ( !Vec_StrEntry( s->vSeen, xSAT_Lit2Var( trail[Idx--] ) ) );

        // Next clause to look at
        p = trail[Idx+1];
        ConfCRef = ( unsigned ) Vec_IntEntry( s->vReasons, xSAT_Lit2Var( p ) );
        Vec_StrWriteEntry( s->vSeen, xSAT_Lit2Var( p ), 0 );
        Count--;

    } while ( Count > 0 );

    Vec_IntArray( vLearnt )[0] = xSAT_NegLit( p );
    xSAT_SolverClaMinimisation( s, vLearnt );

    // Find the backtrack level
    Lits = Vec_IntArray( vLearnt );
    if ( Vec_IntSize( vLearnt ) == 1 )
        *OutBtLevel = 0;
    else
    {
        int iMax = 1;
        int Max   = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( Lits[1] ) );
        int Tmp;

        for (i = 2; i < Vec_IntSize( vLearnt ); i++)
            if ( Vec_IntEntry( s->vLevels, xSAT_Lit2Var( Lits[i]) ) > Max)
            {
                Max   = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( Lits[i]) );
                iMax = i;
            }

        Tmp         = Lits[1];
        Lits[1]     = Lits[iMax];
        Lits[iMax]  = Tmp;
        *OutBtLevel = Vec_IntEntry( s->vLevels, xSAT_Lit2Var( Lits[1] ) );
    }

    *nLBD = xSAT_SolverClaCalcLBD2( s, vLearnt );
    if ( Vec_IntSize( s->vLastDLevel ) > 0 )
    {
        int Var;
        Vec_IntForEachEntry( s->vLastDLevel, Var, i )
        {
            if ( xSAT_SolverReadClause( s, Vec_IntEntry( s->vReasons, Var ) )->nLBD < *nLBD )
                xSAT_SolverVarActBump( s, Var );
        }

        Vec_IntClear( s->vLastDLevel );
    }

    Vec_IntForEachEntry( s->vTagged, Lit, i )
        Vec_StrWriteEntry( s->vSeen, xSAT_Lit2Var( Lit ), 0 );
    Vec_IntClear( s->vTagged );
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
unsigned xSAT_SolverPropagate( xSAT_Solver_t* s )
{
    unsigned hConfl = CRefUndef;
    int * Lits;
    int NegLit;
    int nProp = 0;

    while ( s->iQhead < Vec_IntSize( s->vTrail ) )
    {
        int p = Vec_IntEntry( s->vTrail, s->iQhead++ );
        xSAT_WatchList_t* ws = xSAT_VecWatchListEntry( s->vBinWatches, p );
        xSAT_Watcher_t* begin = xSAT_WatchListArray( ws );
        xSAT_Watcher_t* end   = begin + xSAT_WatchListSize( ws );
        xSAT_Watcher_t *i, *j;

        nProp++;
        for ( i = begin; i < end; i++ )
        {
            if ( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( i->Blocker ) ) == VarX )
                xSAT_SolverEnqueue( s, i->Blocker, i->CRef );
            else if ( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( i->Blocker ) ) == xSAT_LitSign( xSAT_NegLit( i->Blocker ) ) )
                return i->CRef;
        }

        ws    = xSAT_VecWatchListEntry( s->vWatches, p );
        begin = xSAT_WatchListArray( ws );
        end   = begin + xSAT_WatchListSize( ws );

        for ( i = j = begin; i < end; )
        {
            xSAT_Clause_t * pCla;
            xSAT_Watcher_t w;
            if ( Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( i->Blocker ) ) == xSAT_LitSign( i->Blocker ) )
            {
                *j++ = *i++;
                continue;
            }

            pCla = xSAT_SolverReadClause( s, i->CRef );
            Lits = &( pCla->pData[0].Lit );

            // Make sure the false literal is data[1]:
            NegLit = xSAT_NegLit( p );
            if ( Lits[0] == NegLit )
            {
                Lits[0] = Lits[1];
                Lits[1] = NegLit;
            }
            assert( Lits[1] == NegLit );

            w.CRef = i->CRef;
            w.Blocker = Lits[0];

            // If 0th watch is true, then clause is already satisfied.
            if ( Lits[0] != i->Blocker && Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[0] ) ) == xSAT_LitSign( Lits[0] ) )
                *j++ = w;
            else
            {
                // Look for new watch:
                int * stop = Lits + pCla->nSize;
                int * k;
                for ( k = Lits + 2; k < stop; k++ )
                {
                    if (Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( *k ) ) != !xSAT_LitSign( *k ) )
                    {
                        Lits[1] = *k;
                        *k = NegLit;
                        xSAT_WatchListPush( xSAT_VecWatchListEntry( s->vWatches, xSAT_NegLit( Lits[1] ) ), w );
                        goto next;
                    }
                }

                *j++ = w;

                // Clause is unit under assignment:
                if (Vec_StrEntry( s->vAssigns, xSAT_Lit2Var( Lits[0] ) ) == xSAT_LitSign( xSAT_NegLit( Lits[0] ) ) )
                {
                    hConfl = i->CRef;
                    i++;
                    s->iQhead = Vec_IntSize( s->vTrail );
                    // Copy the remaining watches:
                    while (i < end)
                        *j++ = *i++;
                }
                else
                    xSAT_SolverEnqueue( s, Lits[0], i->CRef );
            }
        next:
            i++;
        }

        s->Stats.nInspects += j - xSAT_WatchListArray( ws );
        xSAT_WatchListShrink( ws, j - xSAT_WatchListArray( ws ) );
    }

    s->Stats.nPropagations += nProp;
    s->nPropSimplify -= nProp;

    return hConfl;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void xSAT_SolverReduceDB( xSAT_Solver_t * s )
{
    static abctime TimeTotal = 0;
    abctime clk = Abc_Clock();
    int nLearnedOld = Vec_IntSize( s->vLearnts );
    int i, limit;
    unsigned CRef;
    xSAT_Clause_t * pCla;
    xSAT_Clause_t ** learnts_cls;

    learnts_cls = ABC_ALLOC( xSAT_Clause_t *, nLearnedOld );
    Vec_IntForEachEntry( s->vLearnts, CRef, i )
        learnts_cls[i] = xSAT_SolverReadClause(s, CRef);

    limit = nLearnedOld / 2;

    xSAT_UtilSort((void *) learnts_cls, nLearnedOld,
                  (int (*)( const void *, const void * )) xSAT_ClauseCompare);

    if ( learnts_cls[nLearnedOld / 2]->nLBD <= 3 )
        s->nRC2 += s->Config.nSpecialIncReduce;
    if ( learnts_cls[nLearnedOld - 1]->nLBD <= 5 )
        s->nRC2 += s->Config.nSpecialIncReduce;

    Vec_IntClear( s->vLearnts );
    for ( i = 0; i < nLearnedOld; i++ )
    {
        unsigned CRef;

        pCla = learnts_cls[i];
        CRef = xSAT_MemCRef( s->pMemory, ( unsigned * ) pCla );
        assert( pCla->fMark == 0 );
        if ( pCla->fCanBeDel && pCla->nLBD > 2 && pCla->nSize > 2 && (unsigned) Vec_IntEntry( s->vReasons, xSAT_Lit2Var( pCla->pData[0].Lit ) ) != CRef && ( i < limit ) )
        {
            pCla->fMark = 1;
            s->Stats.nLearntLits -= pCla->nSize;
            xSAT_WatchListRemove( xSAT_VecWatchListEntry( s->vWatches, xSAT_NegLit( pCla->pData[0].Lit ) ), CRef );
            xSAT_WatchListRemove( xSAT_VecWatchListEntry( s->vWatches, xSAT_NegLit( pCla->pData[1].Lit ) ), CRef );
        }
        else
        {
            if ( !pCla->fCanBeDel )
                limit++;
            pCla->fCanBeDel = 1;
            Vec_IntPush( s->vLearnts, CRef );
        }
    }
    ABC_FREE( learnts_cls );

    TimeTotal += Abc_Clock() - clk;
    if ( s->Config.fVerbose )
    {
        Abc_Print(1, "reduceDB: Keeping %7d out of %7d clauses (%5.2f %%)  ",
            Vec_IntSize( s->vLearnts ), nLearnedOld, 100.0 * Vec_IntSize( s->vLearnts ) / nLearnedOld );
        Abc_PrintTime( 1, "Time", TimeTotal );
    }
    xSAT_SolverGarbageCollect(s);
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
char xSAT_SolverSearch( xSAT_Solver_t * s )
{
    iword conflictC  = 0;

    s->Stats.nStarts++;
    for (;;)
    {
        unsigned hConfl = xSAT_SolverPropagate( s );

        if ( hConfl != CRefUndef )
        {
            /* Conflict */
            int BacktrackLevel;
            unsigned nLBD;
            unsigned CRef;

            s->Stats.nConflicts++;
            conflictC++;

            if ( xSAT_SolverDecisionLevel( s ) == 0 )
                return LBoolFalse;

            xSAT_BQueuePush( s->bqTrail, Vec_IntSize( s->vTrail ) );
            if ( s->Stats.nConflicts > s->Config.nFirstBlockRestart && xSAT_BQueueIsValid( s->bqLBD ) && ( Vec_IntSize( s->vTrail ) > ( s->Config.R * ( iword ) xSAT_BQueueAvg( s->bqTrail ) ) ) )
                xSAT_BQueueClean(s->bqLBD);

            Vec_IntClear( s->vLearntClause );
            xSAT_SolverAnalyze( s, hConfl, s->vLearntClause, &BacktrackLevel, &nLBD );

            s->nSumLBD += nLBD;
            xSAT_BQueuePush( s->bqLBD, nLBD );
            xSAT_SolverCancelUntil( s, BacktrackLevel );

            CRef = Vec_IntSize( s->vLearntClause ) == 1 ? CRefUndef : xSAT_SolverClaNew( s, s->vLearntClause , 1 );
            xSAT_SolverEnqueue( s, Vec_IntEntry( s->vLearntClause , 0 ), CRef );

            xSAT_SolverVarActDecay( s );
            xSAT_SolverClaActDecay( s );
        }
        else
        {
            /* No conflict */
            int NextVar;
            if ( xSAT_BQueueIsValid( s->bqLBD ) && ( ( ( iword )xSAT_BQueueAvg( s->bqLBD ) * s->Config.K ) > ( s->nSumLBD / s->Stats.nConflicts ) ) )
            {
                xSAT_BQueueClean( s->bqLBD );
                xSAT_SolverCancelUntil( s, 0 );
                return LBoolUndef;
            }

            // Simplify the set of problem clauses:
            if ( xSAT_SolverDecisionLevel( s ) == 0 )
                xSAT_SolverSimplify( s );

            // Reduce the set of learnt clauses:
            if ( s->Stats.nConflicts >= s->nConfBeforeReduce )
            {
                s->nRC1 = ( s->Stats.nConflicts / s->nRC2 ) + 1;
                xSAT_SolverReduceDB(s);
                s->nRC2 += s->Config.nIncReduce;
                s->nConfBeforeReduce = s->nRC1 * s->nRC2;
            }

            // New variable decision:
            NextVar = xSAT_SolverDecide( s );

            if ( NextVar == VarUndef )
                return LBoolTrue;

            xSAT_SolverNewDecision( s, xSAT_Var2Lit( NextVar, ( int ) Vec_StrEntry( s->vPolarity, NextVar ) ) );
        }
    }

    return LBoolUndef; // cannot happen
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void xSAT_SolverClaRealloc( xSAT_Mem_t * pDest, xSAT_Mem_t * pSrc, unsigned * pCRef )
{
    unsigned nNewCRef;
    xSAT_Clause_t * pNewCla;
    xSAT_Clause_t * pOldCla = xSAT_MemClauseHand( pSrc, *pCRef );

    if ( pOldCla->fReallocd )
    {
        *pCRef = ( unsigned ) pOldCla->nSize;
        return;
    }
    nNewCRef = xSAT_MemAppend( pDest, 3 + pOldCla->fLearnt + pOldCla->nSize );
    pNewCla = xSAT_MemClauseHand( pDest, nNewCRef );
    memcpy( pNewCla, pOldCla, ( 3 + pOldCla->fLearnt + pOldCla->nSize ) * 4 );
    pOldCla->fReallocd = 1;
    pOldCla->nSize = ( unsigned ) nNewCRef;
    *pCRef = nNewCRef;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
void xSAT_SolverGarbageCollect( xSAT_Solver_t * s )
{
    int i;
    unsigned * pArray;
    xSAT_Mem_t * pNewMemMngr = xSAT_MemAlloc(  xSAT_MemCap( s->pMemory ) - xSAT_MemWastedCap( s->pMemory ) );

    for ( i = 0; i < 2 * Vec_StrSize( s->vAssigns ); i++ )
    {
        xSAT_WatchList_t* ws = xSAT_VecWatchListEntry( s->vWatches, i);
        xSAT_Watcher_t* begin = xSAT_WatchListArray(ws);
        xSAT_Watcher_t* end   = begin + xSAT_WatchListSize(ws);
        xSAT_Watcher_t *w;

        for ( w = begin; w != end; w++ )
            xSAT_SolverClaRealloc( pNewMemMngr, s->pMemory, &(w->CRef) );

        ws = xSAT_VecWatchListEntry( s->vBinWatches, i);
        begin = xSAT_WatchListArray(ws);
        end   = begin + xSAT_WatchListSize(ws);
        for ( w = begin; w != end; w++ )
            xSAT_SolverClaRealloc( pNewMemMngr, s->pMemory, &(w->CRef) );
    }

    for ( i = 0; i < Vec_IntSize( s->vTrail ); i++ )
        if ( ( unsigned ) Vec_IntEntry( s->vReasons, xSAT_Lit2Var( Vec_IntEntry( s->vTrail, i ) ) ) != CRefUndef )
            xSAT_SolverClaRealloc( pNewMemMngr, s->pMemory, ( unsigned * ) &( Vec_IntArray( s->vReasons )[xSAT_Lit2Var( Vec_IntEntry( s->vTrail, i ) )] ) );

    pArray = ( unsigned * ) Vec_IntArray( s->vLearnts );
    for ( i = 0; i < Vec_IntSize( s->vLearnts ); i++ )
        xSAT_SolverClaRealloc( pNewMemMngr, s->pMemory, &(pArray[i]) );

    pArray = ( unsigned * ) Vec_IntArray( s->vClauses );
    for ( i = 0; i < Vec_IntSize( s->vClauses ); i++ )
        xSAT_SolverClaRealloc( pNewMemMngr, s->pMemory, &(pArray[i]) );

    xSAT_MemFree( s->pMemory );
    s->pMemory = pNewMemMngr;
}

ABC_NAMESPACE_IMPL_END
