{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
{-# LANGUAGE BangPatterns, DoRec, ScopedTypeVariables, CPP, DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  SAT
-- Copyright   :  (c) Masahiro Sakai 2012
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  provisional
-- Portability :  non-portable (BangPatterns, DoRec, ScopedTypeVariables, CPP, DeriveDataTypeable)
--
-- A CDCL SAT solver.
--
-- It follows the design of MiniSat and SAT4J.
--
-- See also:
--
-- * <http://hackage.haskell.org/package/funsat>
--
-- * <http://hackage.haskell.org/package/incremental-sat-solver>
--
-----------------------------------------------------------------------------
module SAT
  (
  -- * The @Solver@ type
    Solver
  , newSolver

  -- * Basic data structures
  , Var
  , Lit
  , literal
  , litNot
  , litVar
  , litPolarity
  , Clause

  -- * Problem specification
  , newVar
  , newVars
  , newVars_
  , addClause
  , addAtLeast
  , addAtMost
  , addExactly
  , addPBAtLeast
  , addPBAtMost
  , addPBExactly
  , addPBAtLeastSoft
  , addPBAtMostSoft
  , addPBExactlySoft

  -- * Solving
  , solve
  , solveWith
  , BudgetExceeded (..)

  -- * Extract results
  , Model
  , model
  , failedAssumptions

  -- * Solver configulation
  , RestartStrategy (..)
  , setRestartStrategy
  , defaultRestartStrategy
  , setRestartFirst
  , defaultRestartFirst
  , setRestartInc
  , defaultRestartInc
  , setLearntSizeFirst
  , defaultLearntSizeFirst
  , setLearntSizeInc
  , defaultLearntSizeInc
  , setCCMin
  , defaultCCMin
  , LearningStrategy (..)
  , setLearningStrategy
  , defaultLearningStrategy
  , setVarPolarity
  , setLogger
  , setCheckModel
  , setRandomFreq
  , defaultRandomFreq
  , setRandomSeed
  , setConfBudget

  -- * Read state
  , nVars
  , nAssigns
  , nClauses
  , nLearnt

  -- * Internal API
  , varBumpActivity
  , varDecayActivity
  ) where

import Prelude hiding (log)
import Control.Monad
import Control.Exception
#if MIN_VERSION_array(0,4,0)
import Data.Array.IO hiding (unsafeFreeze)
import Data.Array.Unsafe (unsafeFreeze)
#else
import Data.Array.IO
#endif
import Data.Array.Base (unsafeRead, unsafeWrite)
import Data.IORef
import Data.List
import Data.Maybe
import Data.Ord
import qualified Data.IntMap as IM
import qualified Data.IntSet as IS
import qualified Data.Set as Set
import qualified Data.IndexedPriorityQueue as PQ
import qualified Data.SeqQueue as SQ
import Data.Time
import Data.Typeable
import System.CPUTime
import qualified System.Random as Rand
import Text.Printf

import Data.LBool
import SAT.Types

{--------------------------------------------------------------------
  internal data structures
--------------------------------------------------------------------}

type Level = Int

levelRoot :: Level
levelRoot = -1

data Assignment
  = Assignment
  { aValue  :: !Bool
  , aLevel  :: {-# UNPACK #-} !Level
  , aReason :: !(Maybe SomeConstraint)
  , aBacktrackCBs :: !(IORef [IO ()])
  }

data VarData
  = VarData
  { vdAssignment :: !(IORef (Maybe Assignment))
  , vdPolarity   :: !(IORef Bool)
  , vdPosLitData :: !LitData
  , vdNegLitData :: !LitData
  , vdActivity   :: !(IORef VarActivity)
  }

newtype LitData
  = LitData
  { -- | will be invoked when this literal is falsified
    ldWatches :: IORef [SomeConstraint]
  }

newVarData :: IO VarData
newVarData = do
  ainfo <- newIORef Nothing
  polarity <- newIORef True
  pos <- newLitData
  neg <- newLitData
  activity <- newIORef 0
  return $ VarData ainfo polarity pos neg activity

newLitData :: IO LitData
newLitData = do
  ws <- newIORef []
  return $ LitData ws

varData :: Solver -> Var -> IO VarData
varData s !v = do
  a <- readIORef (svVarData s)
  unsafeRead a (v-1)

litData :: Solver -> Lit -> IO LitData
litData s !l =
  -- litVar による heap allocation を避けるために、
  -- litPolarityによる分岐後にvarDataを呼ぶ。
  if litPolarity l
    then do
      vd <- varData s l
      return $ vdPosLitData vd
    else do
      vd <- varData s (negate l)
      return $ vdNegLitData vd

{-# INLINE varValue #-}
varValue :: Solver -> Var -> IO LBool
varValue s !v = do
  vd <- varData s v
  m <- readIORef (vdAssignment vd)
  case m of
    Nothing -> return lUndef
    Just x -> return $! (liftBool $! aValue x)

{-# INLINE litValue #-}
litValue :: Solver -> Lit -> IO LBool
litValue s !l = do
  -- litVar による heap allocation を避けるために、
  -- litPolarityによる分岐後にvarDataを呼ぶ。
  if litPolarity l
    then varValue s l
    else do
      m <- varValue s (negate l)
      return $! lnot m

varLevel :: Solver -> Var -> IO Level
varLevel s !v = do
  vd <- varData s v
  m <- readIORef (vdAssignment vd)
  case m of
    Nothing -> error ("varLevel: unassigned var " ++ show v)
    Just a -> return (aLevel a)

litLevel :: Solver -> Lit -> IO Level
litLevel s l = varLevel s (litVar l)

varReason :: Solver -> Var -> IO (Maybe SomeConstraint)
varReason s !v = do
  vd <- varData s v
  m <- readIORef (vdAssignment vd)
  case m of
    Nothing -> error ("varReason: unassigned var " ++ show v)
    Just a -> return (aReason a)

-- | Solver instance
data Solver
  = Solver
  { svOk           :: !(IORef Bool)
  , svVarQueue     :: !PQ.PriorityQueue
  , svTrail        :: !(IORef [Lit])
  , svVarCounter   :: !(IORef Int)
  , svVarData      :: !(IORef (IOArray Int VarData))
  , svClauseDB     :: !(IORef [SomeConstraint])
  , svLearntDB     :: !(IORef (Int,[SomeConstraint]))
  , svAssumptions  :: !(IORef (IOUArray Int Lit))
  , svLevel        :: !(IORef Level)
  , svBCPQueue     :: !(SQ.SeqQueue Lit)
  , svModel        :: !(IORef (Maybe Model))
  , svNDecision    :: !(IORef Int)
  , svNRandomDecision :: !(IORef Int)
  , svNConflict    :: !(IORef Int)
  , svNRestart     :: !(IORef Int)
  , svNAssigns     :: !(IORef Int)
  , svNFixed       :: !(IORef Int)
  , svNLearntGC    :: !(IORef Int)
  , svNRemovedConstr :: !(IORef Int)

  -- | Inverse of the variable activity decay factor. (default 1 / 0.95)
  , svVarDecay     :: !(IORef Double)

  -- | Amount to bump next variable with.
  , svVarInc       :: !(IORef Double)

  -- | Inverse of the clause activity decay factor. (1 / 0.999)
  , svClaDecay     :: !(IORef Double)

  -- | Amount to bump next clause with.
  , svClaInc       :: !(IORef Double)

  , svRestartStrategy :: !(IORef RestartStrategy)

  -- | The initial restart limit. (default 100)
  , svRestartFirst :: !(IORef Int)

  -- | The factor with which the restart limit is multiplied in each restart. (default 1.5)
  , svRestartInc :: !(IORef Double)

  -- | The initial limit for learnt clauses.
  , svLearntSizeFirst :: !(IORef Int)

  -- | The limit for learnt clauses is multiplied with this factor periodically. (default 1.1)
  , svLearntSizeInc :: !(IORef Double)

  , svLearntLim       :: !(IORef Int)
  , svLearntLimAdjCnt :: !(IORef Int)
  , svLearntLimSeq    :: !(IORef [(Int,Int)])

  -- | Controls conflict clause minimization (0=none, 1=local, 2=recursive)
  , svCCMin :: !(IORef Int)

  , svLearningStrategy :: !(IORef LearningStrategy)

  , svLogger :: !(IORef (Maybe (String -> IO ())))
  , svStartWC    :: !(IORef UTCTime)
  , svLastStatWC :: !(IORef UTCTime)

  , svCheckModel :: !(IORef Bool)

  , svRandomFreq :: !(IORef Double)
  , svRandomGen  :: !(IORef Rand.StdGen)

  , svFailedAssumptions :: !(IORef [Lit])

  , svConfBudget :: !(IORef Int)
  }

markBad :: Solver -> IO ()
markBad solver = writeIORef (svOk solver) False

bcpEnqueue :: Solver -> Lit -> IO ()
bcpEnqueue solver l = SQ.enqueue (svBCPQueue solver) l

bcpDequeue :: Solver -> IO (Maybe Lit)
bcpDequeue solver = SQ.dequeue (svBCPQueue solver)

assignBy :: Constraint c => Solver -> Lit -> c -> IO Bool
assignBy solver lit c = assign_ solver lit (Just (toConstraint c))

assign :: Solver -> Lit -> IO Bool
assign solver lit = assign_ solver lit Nothing

assign_ :: Solver -> Lit -> Maybe SomeConstraint -> IO Bool
assign_ solver !lit reason = assert (validLit lit) $ do
  vd <- varData solver (litVar lit)
  m <- readIORef (vdAssignment vd)
  case m of
    Just a -> return $ litPolarity lit == aValue a
    Nothing -> do
      lv <- readIORef (svLevel solver)
      bt <- newIORef []

      writeIORef (vdAssignment vd) $ Just $
        Assignment
        { aValue  = litPolarity lit
        , aLevel  = lv
        , aReason = reason
        , aBacktrackCBs = bt
        }

      modifyIORef (svTrail solver) (lit:)
      modifyIORef' (svNAssigns solver) (+1)
      when (lv == levelRoot) $ modifyIORef' (svNFixed solver) (+1)
      bcpEnqueue solver lit

      when debugMode $ logIO solver $ do
        let r = case reason of
                  Nothing -> ""
                  Just _ -> " by propagation"
        return $ printf "assign(level=%d): %d%s" lv lit r

      return True

unassign :: Solver -> Var -> IO ()
unassign solver !v = assert (validVar v) $ do
  vd <- varData solver v
  m <- readIORef (vdAssignment vd)
  case m of
    Nothing -> error "unassign: should not happen"
    Just a -> do
      writeIORef (vdPolarity vd) (aValue a)
      readIORef (aBacktrackCBs a) >>= sequence_
  writeIORef (vdAssignment vd) Nothing
  modifyIORef' (svNAssigns solver) (subtract 1)
  PQ.enqueue (svVarQueue solver) v

addBacktrackCB :: Solver -> Var -> IO () -> IO ()
addBacktrackCB solver !v callback = do
  vd <- varData solver v
  m <- readIORef (vdAssignment vd)
  case m of
    Nothing -> error "addBacktrackCB: should not happen"
    Just a -> modifyIORef (aBacktrackCBs a) (callback :)

-- | Register the constraint to be notified when the literal becames false.
watch :: Constraint c => Solver -> Lit -> c -> IO ()
watch solver !lit c = do
  when debugMode $ do
    lits <- watchedLiterals solver c
    unless (lit `elem` lits) $ error "watch: should not happen"
  ld <- litData solver lit
  modifyIORef (ldWatches ld) (toConstraint c : )

-- | Returns list of constraints that are watching the literal.
watches :: Solver -> Lit -> IO [SomeConstraint]
watches solver !lit = do
  ld <- litData solver lit
  readIORef (ldWatches ld)

addToDB :: Constraint c => Solver -> c -> IO ()
addToDB solver c = do
  modifyIORef (svClauseDB solver) (toConstraint c : )
  when debugMode $ logIO solver $ do
    str <- showConstraint solver c
    return $ printf "constraint %s is added" str
  sanityCheck solver

addToLearntDB :: Constraint c => Solver -> c -> IO ()
addToLearntDB solver c = do
  modifyIORef (svLearntDB solver) $ \(n,xs) -> (n+1, toConstraint c : xs)
  when debugMode $ logIO solver $ do
    str <- showConstraint solver c
    return $ printf "constraint %s is added" str
  sanityCheck solver

reduceDB :: Solver -> IO ()
reduceDB solver = do
  (_,cs) <- readIORef (svLearntDB solver)

  xs <- forM cs $ \c -> do
    p <- constrIsProtected solver c
    actval <- constrActivity solver c
    return (c, (p, actval))

  -- Note that False <= True
  let ys = sortBy (comparing snd) xs
      (zs,ws) = splitAt (length ys `div` 2) ys

  let loop [] ret = return ret
      loop ((c,(isShort,_)) : rest) ret = do
        flag <- if isShort
                then return True
                else isLocked solver c
        if flag
          then loop rest (c:ret)
          else do
            detach solver c
            loop rest ret
  zs2 <- loop zs []

  let cs2 = zs2 ++ map fst ws
      n2 = length cs2

  -- log solver $ printf "learnt constraints deletion: %d -> %d" n n2
  writeIORef (svLearntDB solver) (n2,cs2)

type VarActivity = Double

varActivity :: Solver -> Var -> IO VarActivity
varActivity solver !v = do
  vd <- varData solver v
  readIORef (vdActivity vd)

varDecayActivity :: Solver -> IO ()
varDecayActivity solver = do
  d <- readIORef (svVarDecay solver)
  modifyIORef' (svVarInc solver) (d*)

varBumpActivity :: Solver -> Var -> IO ()
varBumpActivity solver !v = do
  inc <- readIORef (svVarInc solver)
  vd <- varData solver v
  modifyIORef' (vdActivity vd) (+inc)
  PQ.update (svVarQueue solver) v
  aval <- readIORef (vdActivity vd)
  when (aval > 1e20) $
    -- Rescale
    varRescaleAllActivity solver

varRescaleAllActivity :: Solver -> IO ()
varRescaleAllActivity solver = do
  vs <- variables solver
  forM_ vs $ \v -> do
    vd <- varData solver v
    modifyIORef' (vdActivity vd) (* 1e-20)
  modifyIORef' (svVarInc solver) (* 1e-20)

variables :: Solver -> IO [Var]
variables solver = do
  n <- nVars solver
  return [1 .. n]

-- | number of variables of the problem.
nVars :: Solver -> IO Int
nVars solver = do
  vcnt <- readIORef (svVarCounter solver)
  return $! (vcnt-1)

-- | number of assigned variables.
nAssigns :: Solver -> IO Int
nAssigns solver = readIORef (svNAssigns solver)

-- | number of clauses.
nClauses :: Solver -> IO Int
nClauses solver = do
  xs <- readIORef (svClauseDB solver)
  return $ length xs

-- | number of learnt constrints.
nLearnt :: Solver -> IO Int
nLearnt solver = do
  (n,_) <- readIORef (svLearntDB solver)
  return n

learntConstraints :: Solver -> IO [SomeConstraint]
learntConstraints solver = do
  (_,cs) <- readIORef (svLearntDB solver)
  return cs

{--------------------------------------------------------------------
  Solver
--------------------------------------------------------------------}

-- | Create a new Solver instance.
newSolver :: IO Solver
newSolver = do
 rec
  ok   <- newIORef True
  vcnt <- newIORef 1
  trail <- newIORef []
  vars <- newIORef =<< newArray_ (1,0)
  vqueue <- PQ.newPriorityQueueBy (ltVar solver)
  db  <- newIORef []
  db2 <- newIORef (0,[])
  as  <- newIORef =<< newArray_ (0,-1)
  lv  <- newIORef levelRoot
  q   <- SQ.newFifo
  m   <- newIORef Nothing
  ndecision <- newIORef 0
  nranddec  <- newIORef 0
  nconflict <- newIORef 0
  nrestart  <- newIORef 0
  nassigns  <- newIORef 0
  nfixed    <- newIORef 0
  nlearntgc <- newIORef 0
  nremoved  <- newIORef 0

  claDecay <- newIORef (1 / 0.999)
  claInc   <- newIORef 1
  varDecay <- newIORef (1 / 0.95)
  varInc   <- newIORef 1
  restartStrat <- newIORef defaultRestartStrategy
  restartFirst <- newIORef defaultRestartFirst
  restartInc <- newIORef defaultRestartInc
  learning <- newIORef defaultLearningStrategy
  learntSizeFirst <- newIORef defaultLearntSizeFirst
  learntSizeInc <- newIORef defaultLearntSizeInc
  ccMin <- newIORef defaultCCMin
  checkModel <- newIORef False

  learntLim       <- newIORef undefined
  learntLimAdjCnt <- newIORef (-1)
  learntLimSeq    <- newIORef undefined

  logger <- newIORef Nothing
  startWC    <- newIORef undefined
  lastStatWC <- newIORef undefined

  randfreq <- newIORef defaultRandomFreq
  randgen  <- newIORef =<< Rand.newStdGen

  failed <- newIORef []

  confBudget <- newIORef (-1)

  let solver =
        Solver
        { svOk = ok
        , svVarCounter = vcnt
        , svVarQueue   = vqueue
        , svTrail      = trail
        , svVarData    = vars
        , svClauseDB   = db
        , svLearntDB   = db2
        , svAssumptions = as
        , svLevel      = lv
        , svBCPQueue   = q
        , svModel      = m
        , svNDecision  = ndecision
        , svNRandomDecision = nranddec
        , svNConflict  = nconflict
        , svNRestart   = nrestart
        , svNAssigns   = nassigns
        , svNFixed     = nfixed
        , svNLearntGC  = nlearntgc
        , svNRemovedConstr = nremoved
        , svVarDecay   = varDecay
        , svVarInc     = varInc
        , svClaDecay   = claDecay
        , svClaInc     = claInc
        , svRestartStrategy = restartStrat
        , svRestartFirst = restartFirst
        , svRestartInc   = restartInc
        , svLearningStrategy = learning
        , svLearntSizeFirst = learntSizeFirst
        , svLearntSizeInc = learntSizeInc
        , svCCMin = ccMin
        , svLearntLim       = learntLim
        , svLearntLimAdjCnt = learntLimAdjCnt
        , svLearntLimSeq    = learntLimSeq
        , svLogger = logger
        , svStartWC    = startWC
        , svLastStatWC = lastStatWC
        , svCheckModel = checkModel
        , svRandomFreq = randfreq
        , svRandomGen  = randgen
        , svFailedAssumptions = failed
        , svConfBudget = confBudget
        }
 return solver

ltVar :: Solver -> Var -> Var -> IO Bool
ltVar solver v1 v2 = do
  a1 <- varActivity solver v1
  a2 <- varActivity solver v2
  return $! a1 > a2

{--------------------------------------------------------------------
  Problem specification
--------------------------------------------------------------------}

-- |Add a new variable
newVar :: Solver -> IO Var
newVar s = do
  v <- readIORef (svVarCounter s)
  writeIORef (svVarCounter s) (v+1)
  vd <- newVarData

  a <- readIORef (svVarData s)
  (_,ub) <- getBounds a
  if v <= ub
    then writeArray a v vd
    else do
      let ub' = max 2 (ub * 3 `div` 2)
      a' <- newArray_ (1,ub')
      forM_ [1..ub] $ \v2 -> do
        vd2 <- readArray a v2
        writeArray a' v2 vd2
      writeArray a' v vd
      writeIORef (svVarData s) a'

  PQ.enqueue (svVarQueue s) v
  return v

-- |Add variables. @newVars solver n = replicateM n (newVar solver)@
newVars :: Solver -> Int -> IO [Var]
newVars s n = replicateM n (newVar s)

-- |Add variables. @newVars_ solver n >> return () = newVars_ solver n@
newVars_ :: Solver -> Int -> IO ()
newVars_ s n = replicateM_ n (newVar s)

-- |Add a clause to the solver.
addClause :: Solver -> Clause -> IO ()
addClause solver lits = do
  d <- readIORef (svLevel solver)
  assert (d == levelRoot) $ return ()

  lits2 <- instantiateClause solver lits
  case normalizeClause =<< lits2 of
    Nothing -> return ()
    Just [] -> markBad solver
    Just [lit] -> do
      ret <- assign solver lit
      assert ret $ return ()
      ret2 <- deduce solver
      case ret2 of
        Nothing -> return ()
        Just _ -> markBad solver
    Just lits3 -> do
      clause <- newClauseData lits3 False
      attach solver clause
      addToDB solver clause

-- | Add a cardinality constraints /atleast({l1,l2,..},n)/.
addAtLeast :: Solver -- ^ The 'Solver' argument.
           -> [Lit]  -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
           -> Int    -- ^ /n/.
           -> IO ()
addAtLeast solver lits n = do
  d <- readIORef (svLevel solver)
  assert (d == levelRoot) $ return ()

  (lits',n') <- liftM normalizeAtLeast $ instantiateAtLeast solver (lits,n)
  let len = length lits'

  if n' <= 0 then return ()
    else if n' > len then markBad solver
    else if n' == 1 then addClause solver lits'
    else if n' == len then do
      forM_ lits' $ \l -> do
        ret <- assign solver l
        assert ret $ return ()
        ret2 <- deduce solver
        case ret2 of
          Nothing -> return ()
          Just _ -> markBad solver
    else do
      c <- newAtLeastData lits' n' False
      attach solver c
      addToDB solver c

-- | Add a cardinality constraints /atmost({l1,l2,..},n)/.
addAtMost :: Solver -- ^ The 'Solver' argument
          -> [Lit]  -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
          -> Int    -- ^ /n/
          -> IO ()
addAtMost solver lits n = addAtLeast solver lits' (len-n)
  where
    len   = length lits
    lits' = map litNot lits

-- | Add a cardinality constraints /exactly({l1,l2,..},n)/.
addExactly :: Solver -- ^ The 'Solver' argument
           -> [Lit]  -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
           -> Int    -- ^ /n/
           -> IO ()
addExactly solver lits n = do
  addAtLeast solver lits n
  addAtMost solver lits n

-- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≥ n/.
addPBAtLeast :: Solver          -- ^ The 'Solver' argument.
             -> [(Integer,Lit)] -- ^ set of terms @[(c1,l1),(c2,l2),…]@
             -> Integer         -- ^ /n/
             -> IO ()
addPBAtLeast solver ts n = do
  d <- readIORef (svLevel solver)
  assert (d == levelRoot) $ return ()

  (ts',degree) <- liftM normalizePBAtLeast $ instantiatePB solver (ts,n)
  let cs = map fst ts'
      slack = sum cs - degree

  if degree <= 0 then return ()
    else if slack < 0 then markBad solver
    else if Set.size (Set.fromList cs) == 1 then do
      let c = head cs
      addAtLeast solver (map snd ts') (fromInteger ((degree+c-1) `div` c))
    else do
      c <- newPBAtLeastData ts' degree False
      attach solver c
      addToDB solver c
      ret <- pbPropagate solver c
      if not ret
       then do
         markBad solver
       else do
         ret2 <- deduce solver
         case ret2 of
           Nothing -> return ()
           Just _ -> markBad solver

-- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≤ n/.
addPBAtMost :: Solver          -- ^ The 'Solver' argument.
            -> [(Integer,Lit)] -- ^ list of @[(c1,l1),(c2,l2),…]@
            -> Integer         -- ^ /n/
            -> IO ()
addPBAtMost solver ts n = addPBAtLeast solver [(-c,l) | (c,l) <- ts] (negate n)

-- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … = n/.
addPBExactly :: Solver          -- ^ The 'Solver' argument.
             -> [(Integer,Lit)] -- ^ list of terms @[(c1,l1),(c2,l2),…]@
             -> Integer         -- ^ /n/
             -> IO ()
addPBExactly solver ts n = do
  (ts2,n2) <- liftM normalizePBExactly $ instantiatePB solver (ts,n)
  addPBAtLeast solver ts2 n2
  addPBAtMost solver ts2 n2

-- | Add a soft pseudo boolean constraints /lit ⇒ c1*l1 + c2*l2 + … ≥ n/.
addPBAtLeastSoft
  :: Solver          -- ^ The 'Solver' argument.
  -> Lit             -- ^ indicator @lit@
  -> [(Integer,Lit)] -- ^ set of terms @[(c1,l1),(c2,l2),…]@
  -> Integer         -- ^ /n/
  -> IO ()
addPBAtLeastSoft solver sel lhs rhs = do
  (lhs', rhs') <- liftM normalizePBAtLeast $ instantiatePB solver (lhs,rhs)
  addPBAtLeast solver ((rhs', litNot sel) : lhs') rhs'

-- | Add a soft pseudo boolean constraints /lit ⇒ c1*l1 + c2*l2 + … ≤ n/.
addPBAtMostSoft
  :: Solver          -- ^ The 'Solver' argument.
  -> Lit             -- ^ indicator @lit@
  -> [(Integer,Lit)] -- ^ set of terms @[(c1,l1),(c2,l2),…]@
  -> Integer         -- ^ /n/
  -> IO ()
addPBAtMostSoft solver sel lhs rhs =
  addPBAtLeastSoft solver sel [(negate c, lit) | (c,lit) <- lhs] (negate rhs)

-- | Add a soft pseudo boolean constraints /lit ⇒ c1*l1 + c2*l2 + … = n/.
addPBExactlySoft
  :: Solver          -- ^ The 'Solver' argument.
  -> Lit             -- ^ indicator @lit@
  -> [(Integer,Lit)] -- ^ set of terms @[(c1,l1),(c2,l2),…]@
  -> Integer         -- ^ /n/
  -> IO ()
addPBExactlySoft solver sel lhs rhs = do
  (lhs2, rhs2) <- liftM normalizePBExactly $ instantiatePB solver (lhs,rhs)
  addPBAtLeastSoft solver sel lhs2 rhs2
  addPBAtMostSoft solver sel lhs2 rhs2

{--------------------------------------------------------------------
  Problem solving
--------------------------------------------------------------------}

-- | Solve constraints.
-- Returns 'True' if the problem is SATISFIABLE.
-- Returns 'False' if the problem is UNSATISFIABLE.
solve :: Solver -> IO Bool
solve solver = do
  as <- newArray_ (0,-1)
  writeIORef (svAssumptions solver) as
  solve_ solver

-- | Solve constraints under assuptions.
-- Returns 'True' if the problem is SATISFIABLE.
-- Returns 'False' if the problem is UNSATISFIABLE.
solveWith :: Solver
          -> [Lit]    -- ^ Assumptions
          -> IO Bool
solveWith solver ls = do
  as <- newListArray (0, length ls -1) ls
  writeIORef (svAssumptions solver) as
  solve_ solver

solve_ :: Solver -> IO Bool
solve_ solver = do
  log solver "Solving starts ..."
  resetStat solver
  writeIORef (svModel solver) Nothing
  writeIORef (svFailedAssumptions solver) []

  ok <- readIORef (svOk solver)
  if not ok
    then return False
    else do
      when debugMode $ dumpVarActivity solver
      d <- readIORef (svLevel solver)
      assert (d == levelRoot) $ return ()

      restartStrategy <- readIORef (svRestartStrategy solver)
      restartFirst  <- readIORef (svRestartFirst solver)
      restartInc    <- readIORef (svRestartInc solver)
      let restartSeq = mkRestartSeq restartStrategy restartFirst restartInc

      let learntSizeAdj = do
            (size,adj) <- shift (svLearntLimSeq solver)
            writeIORef (svLearntLim solver) size
            writeIORef (svLearntLimAdjCnt solver) adj
          onConflict = do
            cnt <- readIORef (svLearntLimAdjCnt solver)
            if (cnt==0)
            then learntSizeAdj
            else writeIORef (svLearntLimAdjCnt solver) $! cnt-1

      cnt <- readIORef (svLearntLimAdjCnt solver)
      when (cnt == -1) $ do
        learntSizeFirst <- readIORef (svLearntSizeFirst solver)
        learntSizeInc   <- readIORef (svLearntSizeInc solver)
        nc <- nClauses solver
        nv <- nVars solver
        let initialLearntLim = if learntSizeFirst > 0 then learntSizeFirst else max ((nc + nv) `div` 3) 16
            learntSizeSeq    = iterate (ceiling . (learntSizeInc*) . fromIntegral) initialLearntLim
            learntSizeAdjSeq = iterate (\x -> (x * 3) `div` 2) (100::Int)
        writeIORef (svLearntLimSeq solver) (zip learntSizeSeq learntSizeAdjSeq)
        learntSizeAdj

      let loop [] = error "solve_: should not happen"
          loop (conflict_lim:rs) = do
            printStat solver True
            ret <- search solver conflict_lim onConflict
            case ret of
              SRFinished x -> return $ Just x
              SRBudgetExceeded -> return Nothing
              SRRestart -> do
                modifyIORef' (svNRestart solver) (+1)
                backtrackTo solver levelRoot
                loop rs

      printStatHeader solver

      startCPU <- getCPUTime
      startWC  <- getCurrentTime
      writeIORef (svStartWC solver) startWC
      result <- loop restartSeq
      endCPU <- getCPUTime
      endWC  <- getCurrentTime

      when (result == Just True) $ do
        checkModel <- readIORef (svCheckModel solver)
        when checkModel $ checkSatisfied solver
        constructModel solver

      backtrackTo solver levelRoot

      when debugMode $ dumpVarActivity solver
      when debugMode $ dumpClaActivity solver
      printStat solver True
      (log solver . printf "#cpu_time = %.3fs") (fromIntegral (endCPU - startCPU) / 10^(12::Int) :: Double)
      (log solver . printf "#wall_clock_time = %.3fs") (realToFrac (endWC `diffUTCTime` startWC) :: Double)
      (log solver . printf "#decision = %d") =<< readIORef (svNDecision solver)
      (log solver . printf "#random_decision = %d") =<< readIORef (svNRandomDecision solver)
      (log solver . printf "#conflict = %d") =<< readIORef (svNConflict solver)
      (log solver . printf "#restart = %d")  =<< readIORef (svNRestart solver)

      case result of
        Just x  -> return x
        Nothing -> throw BudgetExceeded

data BudgetExceeded = BudgetExceeded
  deriving (Show, Typeable)

instance Exception BudgetExceeded

data SearchResult
  = SRFinished Bool
  | SRRestart
  | SRBudgetExceeded

search :: Solver -> Int -> IO () -> IO SearchResult
search solver !conflict_lim onConflict = loop 0
  where
    loop :: Int -> IO SearchResult
    loop !c = do
      sanityCheck solver
      conflict <- deduce solver
      sanityCheck solver

      case conflict of
        Nothing -> do
          lv <- readIORef (svLevel solver)
          when (lv == levelRoot) $ simplify solver

          n <- nLearnt solver
          m <- nAssigns solver
          learnt_lim <- readIORef (svLearntLim solver)
          when (learnt_lim >= 0 && n - m > learnt_lim) $ do
            modifyIORef' (svNLearntGC solver) (+1)
            reduceDB solver

          r <- pickAssumption
          case r of
            Nothing -> return (SRFinished False)
            Just lit
              | lit /= litUndef -> decide solver lit >> loop c
              | otherwise -> do
                  lit2 <- pickBranchLit solver
                  if lit2 == litUndef
                    then return (SRFinished True)
                    else decide solver lit2 >> loop c

        Just constr -> do
          varDecayActivity solver
          constrDecayActivity solver
          onConflict

          modifyIORef' (svNConflict solver) (+1)
          d <- readIORef (svLevel solver)

          when debugMode $ logIO solver $ do
            str <- showConstraint solver constr
            return $ printf "conflict(level=%d): %s" d str

          when (c `mod` 100 == 0) $ do
            printStat solver False

          modifyIORef' (svConfBudget solver) $ \confBudget ->
            if confBudget > 0 then confBudget - 1 else confBudget
          confBudget <- readIORef (svConfBudget solver)

          if d == levelRoot
            then markBad solver >> return (SRFinished False)
            else if confBudget==0 then
              return SRBudgetExceeded
            else if conflict_lim >= 0 && c+1 >= conflict_lim then
              return SRRestart
            else do
              b <- handleConflict constr
              if b
                then loop (c+1)
                else markBad solver >> return (SRFinished False)

    pickAssumption :: IO (Maybe Lit)
    pickAssumption = do
      !as <- readIORef (svAssumptions solver)
      !b <- getBounds as
      let go = do
              d <- readIORef (svLevel solver)
              if not (inRange b (d+1))
                then return (Just litUndef)
                else do
                  l <- readArray as (d+1)
                  val <- litValue solver l
                  if val == lTrue then do
                     -- dummy decision level
                     modifyIORef' (svLevel solver) (+1)
                     go
                   else if val == lFalse then do
                     -- conflict with assumption
                     core <- analyzeFinal solver l
                     writeIORef (svFailedAssumptions solver) core
                     return Nothing
                   else
                     return (Just l)
      go

    handleConflict :: SomeConstraint -> IO Bool
    handleConflict constr = do
      strat <- readIORef (svLearningStrategy solver)
      case strat of
        LearningClause -> do
          (learntClause, level) <- analyzeConflict solver constr
          backtrackTo solver level
          case learntClause of
            [] -> error "search(LearningClause): should not happen"
            [lit] -> do
              ret <- assign solver lit
              assert ret $ return ()
              return ()
            lit:_ -> do
              cl <- newClauseData learntClause True
              attach solver cl
              addToLearntDB solver cl
              assignBy solver lit cl
              constrBumpActivity solver cl
          return True
        LearningHybrid -> do
          (learntClause, level, pb) <- analyzeConflictHybrid solver constr
          level2 <- pbBacktrackLevel solver pb
          let level3 = min level level2

          pbdata <- newPBAtLeastData (fst pb) (snd pb) True
          attach solver pbdata
          addToLearntDB solver pbdata

          backtrackTo solver level3
          slack <- readIORef (pbSlack pbdata)
          if slack < 0
            then do
              if level3 == levelRoot
               then return False
               else handleConflict (toConstraint pbdata)
            else do
              case learntClause of
                [] -> error "search(LearningHybrid): should not happen"
                [lit] -> do
                  ret <- assign solver lit
                  assert ret $ return ()
                  return ()
                lit:_ -> do
                  cl <- newClauseData learntClause True
                  attach solver cl
                  addToLearntDB solver cl
                  when (level3 == level) $ do
                    assignBy solver lit cl
                    constrBumpActivity solver cl
              pbPropagate solver pbdata
              return True

-- | After 'solve' returns True, it returns the model.
model :: Solver -> IO Model
model solver = do
  m <- readIORef (svModel solver)
  return (fromJust m)

-- | After 'solveWith' returns False, it returns a set of assumptions
-- that leads to contradiction. In particular, if it returns an empty
-- set, the problem is unsatisiable without any assumptions.
failedAssumptions :: Solver -> IO [Lit]
failedAssumptions solver = readIORef (svFailedAssumptions solver)

-- | Simplify the clause database according to the current top-level assigment.
simplify :: Solver -> IO ()
simplify solver = do
  let loop [] rs !n     = return (rs,n)
      loop (y:ys) rs !n = do
        b1 <- isSatisfied solver y
        b2 <- isLocked solver y
        if b1 && not b2
         then do
           detach solver y
           loop ys rs (n+1)
         else loop ys (y:rs) n

  -- simplify original constraint DB
  do
    xs <- readIORef (svClauseDB solver)
    (ys,n) <- loop xs [] (0::Int)
    modifyIORef' (svNRemovedConstr solver) (+n)
    writeIORef (svClauseDB solver) ys

  -- simplify learnt constraint DB
  do
    (m,xs) <- readIORef (svLearntDB solver)
    (ys,n) <- loop xs [] (0::Int)
    writeIORef (svLearntDB solver) (m-n, ys)

{--------------------------------------------------------------------
  Parameter settings.
--------------------------------------------------------------------}

setRestartStrategy :: Solver -> RestartStrategy -> IO ()
setRestartStrategy solver s = writeIORef (svRestartStrategy solver) s

-- | default value for @RestartStrategy@.
defaultRestartStrategy :: RestartStrategy
defaultRestartStrategy = MiniSATRestarts

-- | The initial restart limit. (default 100)
-- Negative value is used to disable restart.
setRestartFirst :: Solver -> Int -> IO ()
setRestartFirst solver !n = writeIORef (svRestartFirst solver) n

-- | default value for @RestartFirst@.
defaultRestartFirst :: Int
defaultRestartFirst = 100

-- | The factor with which the restart limit is multiplied in each restart. (default 1.5)
setRestartInc :: Solver -> Double -> IO ()
setRestartInc solver !r = writeIORef (svRestartInc solver) r

-- | default value for @RestartInc@.
defaultRestartInc :: Double
defaultRestartInc = 1.5

data LearningStrategy
  = LearningClause
  | LearningHybrid

setLearningStrategy :: Solver -> LearningStrategy -> IO ()
setLearningStrategy solver l = writeIORef (svLearningStrategy solver) $! l

defaultLearningStrategy :: LearningStrategy
defaultLearningStrategy = LearningClause

-- | The initial limit for learnt clauses.
setLearntSizeFirst :: Solver -> Int -> IO ()
setLearntSizeFirst solver !x = writeIORef (svLearntSizeFirst solver) x

-- | default value for @LearntSizeFirst@.
defaultLearntSizeFirst :: Int
defaultLearntSizeFirst = -1

-- | The limit for learnt clauses is multiplied with this factor each restart. (default 1.1)
setLearntSizeInc :: Solver -> Double -> IO ()
setLearntSizeInc solver !r = writeIORef (svLearntSizeInc solver) r

-- | default value for @LearntSizeInc@.
defaultLearntSizeInc :: Double
defaultLearntSizeInc = 1.1

-- | The limit for learnt clauses is multiplied with this factor each restart. (default 1.1)
setCCMin :: Solver -> Int -> IO ()
setCCMin solver !v = writeIORef (svCCMin solver) v

-- | default value for @CCMin@.
defaultCCMin :: Int
defaultCCMin = 2

-- | The default polarity of a variable.
setVarPolarity :: Solver -> Var -> Bool -> IO ()
setVarPolarity solver v val = do
  vd <- varData solver v
  writeIORef (vdPolarity vd) val

setCheckModel :: Solver -> Bool -> IO ()
setCheckModel solver flag = do
  writeIORef (svCheckModel solver) flag

-- | The frequency with which the decision heuristic tries to choose a random variable
setRandomFreq :: Solver -> Double -> IO ()
setRandomFreq solver r = do
  writeIORef (svRandomFreq solver) r

defaultRandomFreq :: Double
defaultRandomFreq = 0.005

-- | Used by the random variable selection
setRandomSeed :: Solver -> Int -> IO ()
setRandomSeed solver seed = do
  writeIORef (svRandomGen solver) (Rand.mkStdGen seed)

setConfBudget :: Solver -> Maybe Int -> IO ()
setConfBudget solver (Just b) | b >= 0 = writeIORef (svConfBudget solver) b
setConfBudget solver _ = writeIORef (svConfBudget solver) (-1)

{--------------------------------------------------------------------
  API for implementation of @solve@
--------------------------------------------------------------------}

pickBranchLit :: Solver -> IO Lit
pickBranchLit !solver = do
  let vqueue = svVarQueue solver

  -- Random decision
  let withRandGen :: (Rand.StdGen -> (a, Rand.StdGen)) -> IO a
      withRandGen f = do
        randgen  <- readIORef (svRandomGen solver)
        let (r, randgen') = f randgen
        writeIORef (svRandomGen solver) randgen'
        return r
  !randfreq <- readIORef (svRandomFreq solver)
  !size <- PQ.queueSize vqueue
  !r <- withRandGen Rand.random
  var <-
    if (r < randfreq && size >= 2)
    then do
      a <- PQ.getHeapArray vqueue
      i <- withRandGen $ Rand.randomR (0, size-1)
      var <- readArray a i
      val <- varValue solver var
      if val == lUndef
       then do
         modifyIORef' (svNRandomDecision solver) (1+)
         return var
       else return litUndef
    else
      return litUndef

  -- Activity based decision
  let loop :: IO Var
      loop = do
        m <- PQ.dequeue vqueue
        case m of
          Nothing -> return litUndef
          Just var2 -> do
            val2 <- varValue solver var2
            if val2 /= lUndef
              then loop
              else return var2
  var2 <-
    if var==litUndef
    then loop
    else return var

  if var2==litUndef
    then return litUndef
    else do
      vd <- varData solver var2
      -- TODO: random polarity
      p <- readIORef (vdPolarity vd)
      return $! literal var2 p

decide :: Solver -> Lit -> IO ()
decide solver !lit = do
  modifyIORef' (svNDecision solver) (+1)
  modifyIORef' (svLevel solver) (+1)
  when debugMode $ do
    val <- litValue solver lit
    when (val /= lUndef) $ error "decide: should not happen"
  assign solver lit
  return ()

deduce :: Solver -> IO (Maybe SomeConstraint)
deduce solver = loop
  where
    loop :: IO (Maybe SomeConstraint)
    loop = do
      r <- bcpDequeue solver
      case r of
        Nothing -> return Nothing
        Just lit -> do
          ret <- processLit lit
          case ret of
            Just _ -> return ret
            Nothing -> loop

    processLit :: Lit -> IO (Maybe SomeConstraint)
    processLit !lit = do
      let lit2 = litNot lit
      ld <- litData solver lit2
      let wsref = ldWatches ld
      let loop2 [] = return Nothing
          loop2 (w:ws) = do
            ok <- propagate solver w lit2
            if ok
              then loop2 ws
              else do
                modifyIORef wsref (++ws)
                return (Just w)
      ws <- readIORef wsref
      writeIORef wsref []
      loop2 ws

analyzeConflict :: Constraint c => Solver -> c -> IO (Clause, Level)
analyzeConflict solver constr = do
  d <- readIORef (svLevel solver)

  let split :: [Lit] -> IO (LitSet, LitSet)
      split = go (IS.empty, IS.empty)
        where
          go (xs,ys) [] = return (xs,ys)
          go (xs,ys) (l:ls) = do
            lv <- litLevel solver l
            if lv == levelRoot then
                go (xs,ys) ls
              else if lv >= d then
                go (IS.insert l xs, ys) ls
              else
                go (xs, IS.insert l ys) ls

  let loop :: LitSet -> LitSet -> IO LitSet
      loop lits1 lits2
        | sz==1 = do
            return $ lits1 `IS.union` lits2
        | sz>=2 = do
            ret <- popTrail solver
            case ret of
              Nothing -> do
                error $ printf "analyzeConflict: should not happen: empty trail: loop %s %s"
                               (show lits1) (show lits2)
              Just l -> do
                if litNot l `IS.notMember` lits1
                 then do
                   unassign solver (litVar l)
                   loop lits1 lits2
                 else do
                  m <- varReason solver (litVar l)
                  case m of
                    Nothing -> error "analyzeConflict: should not happen"
                    Just constr2 -> do
                      constrBumpActivity solver constr2
                      xs <- reasonOf solver constr2 (Just l)
                      forM_ xs $ \lit -> varBumpActivity solver (litVar lit)
                      unassign solver (litVar l)
                      (ys,zs) <- split xs
                      loop (IS.delete (litNot l) lits1 `IS.union` ys)
                           (lits2 `IS.union` zs)
        | otherwise = error "analyzeConflict: should not happen: reason of current level is empty"
        where
          sz = IS.size lits1

  constrBumpActivity solver constr
  conflictClause <- reasonOf solver constr Nothing
  forM_ conflictClause $ \lit -> varBumpActivity solver (litVar lit)
  (ys,zs) <- split conflictClause
  lits <- loop ys zs

  lits2 <- minimizeConflictClause solver lits

  xs <- liftM (sortBy (flip (comparing snd))) $
    forM (IS.toList lits2) $ \l -> do
      lv <- litLevel solver l
      return (l,lv)

  let level = case xs of
                [] -> error "analyzeConflict: should not happen"
                [_] -> levelRoot
                _:(_,lv):_ -> lv
  return (map fst xs, level)

-- { p } ∪ { pにfalseを割り当てる原因のassumption }
analyzeFinal :: Solver -> Lit -> IO [Lit]
analyzeFinal solver p = do
  lits <- readIORef (svTrail solver)
  let go :: [Lit] -> VarSet -> [Lit] -> IO [Lit]
      go [] _ result = return result
      go (l:ls) seen result = do
        lv <- litLevel solver l
        if lv == levelRoot then
           return result
         else if litVar l `IS.member` seen then do
           r <- varReason solver (litVar l)
           case r of
             Nothing -> do
               let seen' = IS.delete (litVar l) seen
               go ls seen' (l : result)
             Just constr  -> do
               c <- reasonOf solver constr (Just l)
               let seen' = IS.delete (litVar l) seen `IS.union` IS.fromList [litVar l2 | l2 <- c]
               go ls seen' result
         else
           go ls seen result
  go lits (IS.singleton (litVar p)) [p]

analyzeConflictHybrid :: Constraint c => Solver -> c -> IO (Clause, Level, ([(Integer,Lit)], Integer))
analyzeConflictHybrid solver constr = do
  d <- readIORef (svLevel solver)

  let split :: [Lit] -> IO (LitSet, LitSet)
      split = go (IS.empty, IS.empty)
        where
          go (xs,ys) [] = return (xs,ys)
          go (xs,ys) (l:ls) = do
            lv <- litLevel solver l
            if lv == levelRoot then
                go (xs,ys) ls
              else if lv >= d then
                go (IS.insert l xs, ys) ls
              else
                go (xs, IS.insert l ys) ls

  let loop :: LitSet -> LitSet -> ([(Integer,Lit)],Integer) -> IO (LitSet, ([(Integer,Lit)],Integer))
      loop lits1 lits2 pb
        | sz==1 = do
            return $ (lits1 `IS.union` lits2, pb)
        | sz>=2 = do
            ret <- popTrail solver
            case ret of
              Nothing -> do
                error $ printf "analyzeConflictHybrid: should not happen: empty trail: loop %s %s"
                               (show lits1) (show lits2)
              Just l -> do
                m <- varReason solver (litVar l)
                case m of
                  Nothing -> error "analyzeConflictHybrid: should not happen"
                  Just constr2 -> do
                    xs <- reasonOf solver constr2 (Just l)
                    (lits1',lits2') <-
                      if litNot l `IS.notMember` lits1
                      then return (lits1,lits2)
                      else do
                        constrBumpActivity solver constr2
                        forM_ xs $ \lit -> varBumpActivity solver (litVar lit)
                        (ys,zs) <- split xs
                        return  (IS.delete (litNot l) lits1 `IS.union` ys, lits2 `IS.union` zs)

                    pb2 <- toPBAtLeast solver constr2
                    o <- pbOverSAT solver pb2
                    let pb3 = if o then ([(1,l2) | l2 <- l:xs],1) else pb2
                    let pb' =
                           if any (\(_,l2) -> litNot l == l2) (fst pb)
                           then cutResolve pb pb3 (litVar l)
                           else pb

                    unassign solver (litVar l)
                    loop lits1' lits2' pb'

        | otherwise = error "analyzeConflictHybrid: should not happen: reason of current level is empty"
        where
          sz = IS.size lits1

  constrBumpActivity solver constr
  conflictClause <- reasonOf solver constr Nothing
  pbConfl <- toPBAtLeast solver constr
  forM_ conflictClause $ \lit -> varBumpActivity solver (litVar lit)
  (ys,zs) <- split conflictClause
  (lits, pb) <- loop ys zs pbConfl

  lits2 <- minimizeConflictClause solver lits

  xs <- liftM (sortBy (flip (comparing snd))) $
    forM (IS.toList lits2) $ \l -> do
      lv <- litLevel solver l
      return (l,lv)

  let level = case xs of
                [] -> error "analyzeConflict: should not happen"
                [_] -> levelRoot
                _:(_,lv):_ -> lv
  return (map fst xs, level, pb)

pbBacktrackLevel :: Solver -> ([(Integer,Lit)],Integer) -> IO Level
pbBacktrackLevel _ ([], rhs) = assert (rhs > 0) $ return levelRoot
pbBacktrackLevel solver (lhs, rhs) = do
  let go lvs lhs' s = 
        case IS.minView lvs of
          Nothing -> error "pbBacktrackLevel: should not happen"
          Just (lv2, lvs2) -> do
            let s2   = s - sum [c | (c,_,lv3) <- lhs', Just lv2 == lv3]
                lhs2 = [x | x@(_,_,lv3) <- lhs', Just lv2 /= lv3]
            if s2 < 0 then
              return lv2 -- conflict
            else if takeWhile (\(c,_,_) -> c > s) lhs2 /= [] then
              return lv2 --unit
            else
              go lvs2 lhs2 s2

  lhs' <- forM (sortBy (flip (comparing fst)) lhs) $ \(c, lit) -> do
    v <- litValue solver lit
    if v /= lFalse
      then return (c, lit, Nothing)
      else do
        lv <- litLevel solver lit
        return (c, lit, Just lv)

  let lvs = IS.fromList [lv | (_,_,Just lv) <- lhs']
      s0 = sum [c | (c,_) <- lhs] - rhs
  go lvs lhs' s0

minimizeConflictClause :: Solver -> LitSet -> IO LitSet
minimizeConflictClause solver lits = do
  ccmin <- readIORef (svCCMin solver)
  if ccmin >= 2 then
     minimizeConflictClauseRecursive solver lits
   else if ccmin >= 1 then
     minimizeConflictClauseLocal solver lits
   else
     return lits

minimizeConflictClauseLocal :: Solver -> LitSet -> IO LitSet
minimizeConflictClauseLocal solver lits = do
  let xs = IS.toAscList lits
  ys <- filterM (liftM not . isRedundant) xs
  when debugMode $ do
    log solver "minimizeConflictClauseLocal:"
    log solver $ show xs
    log solver $ show ys
  return $ IS.fromAscList $ ys

  where
    isRedundant :: Lit -> IO Bool
    isRedundant lit = do
      c <- varReason solver (litVar lit)
      case c of
        Nothing -> return False
        Just c2 -> do
          ls <- reasonOf solver c2 (Just (litNot lit))
          allM test ls

    test :: Lit -> IO Bool
    test lit = do
      lv <- litLevel solver lit
      return $ lv == levelRoot || lit `IS.member` lits

minimizeConflictClauseRecursive :: Solver -> LitSet -> IO LitSet
minimizeConflictClauseRecursive solver lits = do
  let
    isRedundant :: Lit -> IO Bool
    isRedundant lit = do
      c <- varReason solver (litVar lit)
      case c of
        Nothing -> return False
        Just c2 -> do
          ls <- reasonOf solver c2 (Just (litNot lit))
          go ls IS.empty

    go :: [Lit] -> IS.IntSet -> IO Bool
    go [] _ = return True
    go (lit : ls) seen = do
      lv <- litLevel solver lit
      if lv == levelRoot || lit `IS.member` lits || lit `IS.member` seen
        then go ls seen
        else do
          c <- varReason solver (litVar lit)
          case c of
            Nothing -> return False
            Just c2 -> do
              ls2 <- reasonOf solver c2 (Just (litNot lit))
              go (ls2 ++ ls) (IS.insert lit seen)

  let xs = IS.toAscList lits
  ys <- filterM (liftM not . isRedundant) xs
  when debugMode $ do
    log solver "minimizeConflictClauseRecursive:"
    log solver $ show xs
    log solver $ show ys
  return $ IS.fromAscList $ ys

popTrail :: Solver -> IO (Maybe Lit)
popTrail solver = do
  m <- readIORef (svTrail solver)
  case m of
    []   -> return Nothing
    l:ls -> do
      writeIORef (svTrail solver) ls
      return $ Just l

-- | Revert to the state at given level
-- (keeping all assignment at @level@ but not beyond).
backtrackTo :: Solver -> Int -> IO ()
backtrackTo solver level = do
  when debugMode $ log solver $ printf "backtrackTo: %d" level
  writeIORef (svTrail solver) =<< loop =<< readIORef (svTrail solver)
  SQ.clear (svBCPQueue solver)
  writeIORef (svLevel solver) level
  where
    loop :: [Lit] -> IO [Lit]
    loop [] = return []
    loop lls@(l:ls) = do
      lv <- litLevel solver l
      if lv <= level
        then return lls
        else unassign solver (litVar l) >> loop ls

constructModel :: Solver -> IO ()
constructModel solver = do
  n <- nVars solver
  (marr::IOUArray Var Bool) <- newArray_ (1,n)
  forM_ [1..n] $ \v -> do
    vd <- varData solver v
    a <- readIORef (vdAssignment vd)
    writeArray marr v (aValue (fromJust a))
  m <- unsafeFreeze marr
  writeIORef (svModel solver) (Just m)

constrDecayActivity :: Solver -> IO ()
constrDecayActivity solver = do
  d <- readIORef (svClaDecay solver)
  modifyIORef' (svClaInc solver) (d*)

constrRescaleAllActivity :: Solver -> IO ()
constrRescaleAllActivity solver = do
  xs <- learntConstraints solver
  forM_ xs $ \c -> constrRescaleActivity solver c
  modifyIORef' (svClaInc solver) (* 1e-20)

resetStat :: Solver -> IO ()
resetStat solver = do
  writeIORef (svNDecision solver) 0
  writeIORef (svNRandomDecision solver) 0
  writeIORef (svNConflict solver) 0
  writeIORef (svNRestart solver) 0
  writeIORef (svNLearntGC  solver) 0

printStatHeader :: Solver -> IO ()
printStatHeader solver = do
  log solver $ "============================[ Search Statistics ]============================"
  log solver $ " Time | Restart | Decision | Conflict |      LEARNT     | Fixed    | Removed "
  log solver $ "      |         |          |          |    Limit     GC | Var      | Constra "
  log solver $ "============================================================================="

printStat :: Solver -> Bool -> IO ()
printStat solver force = do
  nowWC <- getCurrentTime
  b <- if force
       then return True
       else do
         lastWC <- readIORef (svLastStatWC solver)
         return $ (nowWC `diffUTCTime` lastWC) > 1
  when b $ do
    startWC   <- readIORef (svStartWC solver)
    let tm = showTimeDiff $ nowWC `diffUTCTime` startWC
    restart   <- readIORef (svNRestart solver)
    dec       <- readIORef (svNDecision solver)
    conflict  <- readIORef (svNConflict solver)
    learntLim <- readIORef (svLearntLim solver)
    learntGC  <- readIORef (svNLearntGC solver)
    fixed     <- readIORef (svNFixed solver)
    removed   <- readIORef (svNRemovedConstr solver)
    log solver $ printf "%s | %7d | %8d | %8d | %8d %6d | %8d | %8d"
      tm restart dec conflict learntLim learntGC fixed removed
    writeIORef (svLastStatWC solver) nowWC

showTimeDiff :: NominalDiffTime -> String
showTimeDiff sec
  | si <  100  = printf "%4.1fs" (fromRational s :: Double)
  | si <= 9999 = printf "%4ds" si
  | mi <  100  = printf "%4.1fm" (fromRational m :: Double)
  | mi <= 9999 = printf "%4dm" mi
  | hi <  100  = printf "%4.1fs" (fromRational h :: Double)
  | otherwise  = printf "%4dh" hi
  where
    s :: Rational
    s = realToFrac sec

    si :: Integer
    si = round s

    m :: Rational
    m = s / 60

    mi :: Integer
    mi = round m

    h :: Rational
    h = m / 60

    hi :: Integer
    hi = round h

{--------------------------------------------------------------------
  constraint implementation
--------------------------------------------------------------------}

class Constraint a where
  toConstraint :: a -> SomeConstraint

  showConstraint :: Solver -> a -> IO String

  attach :: Solver -> a -> IO ()

  watchedLiterals :: Solver -> a -> IO [Lit]

  -- | invoked with the watched literal when the literal is falsified.
  -- 'watch' で 'toConstraint' を呼び出して heap allocation が発生するのを
  -- 避けるために、元の 'SomeConstraint' も渡しておく。
  basicPropagate :: Solver -> SomeConstraint -> a -> Lit -> IO Bool

  -- | deduce a clause C∨l from the constraint and return C.
  -- C and l should be false and true respectively under the current
  -- assignment.
  basicReasonOf :: Solver -> a -> Maybe Lit -> IO Clause

  toPBAtLeast :: Solver -> a -> IO ([(Integer,Lit)], Integer)

  isSatisfied :: Solver -> a -> IO Bool

  constrIsProtected :: Solver -> a -> IO Bool
  constrIsProtected _ _ = return False

  constrActivity :: Solver -> a -> IO Double

  constrBumpActivity :: Solver -> a -> IO ()
  constrBumpActivity _ _ = return ()

  constrRescaleActivity :: Solver -> a -> IO ()
  constrRescaleActivity _ _ = return ()

detach :: Constraint a => Solver -> a -> IO ()
detach solver c = do
  let c2 = toConstraint c
  lits <- watchedLiterals solver c
  forM_ lits $ \lit -> do
    ld <- litData solver lit
    modifyIORef' (ldWatches ld) (delete c2)

-- | invoked with the watched literal when the literal is falsified.
propagate :: Solver -> SomeConstraint -> Lit -> IO Bool
propagate solver c l = basicPropagate solver c c l

-- | deduce a clause C∨l from the constraint and return C.
-- C and l should be false and true respectively under the current
-- assignment.
reasonOf :: Constraint a => Solver -> a -> Maybe Lit -> IO Clause
reasonOf solver c x = do
  when debugMode $
    case x of
      Nothing -> return ()
      Just lit -> do
        val <- litValue solver lit
        unless (lTrue == val) $ do
          str <- showConstraint solver c
          error (printf "reasonOf: value of literal %d should be True but %s (basicReasonOf %s %s)" lit (show val) str (show x))
  cl <- basicReasonOf solver c x
  when debugMode $ do
    forM_ cl $ \lit -> do
      val <- litValue solver lit
      unless (lFalse == val) $ do
        str <- showConstraint solver c
        error (printf "reasonOf: value of literal %d should be False but %s (basicReasonOf %s %s)" lit (show val) str (show x))
  return cl

isLocked :: Solver -> SomeConstraint -> IO Bool
isLocked solver c = anyM p =<< watchedLiterals solver c
  where
    p :: Lit -> IO Bool
    p lit = do
      val <- litValue solver lit
      if val /= lTrue
        then return False
        else do
          m <- varReason solver (litVar lit)
          case m of
            Nothing -> return False
            Just c2 -> return $! c == c2

data SomeConstraint
  = ConstrClause !ClauseData
  | ConstrAtLeast !AtLeastData
  | ConstrPBAtLeast !PBAtLeastData
  deriving Eq

instance Constraint SomeConstraint where
  toConstraint = id

  showConstraint s (ConstrClause c)    = showConstraint s c
  showConstraint s (ConstrAtLeast c)   = showConstraint s c
  showConstraint s (ConstrPBAtLeast c) = showConstraint s c

  attach s (ConstrClause c)    = attach s c
  attach s (ConstrAtLeast c)   = attach s c
  attach s (ConstrPBAtLeast c) = attach s c

  watchedLiterals s (ConstrClause c)    = watchedLiterals s c
  watchedLiterals s (ConstrAtLeast c)   = watchedLiterals s c
  watchedLiterals s (ConstrPBAtLeast c) = watchedLiterals s c

  basicPropagate s this (ConstrClause c)  lit   = basicPropagate s this c lit
  basicPropagate s this (ConstrAtLeast c) lit   = basicPropagate s this c lit
  basicPropagate s this (ConstrPBAtLeast c) lit = basicPropagate s this c lit

  basicReasonOf s (ConstrClause c)  l   = basicReasonOf s c l
  basicReasonOf s (ConstrAtLeast c) l   = basicReasonOf s c l
  basicReasonOf s (ConstrPBAtLeast c) l = basicReasonOf s c l

  toPBAtLeast s (ConstrClause c)    = toPBAtLeast s c
  toPBAtLeast s (ConstrAtLeast c)   = toPBAtLeast s c
  toPBAtLeast s (ConstrPBAtLeast c) = toPBAtLeast s c

  isSatisfied s (ConstrClause c)    = isSatisfied s c
  isSatisfied s (ConstrAtLeast c)   = isSatisfied s c
  isSatisfied s (ConstrPBAtLeast c) = isSatisfied s c

  constrIsProtected s (ConstrClause c)    = constrIsProtected s c
  constrIsProtected s (ConstrAtLeast c)   = constrIsProtected s c
  constrIsProtected s (ConstrPBAtLeast c) = constrIsProtected s c

  constrActivity s (ConstrClause c)    = constrActivity s c
  constrActivity s (ConstrAtLeast c)   = constrActivity s c
  constrActivity s (ConstrPBAtLeast c) = constrActivity s c

  constrBumpActivity s (ConstrClause c)    = constrBumpActivity s c
  constrBumpActivity s (ConstrAtLeast c)   = constrBumpActivity s c
  constrBumpActivity s (ConstrPBAtLeast c) = constrBumpActivity s c

  constrRescaleActivity s (ConstrClause c)    = constrRescaleActivity s c
  constrRescaleActivity s (ConstrAtLeast c)   = constrRescaleActivity s c
  constrRescaleActivity s (ConstrPBAtLeast c) = constrRescaleActivity s c

{--------------------------------------------------------------------
  Clause
--------------------------------------------------------------------}

data ClauseData
  = ClauseData
  { claLits :: !(IOUArray Int Lit)
  , claActivity :: !(IORef Double)
  }

instance Eq ClauseData where
  c1 == c2 = claActivity c1 == claActivity c2

newClauseData :: Clause -> Bool -> IO ClauseData
newClauseData ls learnt = do
  let size = length ls
  a <- newListArray (0, size-1) ls
  act <- newIORef $! (if learnt then 0 else -1)
  return (ClauseData a act)

instance Constraint ClauseData where
  toConstraint = ConstrClause

  showConstraint _ this = do
    lits <- getElems (claLits this)
    return (show lits)

  attach solver this = do
    lits <- getElems (claLits this)
    case lits of
      l1:l2:_ -> do
        watch solver l1 this
        watch solver l2 this
      _ -> return ()

  watchedLiterals _ this = do
    lits <- getElems (claLits this)
    case lits of
      l1:l2:_ -> return [l1, l2]
      _ -> return []

  basicPropagate !s this this2 !falsifiedLit = do
    preprocess

    !lit0 <- unsafeRead a 0
    !val0 <- litValue s lit0
    if val0 == lTrue
      then do
        watch s falsifiedLit this
        return True
      else do
        (!lb,!ub) <- getBounds a
        assert (lb==0) $ return ()
        i <- findForWatch 2 ub
        case i of
          -1 -> do
            when debugMode $ logIO s $ do
               str <- showConstraint s this
               return $ printf "basicPropagate: %s is unit" str
            watch s falsifiedLit this
            assignBy s lit0 this
          _  -> do
            !lit1 <- unsafeRead a 1
            !liti <- unsafeRead a i
            unsafeWrite a 1 liti
            unsafeWrite a i lit1
            watch s liti this
            return True

    where
      a = claLits this2

      preprocess :: IO ()
      preprocess = do
        !l0 <- unsafeRead a 0
        !l1 <- unsafeRead a 1
        assert (l0==falsifiedLit || l1==falsifiedLit) $ return ()
        when (l0==falsifiedLit) $ do
          unsafeWrite a 0 l1
          unsafeWrite a 1 l0

      -- Maybe を heap allocation するのを避けるために、
      -- 見つからなかったときは -1 を返すことに。
      findForWatch :: Int -> Int -> IO Int
      findForWatch i end | i > end = return (-1)
      findForWatch i end = do
        val <- litValue s =<< unsafeRead a i
        if val /= lFalse
          then return i
          else findForWatch (i+1) end

  basicReasonOf _ this l = do
    lits <- getElems (claLits this)
    case l of
      Nothing -> return lits
      Just lit -> do
        assert (lit == head lits) $ return ()
        return $ tail lits

  toPBAtLeast _ (ClauseData a _) = do
    lits <- getElems a
    return ([(1,l) | l <- lits], 1)

  isSatisfied solver this = do
    lits <- getElems (claLits this)
    vals <- mapM (litValue solver) lits
    return $ lTrue `elem` vals

  constrIsProtected _ this = do
    size <- liftM rangeSize (getBounds (claLits this))
    return $! size <= 2

  constrActivity _ this = do
    let act = claActivity this
    readIORef act

  constrBumpActivity solver this = do
    let act = claActivity this
    aval <- readIORef act
    when (aval >= 0) $ do -- learnt clause
      inc <- readIORef (svClaInc solver)
      let aval2 = aval+inc
      writeIORef act $! aval2
      when (aval2 > 1e20) $
        -- Rescale
        constrRescaleAllActivity solver

  constrRescaleActivity _ this = do
    let act = claActivity this
    aval <- readIORef act
    when (aval >= 0) $ writeIORef act $! (aval * 1e-20)

instantiateClause :: Solver -> Clause -> IO (Maybe Clause)
instantiateClause solver = loop []
  where
    loop :: [Lit] -> [Lit] -> IO (Maybe Clause)
    loop ret [] = return $ Just ret
    loop ret (l:ls) = do
      val <- litValue solver l
      if val==lTrue then
         return Nothing
       else if val==lFalse then
         loop ret ls
       else
         loop (l : ret) ls

{--------------------------------------------------------------------
  Cardinality Constraint
--------------------------------------------------------------------}

data AtLeastData
  = AtLeastData
  { atLeastLits :: IOUArray Int Lit
  , atLeastNum :: !Int
  , atLeastActivity :: !(IORef Double)
  }
  deriving Eq

newAtLeastData :: [Lit] -> Int -> Bool -> IO AtLeastData
newAtLeastData ls n learnt = do
  let size = length ls
  a <- newListArray (0, size-1) ls
  act <- newIORef $! (if learnt then 0 else -1)
  return (AtLeastData a n act)

instance Constraint AtLeastData where
  toConstraint = ConstrAtLeast

  showConstraint _ this = do
    lits <- getElems (atLeastLits this)
    return $ show lits ++ " >= " ++ show (atLeastNum this)

  attach solver this = do
    lits <- getElems (atLeastLits this)
    let n = atLeastNum this
    let ws = if length lits > n then take (n+1) lits else []
    forM_ ws $ \l -> watch solver l this

  watchedLiterals _ this = do
    lits <- getElems (atLeastLits this)
    let n = atLeastNum this
    let ws = if length lits > n then take (n+1) lits else []
    return ws

  basicPropagate s this this2 falsifiedLit = do
    preprocess

    when debugMode $ do
      litn <- readArray a n
      unless (litn == falsifiedLit) $ error "AtLeastData.basicPropagate: should not happen"

    (lb,ub) <- getBounds a
    assert (lb==0) $ return ()
    ret <- findForWatch (n+1) ub
    case ret of
      Nothing -> do
        when debugMode $ logIO s $ do
          str <- showConstraint s this
          return $ printf "basicPropagate: %s is unit" str
        watch s falsifiedLit this
        let loop :: Int -> IO Bool
            loop i
              | i >= n = return True
              | otherwise = do
                  liti <- readArray a i
                  ret2 <- assignBy s liti this
                  if ret2
                    then loop (i+1)
                    else return False
        loop 0
      Just i  -> do
        liti <- readArray a i
        litn <- readArray a n
        writeArray a i litn
        writeArray a n liti
        watch s liti this
        return True

    where
      a = atLeastLits this2
      n = atLeastNum this2

      preprocess :: IO ()
      preprocess = loop 0
        where
          loop :: Int -> IO ()
          loop i
            | i >= n = return ()
            | otherwise = do
              li <- readArray a i
              if (li /= falsifiedLit)
                then loop (i+1)
                else do
                  ln <- readArray a n
                  writeArray a n li
                  writeArray a i ln

      findForWatch :: Int -> Int -> IO (Maybe Int)
      findForWatch i end | i > end = return Nothing
      findForWatch i end = do
        val <- litValue s =<< readArray a i
        if val /= lFalse
          then return (Just i)
          else findForWatch (i+1) end

  basicReasonOf s this concl = do
    lits <- getElems (atLeastLits this)
    let n = atLeastNum this
    case concl of
      Nothing -> do
        let f :: [Lit] -> IO Lit
            f [] = error "AtLeastData.basicReasonOf: should not happen"
            f (l:ls) = do
              val <- litValue s l
              if val == lFalse
                then return l
                else f ls
        lit <- f (take n lits)
        return $ lit : drop n lits
      Just lit -> do
        assert (lit `elem` take n lits) $ return ()
        return $ drop n lits

  toPBAtLeast _ this = do
    lits <- getElems (atLeastLits this)
    return ([(1,l) | l <- lits], fromIntegral (atLeastNum this))

  isSatisfied solver this = do
    lits <- getElems (atLeastLits this)
    vals <- mapM (litValue solver) lits
    return $ length [v | v <- vals, v == lTrue] >= atLeastNum this

  constrActivity _ this = do
    let act = atLeastActivity this
    readIORef act

  constrBumpActivity solver this = do
    let act = atLeastActivity this
    aval <- readIORef act
    when (aval >= 0) $ do -- learnt clause
      inc <- readIORef (svClaInc solver)
      let aval2 = aval+inc
      writeIORef act $! aval2
      when (aval2 > 1e20) $
        -- Rescale
        constrRescaleAllActivity solver

  constrRescaleActivity _ this = do
    let act = atLeastActivity this
    aval <- readIORef act
    when (aval >= 0) $ writeIORef act $! (aval * 1e-20)

instantiateAtLeast :: Solver -> ([Lit],Int) -> IO ([Lit],Int)
instantiateAtLeast solver (xs,n) = loop ([],n) xs
  where
    loop :: ([Lit],Int) -> [Lit] -> IO ([Lit],Int)
    loop ret [] = return ret
    loop (ys,m) (l:ls) = do
      val <- litValue solver l
      if val == lTrue then
         loop (ys, m-1) ls
       else if val == lFalse then
         loop (ys, m) ls
       else
         loop (l:ys, m) ls

{--------------------------------------------------------------------
  Pseudo Boolean Constraint
--------------------------------------------------------------------}

data PBAtLeastData
  = PBAtLeastData
  { pbTerms  :: !(LitMap Integer)
  , pbDegree :: !Integer
  , pbSlack  :: !(IORef Integer)
  , pbActivity :: !(IORef Double)
  , pbReasons :: !(IORef (LitMap [(Lit, Integer)]))
  }
  deriving Eq

newPBAtLeastData :: [(Integer,Lit)] -> Integer -> Bool -> IO PBAtLeastData
newPBAtLeastData ts degree learnt = do
  let slack = sum (map fst ts) - degree
      m = IM.fromList [(l,c) | (c,l) <- ts]
  s <- newIORef slack
  act <- newIORef $! (if learnt then 0 else -1)
  rs <- newIORef IM.empty
  return (PBAtLeastData m degree s act rs)

instance Constraint PBAtLeastData where
  toConstraint = ConstrPBAtLeast

  showConstraint _ this = do
    return $ show [(c,l) | (l,c) <- IM.toList (pbTerms this)] ++ " >= " ++ show (pbDegree this)

  attach solver this = do
    forM_ (IM.keys (pbTerms this)) $ \l -> watch solver l this
    cs <- forM (IM.toList (pbTerms this)) $ \(l,c) -> do
      v <- litValue solver l
      if v == lFalse
        then do
          addBacktrackCB solver (litVar l) $ modifyIORef' (pbSlack this) (+ c)
          return 0
        else return c
    writeIORef (pbSlack this) $! sum cs - pbDegree this

  watchedLiterals _ this = do
    return $ IM.keys $ pbTerms this

  basicPropagate solver this this2 falsifiedLit = do
    watch solver falsifiedLit this
    let c = pbTerms this2 IM.! falsifiedLit
    let slack = pbSlack this2
    modifyIORef' slack (subtract c)
    addBacktrackCB solver (litVar falsifiedLit) $ modifyIORef' slack (+ c)
    pbPropagate solver this2

  basicReasonOf solver this l = do
    let m = pbTerms this
    xs <-
      case l of
        Just lit -> do
          -- 保存しておいたものを使わないと、
          -- その後に割り当てられたものを含んでしまってまずいことがある。
          rs <- readIORef (pbReasons this)
          return $ sortBy (flip (comparing snd)) $ (rs IM.! lit)
        Nothing -> do
          -- 直接のコンフリクトの場合には現在のもので大丈夫なはず
          tmp <- filterM (\(lit,_) -> liftM (lFalse ==) (litValue solver lit)) (IM.toList m)
          return $ sortBy (flip (comparing snd)) $ tmp
    let max_slack = sum (map snd $ IM.toList m) - pbDegree this
    case l of
      Nothing -> return $ f max_slack xs
      Just lit -> return $ f (max_slack - (m IM.! lit)) xs
    where
      f :: Integer -> [(Lit,Integer)] -> [Lit]
      f s xs = go s xs []

      go :: Integer -> [(Lit,Integer)] -> [Lit] -> [Lit]
      go s _ ret | s < 0 = ret
      go _ [] _ = error "PBAtLeastData.basicReasonOf: should not happen"
      go s ((lit,c):xs) ret = go (s - c) xs (lit:ret)

  toPBAtLeast _ this = do
    return ([(c,l) | (l,c) <- IM.toList (pbTerms this)], pbDegree this)

  isSatisfied solver this = do
    xs <- forM (IM.toList (pbTerms this)) $ \(l,c) -> do
      v <- litValue solver l
      if v == lTrue
        then return c
        else return 0
    return $ sum xs >= pbDegree this

  constrActivity _ this = do
    let act = pbActivity this
    readIORef act

  constrBumpActivity solver this = do
    let act = pbActivity this
    aval <- readIORef act
    when (aval >= 0) $ do -- learnt clause
      inc <- readIORef (svClaInc solver)
      let aval2 = aval+inc
      writeIORef act $! aval2
      when (aval2 > 1e20) $
        -- Rescale
        constrRescaleAllActivity solver

  constrRescaleActivity _ this = do
    let act = pbActivity this
    aval <- readIORef act
    when (aval >= 0) $ writeIORef act $! (aval * 1e-20)

instantiatePB :: Solver -> ([(Integer,Lit)],Integer) -> IO ([(Integer,Lit)],Integer)
instantiatePB solver (xs,n) = loop ([],n) xs
  where
    loop :: ([(Integer,Lit)],Integer) -> [(Integer,Lit)] -> IO ([(Integer,Lit)],Integer)
    loop ret [] = return ret
    loop (ys,m) ((c,l):ts) = do
      val <- litValue solver l
      if val == lTrue then
         loop (ys, m-c) ts
       else if val == lFalse then
         loop (ys, m) ts
       else
         loop ((c,l):ys, m) ts

pbPropagate :: Solver -> PBAtLeastData -> IO Bool
pbPropagate solver this = do
  let slack = pbSlack this
  s <- readIORef slack
  if s < 0
    then return False
    else do
      ref <- newIORef Nothing
      let m = do
            x <- readIORef ref
            case x of
              Just r -> return r
              Nothing -> do
                let isFalse (l,_) = liftM (lFalse==) (litValue solver l)
                r <- filterM isFalse $ IM.toAscList $ pbTerms this
                writeIORef ref (Just r)
                return r

      forM_ (IM.toList (pbTerms this)) $ \(l1,c1) -> do
        when (c1 > s) $ do
          v <- litValue solver l1
          when (v == lUndef) $ do
            -- あとでbasicReasonOfで使うために、
            -- その時点でfalseになっているリテラルを保存しておく
            r <- m
            modifyIORef (pbReasons this) (IM.insert l1 r)

            assignBy solver l1 this
            constrBumpActivity solver this
            return ()

      return True

pbOverSAT :: Solver -> ([(Integer,Lit)],Integer) -> IO Bool
pbOverSAT solver (lhs, rhs) = do
  ss <- forM lhs $ \(c,l) -> do
    v <- litValue solver l
    if v /= lFalse
      then return c
      else return 0
  return $! sum ss > rhs

{--------------------------------------------------------------------
  Restart strategy
--------------------------------------------------------------------}

data RestartStrategy = MiniSATRestarts | ArminRestarts | LubyRestarts
  deriving (Show, Eq, Ord)

mkRestartSeq :: RestartStrategy -> Int -> Double -> [Int]
mkRestartSeq MiniSATRestarts = miniSatRestartSeq
mkRestartSeq ArminRestarts   = arminRestartSeq
mkRestartSeq LubyRestarts    = lubyRestartSeq

miniSatRestartSeq :: Int -> Double -> [Int]
miniSatRestartSeq start inc = iterate (ceiling . (inc *) . fromIntegral) start

{-
miniSatRestartSeq :: Int -> Double -> [Int]
miniSatRestartSeq start inc = map round $ iterate (inc*) (fromIntegral start)
-}

arminRestartSeq :: Int -> Double -> [Int]
arminRestartSeq start inc = go (fromIntegral start) (fromIntegral start)
  where  
    go !inner !outer = round inner : go inner' outer'
      where
        (inner',outer') = 
          if inner >= outer
          then (fromIntegral start, outer * inc)
          else (inner * inc, outer)

lubyRestartSeq :: Int -> Double -> [Int]
lubyRestartSeq start inc = map (ceiling . (fromIntegral start *) . luby inc) [0..]

{-
  Finite subsequences of the Luby-sequence:

  0: 1
  1: 1 1 2
  2: 1 1 2 1 1 2 4
  3: 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8
  ...


-}
luby :: Double -> Integer -> Double
luby y x = go2 size1 sequ1 x
  where
    -- Find the finite subsequence that contains index 'x', and the
    -- size of that subsequence:
    (size1, sequ1) = go 1 0

    go :: Integer -> Integer -> (Integer, Integer)
    go size sequ
      | size < x+1 = go (2*size+1) (sequ+1)
      | otherwise  = (size, sequ)

    go2 :: Integer -> Integer -> Integer -> Double
    go2 size sequ x2
      | size-1 /= x2 = let size' = (size-1) `div` 2 in go2 size' (sequ - 1) (x2 `mod` size')
      | otherwise = y ^ sequ


{--------------------------------------------------------------------
  utility
--------------------------------------------------------------------}

allM :: Monad m => (a -> m Bool) -> [a] -> m Bool
allM p = go
  where
    go [] = return True
    go (x:xs) = do
      b <- p x
      if b
        then go xs
        else return False

anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool
anyM p = go
  where
    go [] = return False
    go (x:xs) = do
      b <- p x
      if b
        then return True
        else go xs

#if !MIN_VERSION_base(4,6,0)

modifyIORef' :: IORef a -> (a -> a) -> IO ()
modifyIORef' ref f = do
  x <- readIORef ref
  writeIORef ref $! f x

#endif

shift :: IORef [a] -> IO a
shift ref = do
  (x:xs) <- readIORef ref
  writeIORef ref xs
  return x    

{--------------------------------------------------------------------
  debug
--------------------------------------------------------------------}

debugMode :: Bool
debugMode = False

checkSatisfied :: Solver -> IO ()
checkSatisfied solver = do
  cls <- readIORef (svClauseDB solver)
  forM_ cls $ \c -> do
    b <- isSatisfied solver c
    unless b $ do
      s <- showConstraint solver c
      log solver $ "BUG: " ++ s ++ " is violated"

sanityCheck :: Solver -> IO ()
sanityCheck _ | not debugMode = return ()
sanityCheck solver = do
  cls <- readIORef (svClauseDB solver)
  forM_ cls $ \constr -> do
    lits <- watchedLiterals solver constr
    forM_ lits $ \l -> do
      ws <- watches solver l
      unless (constr `elem` ws) $ error $ printf "sanityCheck:A:%s" (show lits)

  vs <- variables solver
  let lits = [l | v <- vs, l <- [literal v True, literal v False]]
  forM_ lits $ \l -> do
    cs <- watches solver l
    forM_ cs $ \constr -> do
      lits2 <- watchedLiterals solver constr
      unless (l `elem` lits2) $ do
        error $ printf "sanityCheck:C:%d %s" l (show lits2)

dumpVarActivity :: Solver -> IO ()
dumpVarActivity solver = do
  log solver "Variable activity:"
  vs <- variables solver
  forM_ vs $ \v -> do
    activity <- varActivity solver v
    log solver $ printf "activity(%d) = %d" v activity

dumpClaActivity :: Solver -> IO ()
dumpClaActivity solver = do
  log solver "Learnt clause activity:"
  xs <- learntConstraints solver
  forM_ xs $ \c -> do
    s <- showConstraint solver c
    aval <- constrActivity solver c
    log solver $ printf "activity(%s) = %f" s aval

-- | set callback function for receiving messages.
setLogger :: Solver -> (String -> IO ()) -> IO ()
setLogger solver logger = do
  writeIORef (svLogger solver) (Just logger)

log :: Solver -> String -> IO ()
log solver msg = logIO solver (return msg)

logIO :: Solver -> IO String -> IO ()
logIO solver action = do
  m <- readIORef (svLogger solver)
  case m of
    Nothing -> return ()
    Just logger -> action >>= logger