packages feed

boolector-0.0.0.2: src/Boolector/Foreign.chs

{- |

   This module exports a subset of the low-level C Boolector API to Haskell
   code.  In general, you don't want to use this module and should use the
   'Boolector' module instead.

-}
{-# LANGUAGE StandaloneDeriving #-}
module Boolector.Foreign (
   -- ** Boolector state, options and configurations
    Btor
  , new
  , Option(..)
  , setOpt
  , getOpt
  , setSatSolver
  -- * SAT/SMT queries
  , Node
  , sat
  , limitedSat
  , simplify
  , Status(..)
  -- ** Assert and assume
  , assert
  , assume
  , failed
  , fixateAssumptions
  , resetAssumptions
  , push
  , pop
  -- ** Variables and constants
  , var
  , const
  , constd
  , consth
  -- *** Booleans
  , true
  , false
  -- *** Bit-vectors
  , zero
  , one
  , ones
  , unsignedInt
  , int
  -- *** Arrays
  , array
  -- *** Functions
  , fun
  , uf
  -- **** Parameters
  , param
  -- *** Quantified terms
  , forall
  , exists
  -- ** Operations
  -- *** Implications and conditionals
  , implies
  , iff
  , cond
  -- *** Equality checking
  , eq
  , ne
  -- *** Bit flipping, extraction, extension, and reduction
  , not
  , neg
  , redor
  , redxor
  , redand
  , slice
  , uext
  , sext
  , concat
  , repeat
  -- *** Bit-wise operations
  , xor
  , xnor
  , and
  , nand
  , or
  , nor
  , sll
  , srl
  , sra
  , rol
  , ror
  -- *** Arithmetic operations
  , add
  , uaddo
  , saddo
  , inc
  , sub
  , usubo
  , ssubo
  , dec
  , mul
  , umulo
  , smulo
  , udiv
  , sdiv
  , sdivo
  , urem
  , srem
  , smod
  -- *** Comparison operations
  , ult
  , slt
  , ulte
  , slte
  , ugt
  , sgt
  , ugte
  , sgte
  -- *** Array operations
  , read
  , write
  -- *** Function operations
  , apply
  -- ** Accessors
  , getSort
  , funGetDomainSort
  , funGetCodomainSort
  , getFunArity
  , getSymbol
  , setSymbol
  , getWidth
  , getIndexWidth
  , isConst
  , isVar
  , isArray
  , isArrayVar
  , isParam
  , isBoundParam
  , isUf
  , isFun
  -- ** Models
  , bvAssignment
  -- ** Sorts
  , Sort
  , boolSort
  , bitvecSort
  , funSort
  , arraySort
  -- *** Accessors
  , isEqualSort
  , isArraySort
  , isBitvecSort
  , isFunSort
  , funSortCheck
  -- * Debug dumping
  , dumpBtorNode
  , dumpSmt2Node
  , dumpBtor
  , dumpSmt2
  -- * Helpers
  , fopen
  , setTerm
  ) where

import Prelude hiding (read, not, and, or, const, concat, repeat)

import Foreign hiding (xor, new)
import Foreign.C

import Control.Monad

{#context lib = "boolector" prefix = "boolector_" #}

#include "boolector.h"


--
-- Types
--

-- | Status.
{# enum define Status {
  BOOLECTOR_UNKNOWN as Unknown,
  BOOLECTOR_SAT as Sat,
  BOOLECTOR_UNSAT as Unsat
  } deriving (Eq, Ord, Show ) #}

-- | Boolector instances.
{#pointer *Btor as Btor foreign newtype #}
deriving instance Eq Btor
deriving instance Ord Btor

-- | AST node.
{#pointer *BoolectorNode as Node foreign newtype #}
deriving instance Eq Node
deriving instance Ord Node

-- | Sort.
{#pointer *BoolectorAnonymous as Sort foreign newtype #}
deriving instance Eq Sort
deriving instance Ord Sort

-- | Solver option.
-- See <https://github.com/Boolector/boolector/blob/47f94b39fb6e099195da043ddaf8d82e4b2aebc9/src/btortypes.h#L37>
{# enum define Option {
BTOR_OPT_MODEL_GEN                     as OPT_MODEL_GEN,
BTOR_OPT_INCREMENTAL                   as OPT_INCREMENTAL,
BTOR_OPT_INCREMENTAL_SMT1              as OPT_INCREMENTAL_SMT1,
BTOR_OPT_INPUT_FORMAT                  as OPT_INPUT_FORMAT,
BTOR_OPT_OUTPUT_NUMBER_FORMAT          as OPT_OUTPUT_NUMBER_FORMAT,
BTOR_OPT_OUTPUT_FORMAT                 as OPT_OUTPUT_FORMAT,
BTOR_OPT_ENGINE                        as OPT_ENGINE,
BTOR_OPT_SAT_ENGINE                    as OPT_SAT_ENGINE,
BTOR_OPT_AUTO_CLEANUP                  as OPT_AUTO_CLEANUP,
BTOR_OPT_PRETTY_PRINT                  as OPT_PRETTY_PRINT,
BTOR_OPT_EXIT_CODES                    as OPT_EXIT_CODES,
BTOR_OPT_SEED                          as OPT_SEED,
BTOR_OPT_VERBOSITY                     as OPT_VERBOSITY,
BTOR_OPT_LOGLEVEL                      as OPT_LOGLEVEL,
BTOR_OPT_REWRITE_LEVEL                 as OPT_REWRITE_LEVEL,
BTOR_OPT_SKELETON_PREPROC              as OPT_SKELETON_PREPROC,
BTOR_OPT_ACKERMANN                     as OPT_ACKERMANN,
BTOR_OPT_BETA_REDUCE_ALL               as OPT_BETA_REDUCE_ALL,
BTOR_OPT_ELIMINATE_SLICES              as OPT_ELIMINATE_SLICES,
BTOR_OPT_VAR_SUBST                     as OPT_VAR_SUBST,
BTOR_OPT_UCOPT                         as OPT_UCOPT,
BTOR_OPT_MERGE_LAMBDAS                 as OPT_MERGE_LAMBDAS,
BTOR_OPT_EXTRACT_LAMBDAS               as OPT_EXTRACT_LAMBDAS,
BTOR_OPT_NORMALIZE                     as OPT_NORMALIZE,
BTOR_OPT_NORMALIZE_ADD                 as OPT_NORMALIZE_ADD,
BTOR_OPT_FUN_PREPROP                   as OPT_FUN_PREPROP,
BTOR_OPT_FUN_PRESLS                    as OPT_FUN_PRESLS,
BTOR_OPT_FUN_DUAL_PROP                 as OPT_FUN_DUAL_PROP,
BTOR_OPT_FUN_DUAL_PROP_QSORT           as OPT_FUN_DUAL_PROP_QSORT,
BTOR_OPT_FUN_JUST                      as OPT_FUN_JUST,
BTOR_OPT_FUN_JUST_HEURISTIC            as OPT_FUN_JUST_HEURISTIC,
BTOR_OPT_FUN_LAZY_SYNTHESIZE           as OPT_FUN_LAZY_SYNTHESIZE,
BTOR_OPT_FUN_EAGER_LEMMAS              as OPT_FUN_EAGER_LEMMAS,
BTOR_OPT_FUN_STORE_LAMBDAS             as OPT_FUN_STORE_LAMBDAS,
BTOR_OPT_SLS_NFLIPS                    as OPT_SLS_NFLIPS,
BTOR_OPT_SLS_STRATEGY                  as OPT_SLS_STRATEGY,
BTOR_OPT_SLS_JUST                      as OPT_SLS_JUST,
BTOR_OPT_SLS_MOVE_GW                   as OPT_SLS_MOVE_GW,
BTOR_OPT_SLS_MOVE_RANGE                as OPT_SLS_MOVE_RANGE,
BTOR_OPT_SLS_MOVE_SEGMENT              as OPT_SLS_MOVE_SEGMENT,
BTOR_OPT_SLS_MOVE_RAND_WALK            as OPT_SLS_MOVE_RAND_WALK,
BTOR_OPT_SLS_PROB_MOVE_RAND_WALK       as OPT_SLS_PROB_MOVE_RAND_WALK,
BTOR_OPT_SLS_MOVE_RAND_ALL             as OPT_SLS_MOVE_RAND_ALL,
BTOR_OPT_SLS_MOVE_RAND_RANGE           as OPT_SLS_MOVE_RAND_RANGE,
BTOR_OPT_SLS_MOVE_PROP                 as OPT_SLS_MOVE_PROP,
BTOR_OPT_SLS_MOVE_PROP_N_PROP          as OPT_SLS_MOVE_PROP_N_PROP,
BTOR_OPT_SLS_MOVE_PROP_N_SLS           as OPT_SLS_MOVE_PROP_N_SLS,
BTOR_OPT_SLS_MOVE_PROP_FORCE_RW        as OPT_SLS_MOVE_PROP_FORCE_RW,
BTOR_OPT_SLS_MOVE_INC_MOVE_TEST        as OPT_SLS_MOVE_INC_MOVE_TEST,
BTOR_OPT_SLS_USE_RESTARTS              as OPT_SLS_USE_RESTARTS,
BTOR_OPT_SLS_USE_BANDIT                as OPT_SLS_USE_BANDIT,
BTOR_OPT_PROP_NPROPS                   as OPT_PROP_NPROPS,
BTOR_OPT_PROP_USE_RESTARTS             as OPT_PROP_USE_RESTARTS,
BTOR_OPT_PROP_USE_BANDIT               as OPT_PROP_USE_BANDIT,
BTOR_OPT_PROP_PATH_SEL                 as OPT_PROP_PATH_SEL,
BTOR_OPT_PROP_PROB_USE_INV_VALUE       as OPT_PROP_PROB_USE_INV_VALUE,
BTOR_OPT_PROP_PROB_FLIP_COND           as OPT_PROP_PROB_FLIP_COND,
BTOR_OPT_PROP_PROB_FLIP_COND_CONST     as OPT_PROP_PROB_FLIP_COND_CONST,
BTOR_OPT_PROP_FLIP_COND_CONST_DELTA    as OPT_PROP_FLIP_COND_CONST_DELTA,
BTOR_OPT_PROP_FLIP_COND_CONST_NPATHSEL as OPT_PROP_FLIP_COND_CONST_NPATHSEL,
BTOR_OPT_PROP_PROB_SLICE_KEEP_DC       as OPT_PROP_PROB_SLICE_KEEP_DC,
BTOR_OPT_PROP_PROB_CONC_FLIP           as OPT_PROP_PROB_CONC_FLIP,
BTOR_OPT_PROP_PROB_SLICE_FLIP          as OPT_PROP_PROB_SLICE_FLIP,
BTOR_OPT_PROP_PROB_EQ_FLIP             as OPT_PROP_PROB_EQ_FLIP,
BTOR_OPT_PROP_PROB_AND_FLIP            as OPT_PROP_PROB_AND_FLIP,
BTOR_OPT_PROP_NO_MOVE_ON_CONFLICT      as OPT_PROP_NO_MOVE_ON_CONFLICT,
BTOR_OPT_AIGPROP_USE_RESTARTS          as OPT_AIGPROP_USE_RESTARTS,
BTOR_OPT_AIGPROP_USE_BANDIT            as OPT_AIGPROP_USE_BANDIT,
BTOR_OPT_QUANT_SYNTH                   as OPT_QUANT_SYNTH,
BTOR_OPT_QUANT_DUAL_SOLVER             as OPT_QUANT_DUAL_SOLVER,
BTOR_OPT_QUANT_SYNTH_LIMIT             as OPT_QUANT_SYNTH_LIMIT,
BTOR_OPT_QUANT_SYNTH_ITE_COMPLETE      as OPT_QUANT_SYNTH_ITE_COMPLETE,
BTOR_OPT_QUANT_FIXSYNTH                as OPT_QUANT_FIXSYNTH,
BTOR_OPT_QUANT_SYNTH_QI                as OPT_QUANT_SYNTH_QI,
BTOR_OPT_QUANT_DER                     as OPT_QUANT_DER,
BTOR_OPT_QUANT_CER                     as OPT_QUANT_CER,
BTOR_OPT_QUANT_MINISCOPE               as OPT_QUANT_MINISCOPE,
BTOR_OPT_DEFAULT_TO_CADICAL            as OPT_DEFAULT_TO_CADICAL,
BTOR_OPT_SORT_EXP                      as OPT_SORT_EXP,
BTOR_OPT_SORT_AIG                      as OPT_SORT_AIG,
BTOR_OPT_SORT_AIGVEC                   as OPT_SORT_AIGVEC,
BTOR_OPT_AUTO_CLEANUP_INTERNAL         as OPT_AUTO_CLEANUP_INTERNAL,
BTOR_OPT_SIMPLIFY_CONSTRAINTS          as OPT_SIMPLIFY_CONSTRAINTS,
BTOR_OPT_CHK_FAILED_ASSUMPTIONS        as OPT_CHK_FAILED_ASSUMPTIONS,
BTOR_OPT_CHK_MODEL                     as OPT_CHK_MODEL,
BTOR_OPT_CHK_UNCONSTRAINED             as OPT_CHK_UNCONSTRAINED,
BTOR_OPT_PARSE_INTERACTIVE             as OPT_PARSE_INTERACTIVE,
BTOR_OPT_SAT_ENGINE_LGL_FORK           as OPT_SAT_ENGINE_LGL_FORK,
BTOR_OPT_INCREMENTAL_RW                as OPT_INCREMENTAL_RW,
BTOR_OPT_DECLSORT_BV_WIDTH             as OPT_DECLSORT_BV_WIDTH,
BTOR_OPT_NUM_OPTS                      as OPT_NUM_OPTS
} deriving (Eq, Ord, Show ) #}


--
--  Solver-level interface
--

-- | Create a new instance of Boolector.
new :: IO (Btor)
new = do
 ptrBtor <- new'_ 
 -- run delete on the btor at the end
 foreignPtrBtor <- newForeignPtr boolector_delete ptrBtor
 -- run release_all before delete
 addForeignPtrFinalizer boolector_release_all foreignPtrBtor 
 return $ Btor foreignPtrBtor

foreign import ccall "boolector_new"
  new'_ :: IO (Ptr Btor)

foreign import ccall "&boolector_delete"
  boolector_delete :: FinalizerPtr Btor

foreign import ccall "&boolector_release_all"
  boolector_release_all :: FinalizerPtr Btor

-- | Push new context levels.
{#fun push as ^ { `Btor', `CUInt' } -> `()' #}

-- | Pop context levels.
{#fun pop as ^ { `Btor', `CUInt' } -> `()' #}

-- | Set a termination callback.
setTerm :: Btor -> (Ptr () -> IO Int) -> IO ()
setTerm b callback = do
  cb <- makeWrapper callback
  withBtor b $ \ b' -> setTerm'_ b' cb nullPtr

foreign import ccall "wrapper"
  makeWrapper :: (Ptr () -> IO Int) -> IO (FunPtr (Ptr () -> IO Int))

foreign import ccall "boolector_set_term"
  setTerm'_ :: Ptr Btor -> (FunPtr (Ptr () -> IO Int)) -> Ptr () -> IO ()

--
-- Options
--

-- | Set the SAT solver to use.
--
-- Currently supported: @Lingeling@, @PicoSAT@, and @MiniSAT@.
-- Returns non-zero value if setting the SAT solver was successful.
{#fun set_sat_solver as ^ { `Btor', `String' } -> `()' #}

-- | Set option. See btortypes.h
{#fun set_opt as ^ { `Btor', `Option', `CUInt' } -> `()' #}

-- | Get the current value of an option.
{#fun get_opt as ^ { `Btor', `Option' } -> `CUInt' #}

--
-- Solving
--

-- | Add a constraint.
{#fun assert as ^ { `Btor' , `Node' } -> `()' #}

-- | Add an assumption.
{#fun assume as ^ { `Btor' , `Node' } -> `()' #}

-- | Determine if assumption @node@ is a failed assumption.
--
-- Failed assumptions are those assumptions, that force an input formula
-- to become unsatisfiable.
{#fun failed as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Add all assumptions as assertions.
{#fun fixate_assumptions as ^ { `Btor' } -> `()' #}

-- | Resets all added assumptions.
{#fun reset_assumptions as ^ { `Btor' } -> `()' #}

-- | Solve an input formula.
--
-- An input formula is defined by constraints added via 'assert'.
-- You can guide the search for a solution to an input formula by making
-- assumptions via 'assume'.
{#fun sat as ^ { `Btor' } -> `Status' #}

-- | Solve an input formula and limit the search by the number of lemmas
-- generated and the number of conflicts encountered by the underlying
-- SAT solver.
--
-- An input formula is defined by constraints added via 'assert'.
-- You can guide the search for a solution to an input formula by making
-- assumptions via 'assume'.
--
-- Returns 'Sat' if the input formula is satisfiable (under possibly given
-- assumptions), 'Usat' if the instance is unsatisfiable, and 'Unknown' if the
-- instance could not be solved within given limits.
{#fun limited_sat as ^ { `Btor' , `Int', `Int' } -> `Status' #}

-- | Simplify current input formula.
{#fun simplify as ^ { `Btor' } -> `Status' #}

--
-- Expressions
--

-- | Create bit vector constant representing the bit vector @bits@.
{#fun const as ^ { `Btor' , `String' } -> `Node' #}

-- | Create bit vector constant representing the decimal number @str@.
{#fun constd as ^ { `Btor' , `Sort', `String' } -> `Node' #}

-- | Create bit vector constant representing the hexadecimal number @str@.
{#fun consth as ^ { `Btor' , `Sort', `String' } -> `Node' #}

-- | Create constant true. This is represented by the bit vector constant one
-- with bit width one.
{#fun true as ^ { `Btor'  } -> `Node' #}

-- | Create bit vector constant zero with bit width one.
{#fun false as ^ { `Btor' } -> `Node' #}

-- | Create bit vector constant zero of sort @sort@.
{#fun zero as ^ { `Btor', `Sort' } -> `Node' #}

-- | Create bit vector constant one of sort @sort@.
{#fun one as ^ { `Btor', `Sort' } -> `Node' #}

-- | Create bit vector constant of sort @sort@, where each bit is set to one.
{#fun ones as ^ { `Btor', `Sort' } -> `Node' #}

-- |  Create bit vector constant representing the unsigned integer @u@ of
-- sort @sort@.
--
-- The constant is obtained by either truncating bits or by
-- unsigned extension (padding with zeroes).
{#fun unsigned_int as ^ { `Btor', `CUInt', `Sort' } -> `Node' #}

-- | Create bit vector constant representing the signed integer @i@ of sort
-- @sort@.
--
-- The constant is obtained by either truncating bits or by
-- signed extension (padding with ones).
{#fun int as ^ { `Btor', `CInt', `Sort' } -> `Node' #}

-- | Create a bit vector variable of sort @sort@.
--
-- The name must be unique.
{#fun var as ^ { `Btor' , `Sort', `String' } -> `Node' #}

-- | Create the one's complement of bit vector @node@.
{#fun not as ^ { `Btor' , `Node'} -> `Node' #}

-- | Create the two's complement of bit vector @node@.
{#fun neg as ^ { `Btor' , `Node'} -> `Node' #}

-- | Create *or* reduction of node @node@.
--
-- All bits of node @node@ are combined by a Boolean *or*.
{#fun redor as ^ { `Btor' , `Node'} -> `Node' #}

-- | Create *xor* reduction of node @node@.
--
-- All bits of @node@ are combined by a Boolean *xor*.
{#fun redxor as ^ { `Btor' , `Node'} -> `Node' #}

-- | Create *and* reduction of node @node@.
--
-- All bits of @node@ are combined by a Boolean *and*.
{#fun redand as ^ { `Btor' , `Node'} -> `Node' #}

-- | Create a bit vector slice of @node@ from index @upper@ to index @lower@.
{#fun slice as ^ { `Btor' , `Node', `CUInt', `CUInt'} -> `Node' #}

-- | Create unsigned extension.
--
-- The bit vector @node@ is padded with @width@ * zeroes.
{#fun uext as ^ { `Btor' , `Node', `CUInt'} -> `Node' #}

-- | Create signed extension.
--
-- The bit vector @node@ is padded with @width@ bits where the value
-- depends on the value of the most significant bit of node @n@.
{#fun sext as ^ { `Btor' , `Node', `CUInt'} -> `Node' #}

-- | Create the concatenation of two bit vectors.
{#fun concat as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create @n@ concatenations of a given node @node@.
{#fun repeat as ^ { `Btor' , `Node', `CUInt'} -> `Node' #}

--
-- Implications.
--

-- | Create boolean implication.
{#fun implies as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create Boolean equivalence.
{#fun iff as ^ { `Btor' , `Node', `Node'} -> `Node' #}

--
-- Equality.
--

-- | Create bit vector or array equality.
--
-- Both operands are either bit vectors with the same bit width or arrays
-- of the same type.
{#fun eq as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create bit vector or array inequality.
--
-- Both operands are either bit vectors with the same bit width or arrays
-- of the same type.
{#fun ne as ^ { `Btor' , `Node', `Node'} -> `Node' #}

--
-- Conditionals.
--

-- | Create an if-then-else.
--
-- If condition @n_cond@ is true, then @n_then@ is returned, else @n_else@
-- is returned.
-- Nodes @n_then@ and @n_else@ must be either both arrays or both bit vectors.
{#fun cond as ^ { `Btor' , `Node', `Node', `Node'} -> `Node' #}

--
-- Bit-wise operations.
--

-- | Create a bit vector *xor*.
{#fun xor as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector *xnor*.
{#fun xnor as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector *and*.
{#fun and as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector *nand*.
{#fun nand as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector *or*.
{#fun or as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector *nor*.
{#fun nor as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a logical shift left.
--
-- Given node @n1@, the value it represents is the number of zeroes shifted
-- into node @n0@ from the right.
{#fun sll as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a logical shift right.
--
-- Given node @n1@, the value it represents is the number of zeroes shifted
-- into node @n0@ from the left.
{#fun srl as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an arithmetic shift right.
--
-- Analogously to 'srl', but whether zeroes or ones are shifted in depends on
-- the most significant bit of @n0@.
{#fun sra as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a rotate left.
--
-- Given bit vector node @n1@, the value it represents is the number of bits
-- by which node @n0@ is rotated to the left.
{#fun rol as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a rotate right.
--
-- Given bit vector node @n1@, the value it represents is the number of bits by
-- which node @n0@ is rotated to the right.
{#fun ror as ^ { `Btor' , `Node', `Node'} -> `Node' #}

--
-- Arithmetic operations.
--

-- | Create bit vector addition.
{#fun add as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create bit vector expression that increments bit vector @node@ by one.
{#fun inc as ^ { `Btor' , `Node' } -> `Node' #}

-- | Create an unsigned bit vector addition overflow detection.
{#fun uaddo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed bit vector addition overflow detection.
{#fun saddo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a bit vector subtraction.
{#fun sub as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned bit vector subtraction overflow detection.
{#fun usubo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed bit vector subtraction overflow detection.
{#fun ssubo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create bit vector expression that decrements bit vector @node@ by one.
{#fun dec as ^ { `Btor' , `Node' } -> `Node' #}

-- | Create a bitvector multiplication.
{#fun mul as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned bit vector multiplication overflow detection.
{#fun umulo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create signed multiplication overflow detection.
{#fun smulo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create unsigned division.
{#fun udiv as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create signed division.
{#fun sdiv as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed bit vector division overflow detection.
{#fun sdivo as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned remainder.
{#fun urem as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed remainder.
{#fun srem as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a, signed remainder where its sign matches the sign of the divisor.
{#fun smod as ^ { `Btor' , `Node', `Node'} -> `Node' #}

--
-- Comparison operations.
--

-- | Create an unsigned less than.
{#fun ult as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed less than.
{#fun slt as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned less than or equal.
{#fun ulte as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed less than or equal.
{#fun slte as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned greater than.
{#fun ugt as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed greater than.
{#fun sgt as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create an unsigned greater than or equal.
{#fun ugte as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a signed greater than or equal.
{#fun sgte as ^ { `Btor' , `Node', `Node'} -> `Node' #}

--
-- Array operations
--

-- | Create a one-dimensional bit vector array with sort @sort@.
--
-- The name must be unique.
{#fun array as ^ { `Btor' , `Sort', `String' } -> `Node' #}

-- | Create a read on array @n_array@ at position @n_index@.
{#fun read as ^ { `Btor' , `Node', `Node'} -> `Node' #}

-- | Create a write on array @n_array@ at position @n_index@ with value
-- @n_value@.
--
-- The array is updated at exactly one position, all other elements remain
-- unchanged. The bit width of @n_index@ must be the same as the bit width of
-- the indices of @n_array@. The bit width of @n_value@ must be the same as
-- the bit width of the elements of @n_array@.
{#fun write as ^ { `Btor' , `Node', `Node', `Node'} -> `Node' #}

--
-- Functions
--

-- | Create an uninterpreted function with sort @sort@.
--
-- The name must be unique.
{#fun uf as ^ { `Btor' , `Sort', `String' } -> `Node' #}


-- | Create function parameter of sort @sort@.
--
-- This kind of node is used to create parameterized expressions, which are
-- used to create functions. Once a parameter is bound to a function, it
-- cannot be re-used in other functions.
{#fun param as ^ { `Btor' , `Sort', `String'} -> `Node' #}

-- | Create a function with body @node@ parameterized over parameters
-- @param_nodes@.
--
-- This kind of node is similar to macros in the SMT-LIB standard 2.0.
-- Note that as soon as a parameter is bound to a function, it can not be
-- reused in other functions.
-- Call a function via 'apply'.
fun :: Btor -> [Node] -> Node -> IO Node
fun hbtor hargs hret = withBtor hbtor $ \cbotr ->
  withNodes hargs $ \cargs ->
    withArrayLen cargs $ \len cargsPtr ->
      withNode hret $ \cret -> do
        cptr <- fun'_ cbotr cargsPtr (fromIntegral len) cret
        Node `liftM` newForeignPtr_ cptr

foreign import ccall "boolector_fun"
  fun'_ :: Ptr Btor -> Ptr (Ptr Node) -> CUInt -> Ptr Node -> IO (Ptr Node)

-- | Create a function application on function @n_fun@ with arguments
-- @arg_nodes@.
apply :: Btor -> [Node] -> Node -> IO Node
apply hbtor hargs hfun = withBtor hbtor $ \cbotr ->
  withNodes hargs $ \cargs ->
    withArrayLen cargs $ \len cargsPtr ->
      withNode hfun $ \cfun -> do
        cptr <- apply'_ cbotr cargsPtr (fromIntegral len) cfun
        Node `liftM` newForeignPtr_ cptr

foreign import ccall "boolector_apply"
  apply'_ :: Ptr Btor -> Ptr (Ptr Node) -> CUInt -> Ptr Node -> IO (Ptr Node)

-- | Create a universally quantified term.
forall :: Btor -> [Node] -> Node -> IO Node
forall hbtor hparams hbody = withBtor hbtor $ \cbotr ->
  withNodes hparams $ \cparams ->
    withArrayLen cparams $ \len cparamsPtr ->
      withNode hbody $ \cbody -> do
        cptr <- forall'_ cbotr cparamsPtr (fromIntegral len) cbody
        Node `liftM` newForeignPtr_ cptr

foreign import ccall "boolector_forall"
  forall'_ :: Ptr Btor -> Ptr (Ptr Node) -> CUInt -> Ptr Node -> IO (Ptr Node)

-- | Create an existentially quantifed term.
exists :: Btor -> [Node] -> Node -> IO Node
exists hbtor hparams hbody = withBtor hbtor $ \cbotr ->
  withNodes hparams $ \cparams ->
    withArrayLen cparams $ \len cparamsPtr ->
      withNode hbody $ \cbody -> do
        cptr <- exists'_ cbotr cparamsPtr (fromIntegral len) cbody
        Node `liftM` newForeignPtr_ cptr

foreign import ccall "boolector_exists"
  exists'_ :: Ptr Btor -> Ptr (Ptr Node) -> CUInt -> Ptr Node -> IO (Ptr Node)

-- | Helper function for executing list of Nodes.
withNodes :: [Node] -> ([Ptr Node] -> IO a) -> IO a
withNodes [] f = f []
withNodes (hx:hxs) f = withNode hx $ \cx -> withNodes hxs $ \cxs -> f (cx:cxs)


--
-- Accessors
--

-- | Get the sort of given node.
{#fun get_sort as ^ { `Btor' , `Node' } -> `Sort' #}

-- | Get the domain sort of given function node node.
{#fun fun_get_domain_sort as ^ { `Btor' , `Node' } -> `Sort' #}

-- | Get the codomain sort of given function node node.
{#fun fun_get_codomain_sort as ^ { `Btor' , `Node' } -> `Sort' #}

-- | Get the arity of function node.
{#fun get_fun_arity as ^ { `Btor' , `Node' } -> `CUInt' #}

-- | Get the symbol of an expression.
foreign import ccall safe "Boolector/Foreign.chs.h boolector_get_symbol"
  getSymbol'_ :: Ptr Btor -> Ptr Node -> IO CString

-- | Get the symbol of an expression.
getSymbol :: Btor -> Node -> IO (Maybe String)
getSymbol hbtor hnode = withBtor hbtor $ \cbtor -> 
  withNode hnode $ \cnode -> do
    cstrPtr <- getSymbol'_ cbtor cnode
    if cstrPtr == nullPtr 
      then return Nothing
      else Just `liftM` peekCString cstrPtr

-- | Set the symbol of an expression.
{#fun set_symbol as ^ { `Btor' , `Node', `String' } -> `()' #}

-- | Get the bit width of an expression.
--
-- If the expression is an array, it returns the bit width of the array
-- elements.
-- If the expression is a function, it returns the bit width of the function's
-- return value.
{#fun get_width as ^ { `Btor' , `Node' } -> `CUInt' #}

-- | Get the bit width of indices of @n_array@.
{#fun get_index_width as ^ { `Btor' , `Node' } -> `CUInt' #}

-- | Determine if given node is a constant node.
{#fun is_const as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is a bit vector variable.
{#fun is_var as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is an array node.
{#fun is_array as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is an array node.
{#fun is_array_var as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is a parameter node.
{#fun is_param as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given parameter node is bound by a function.
{#fun is_bound_param as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is an uninterpreted function node.
{#fun is_uf as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Determine if given node is a function node.
{#fun is_fun as ^ { `Btor' , `Node' } -> `Bool' #}

-- | Check if sorts of given arguments matches the function signature.
-- Returns 'Nothing' if all sorts are correct; otherwise it returns the
-- position of the incorrect argument.
funSortCheck :: Btor -> [Node] -> Node -> IO (Maybe Int)
funSortCheck hbtor hparams hfun = withBtor hbtor $ \cbotr ->
  withNodes hparams $ \cparams ->
    withArrayLen cparams $ \len cparamsPtr ->
      withNode hfun $ \cfun -> do
       rt <- funSortCheck'_ cbotr cparamsPtr (fromIntegral len) cfun
       return $ if rt == -1
                  then Nothing
                  else Just $ fromIntegral rt

foreign import ccall "boolector_fun_sort_check"
  funSortCheck'_ :: Ptr Btor -> Ptr (Ptr Node) -> CUInt -> Ptr Node -> IO CInt

--
-- Models.
--

-- | Generate an assignment string for bit vector expression if
-- 'sat' has returned 'Sat' and model generation has been enabled.
--
-- The expression can be an arbitrary bit vector expression which
-- occurs in an assertion or current assumption. The assignment string has to
-- be freed by 'free_bv_assignment'.
--
-- TODO: we should change this function to return a ModelString and use
-- free_bv_assignment to actually free the assignments. We're very leaky right
--now.
{#fun bv_assignment as ^ { `Btor' , `Node' } -> `String' #}


--
-- Sorts.
--

-- | Create Boolean sort.
{#fun bool_sort as ^ { `Btor'} -> `Sort' #}

-- | Create bit vector sort of bit width @width@.
{#fun bitvec_sort as ^ { `Btor' , `CUInt' } -> `Sort' #}

-- | Create function sort.
funSort :: Btor -> [Sort] -> Sort -> IO Sort
funSort hbtor hargs hret = withBtor hbtor $ \cbotr ->
  withSorts hargs $ \cargs ->
    withArrayLen cargs $ \len cargsPtr ->
      withSort hret $ \cret -> do
        cptr <- funSort'_ cbotr cargsPtr (fromIntegral len) cret
        Sort `liftM` newForeignPtr_ cptr

foreign import ccall "boolector_fun_sort"
  funSort'_ :: Ptr Btor -> Ptr (Ptr Sort) -> CUInt -> Ptr Sort -> IO (Ptr Sort)

-- | Helper function for executing list of Sorts.
withSorts :: [Sort] -> ([Ptr Sort] -> IO a) -> IO a
withSorts [] f = f []
withSorts (hx:hxs) f = withSort hx $ \cx -> withSorts hxs $ \cxs -> f (cx:cxs)

-- | Create array sort.
{#fun array_sort as ^ { `Btor' , `Sort', `Sort' } -> `Sort' #}

-- | Determine if @n0@ and @n1@ have the same sort or not.
{#fun is_equal_sort as ^ { `Btor' , `Node', `Node' } -> `Bool' #}

-- | Determine if @sort@ is an array sort.
{#fun is_array_sort as ^ { `Btor' , `Sort' } -> `Bool' #}

-- | Determine if @sort@ is a bit-vector sort.
{#fun is_bitvec_sort as ^ { `Btor' , `Sort' } -> `Bool' #}

-- | Determine if @sort@ is a function sort.
{#fun is_fun_sort as ^ { `Btor' , `Sort' } -> `Bool' #}

--
-- Dumping
--

-- | POSIX files
{#pointer *FILE as File foreign finalizer fclose newtype#}

-- | Expose POSIX file open.
{#fun fopen as ^ {`String', `String'} -> `File' #}

-- | Recursively dump @node@ to file in BTOR_ format.
{#fun dump_btor_node as ^ { `Btor' , `File', `Node' } -> `()' #}

-- | Dump formula to file in BTOR_ format.
{#fun dump_btor as ^ { `Btor' , `File' } -> `()' #}

-- | Recursively dump @node@ to file in SMT-LIB v2 format.
{#fun dump_smt2_node as ^ { `Btor' , `File', `Node' } -> `()' #}

-- | Dumps formula to file in SMT-LIB v2 format.
{#fun dump_smt2 as ^ { `Btor' , `File' } -> `()' #}