{-# OPTIONS_GHC -optc-DCTYPE_solver=minisat_solver* #-}
{-# OPTIONS_GHC -optc-DHTYPE_solver=Solver #-}
{-# OPTIONS_GHC -optc-DCTYPE_bool=minisat_bool #-}
{-# OPTIONS_GHC -optc-DHTYPE_bool=Bool #-}
{-# OPTIONS_GHC -optc-DCTYPE_lit=minisat_Lit #-}
{-# OPTIONS_GHC -optc-DHTYPE_lit=Lit #-}
{-# OPTIONS_GHC -optc-DCTYPE_int=int #-}
{-# OPTIONS_GHC -optc-DHTYPE_int=Int #-}
{-# OPTIONS_GHC -optc-DCTYPE_var=minisat_Var #-}
{-# OPTIONS_GHC -optc-DHTYPE_var=Var #-}
{-# OPTIONS_GHC -optc-DCTYPE_lbool=minisat_lbool #-}
{-# OPTIONS_GHC -optc-DHTYPE_lbool=LBool #-}
{-# LINE 1 "minisat-haskell-bindings/MiniSat.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "minisat-haskell-bindings/MiniSat.hsc" #-}
{-# LANGUAGE ScopedTypeVariables #-}

module MiniSat where

import Foreign.Ptr     ( Ptr, nullPtr )
import Foreign.C.Types ( CInt(..) )
import Control.Exception (bracket, finally, mask_, onException )
import Control.Concurrent.Async


{-# LINE 12 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 13 "minisat-haskell-bindings/MiniSat.hsc" #-}

-- | Run a minisat instance in such a way that it is
-- interruptable (by sending killThread).
-- cf. https://github.com/niklasso/minisat-haskell-bindings/issues/1
withNewSolverAsync :: (Solver -> IO a) -> IO a
withNewSolverAsync h = 
  bracket newSolver deleteSolver $ \  s -> do
    mask_ $ withAsync (h s) $ \ a -> do
      wait a `onException` minisat_interrupt s

withNewSolver :: (Solver -> IO a) -> IO a
withNewSolver h =
  do s <- newSolver
     h s `finally` deleteSolver s

newSolver :: IO Solver
newSolver =
  do s <- minisat_new
     eliminate s True -- make the default behave as a normal solver (avoiding common bugs)
     return s

deleteSolver :: Solver -> IO ()
deleteSolver = minisat_delete

newLit :: Solver -> IO Lit
newLit = minisat_newLit

neg :: Lit -> Lit
neg = minisat_negate

addClause :: Solver -> [Lit] -> IO Bool
addClause s xs =
  do minisat_addClause_begin s
     sequence_ [ minisat_addClause_addLit s x | x <- xs ]
     minisat_addClause_commit s

simplify :: Solver -> IO Bool
simplify = minisat_simplify

eliminate :: Solver -> Bool -> IO Bool
eliminate = minisat_eliminate

setFrozen :: Solver -> Var -> Bool -> IO ()
setFrozen = minisat_setFrozen

isEliminated :: Solver -> Var -> IO Bool
isEliminated = minisat_isEliminated

{-
solve :: Solver -> [Lit] -> Model a -> IO (Either [Lit] a)
-}

solve :: Solver -> [Lit] -> IO Bool
solve s xs =
  do minisat_solve_begin s
     sequence_ [ minisat_solve_addLit s x | x <- xs ]
     minisat_solve_commit s

limited_solve :: Solver -> [Lit] -> IO LBool
limited_solve s xs =
  do minisat_solve_begin s
     sequence_ [ minisat_solve_addLit s x | x <- xs ]
     minisat_limited_solve_commit s

value, modelValue :: Solver -> Lit -> IO (Maybe Bool)
(value,modelValue) = (get minisat_value_Lit, get minisat_modelValue_Lit)
 where
  get f s x = mbool `fmap` f s x

  mbool b 
    | b == l_False = Just False
    | b == l_True  = Just True
    | otherwise    = Nothing

conflict :: Solver -> IO [Lit]
conflict s =
  do n <- minisat_conflict_len s
     sequence [ minisat_conflict_nthLit s i | i <- [0..n-1] ]

-- TODO: Is it possible to FFI C constants, instead of using a dummy function?
l_True, l_False, l_Undef :: LBool
l_True  = minisat_get_l_True
l_False = minisat_get_l_False
l_Undef = minisat_get_l_Undef

----------------------------------------------------------------------------

newtype Solver = MkSolver (Ptr ())
newtype Var    = MkVar CInt  deriving ( Eq, Ord )
newtype Lit    = MkLit CInt  deriving ( Eq, Ord )
newtype LBool  = MkLBool CInt deriving ( Eq, Ord )

instance Show Var where
  show (MkVar n) = 'v' : show n

instance Show Lit where
  show x = (if minisat_sign x then "~" else "") ++ show (minisat_var x) 

instance Show LBool where
  show b
    | b == l_False = "False"
    | b == l_True  = "True"
    | otherwise    = "Undef"


{-# LINE 118 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 119 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 120 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 121 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 122 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 123 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 124 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 125 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 126 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 127 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 128 "minisat-haskell-bindings/MiniSat.hsc" #-}

{-# LINE 129 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall unsafe minisat_new :: IO (Solver)

{-# LINE 131 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_delete :: Solver -> IO (())

{-# LINE 132 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_newVar :: Solver -> IO (Var)

{-# LINE 133 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_newLit :: Solver -> IO (Lit)

{-# LINE 134 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_mkLit :: Var -> Lit

{-# LINE 135 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_mkLit_args :: Var -> Int -> Lit

{-# LINE 136 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_negate :: Lit -> Lit

{-# LINE 137 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_var :: Lit -> Var

{-# LINE 138 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_sign :: Lit -> Bool

{-# LINE 139 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_addClause :: Solver -> Int -> Ptr (Lit) -> IO (Bool)

{-# LINE 140 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_addClause_begin :: Solver -> IO (())

{-# LINE 141 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_addClause_addLit :: Solver -> Lit -> IO (())

{-# LINE 142 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_addClause_commit :: Solver -> IO (Bool)

{-# LINE 143 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_simplify :: Solver -> IO (Bool)

{-# LINE 144 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall safe minisat_solve :: Solver -> Int -> Ptr (Lit) -> IO (Bool)

{-# LINE 145 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_solve_begin :: Solver -> IO (())

{-# LINE 146 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_solve_addLit :: Solver -> Lit -> IO (())

{-# LINE 147 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall safe minisat_solve_commit :: Solver -> IO (Bool)

{-# LINE 148 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall safe minisat_limited_solve_commit :: Solver -> IO (LBool)

{-# LINE 149 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall safe minisat_interrupt :: Solver -> IO (())

{-# LINE 151 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall safe minisat_clearInterrupt :: Solver -> IO (())

{-# LINE 152 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall unsafe minisat_okay :: Solver -> IO (Bool)

{-# LINE 154 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_setPolarity :: Solver -> Var -> Int -> IO (())

{-# LINE 155 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_setDecisionVar :: Solver -> Var -> Int -> IO (())

{-# LINE 156 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_value_Var :: Solver -> Var -> IO (LBool)

{-# LINE 157 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_value_Lit :: Solver -> Lit -> IO (LBool)

{-# LINE 158 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_modelValue_Var :: Solver -> Var -> IO (LBool)

{-# LINE 159 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_modelValue_Lit :: Solver -> Lit -> IO (LBool)

{-# LINE 160 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall unsafe minisat_get_l_True :: LBool

{-# LINE 162 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_get_l_False :: LBool

{-# LINE 163 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_get_l_Undef :: LBool

{-# LINE 164 "minisat-haskell-bindings/MiniSat.hsc" #-}

-- // Simpsolver methods:
foreign import ccall unsafe minisat_setFrozen :: Solver -> Var -> Bool -> IO (())

{-# LINE 167 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_isEliminated :: Solver -> Var -> IO (Bool)

{-# LINE 168 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_eliminate :: Solver -> Bool -> IO (Bool)

{-# LINE 169 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall unsafe minisat_num_assigns :: Solver -> IO (Int)

{-# LINE 171 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_num_clauses :: Solver -> IO (Int)

{-# LINE 172 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_num_learnts :: Solver -> IO (Int)

{-# LINE 173 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_num_vars :: Solver -> IO (Int)

{-# LINE 174 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_num_freeVars :: Solver -> IO (Int)

{-# LINE 175 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_num_conflicts :: Solver -> IO (Int)

{-# LINE 176 "minisat-haskell-bindings/MiniSat.hsc" #-}

foreign import ccall unsafe minisat_conflict_len :: Solver -> IO (Int)

{-# LINE 178 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_conflict_nthLit :: Solver -> Int -> IO (Lit)

{-# LINE 179 "minisat-haskell-bindings/MiniSat.hsc" #-}
foreign import ccall unsafe minisat_set_verbosity :: Solver -> Int -> IO (())

{-# LINE 180 "minisat-haskell-bindings/MiniSat.hsc" #-}