module Data.SBV.SMT.SMTLib2(cvt, addNonEqConstraints) where
import Data.Bits (bit)
import qualified Data.Foldable as F (toList)
import qualified Data.Map as M
import qualified Data.IntMap as IM
import Data.List (intercalate, partition)
import Numeric (showHex)
import Data.SBV.BitVectors.Data
addNonEqConstraints :: [(Quantifier, NamedSymVar)] -> [[(String, CW)]] -> SMTLibPgm -> Maybe String
addNonEqConstraints qinps allNonEqConstraints (SMTLibPgm _ (aliasTable, pre, post))
| null allNonEqConstraints
= Just $ intercalate "\n" $ pre ++ post
| null refutedModel
= Nothing
| True
= Just $ intercalate "\n" $ pre
++ [ "; --- refuted-models ---" ]
++ concatMap nonEqs (map (map intName) nonEqConstraints)
++ post
where refutedModel = concatMap nonEqs (map (map intName) nonEqConstraints)
intName (s, c)
| Just sw <- s `lookup` aliasTable = (show sw, c)
| True = (s, c)
nonEqConstraints = filter (not . null) $ map (filter (\(s, _) -> s `elem` topUnivs)) allNonEqConstraints
topUnivs = [s | (_, (_, s)) <- takeWhile (\p -> fst p == EX) qinps]
nonEqs :: [(String, CW)] -> [String]
nonEqs [] = []
nonEqs [sc] = ["(assert " ++ nonEq sc ++ ")"]
nonEqs (sc:r) = ["(assert (or " ++ nonEq sc]
++ map ((" " ++) . nonEq) r
++ [" ))"]
nonEq :: (String, CW) -> String
nonEq (s, c) = "(not (= " ++ s ++ " " ++ cvtCW c ++ "))"
tbd :: String -> a
tbd e = error $ "SBV.SMTLib2: Not-yet-supported: " ++ e
cvt :: Bool
-> Bool
-> [String]
-> [(Quantifier, NamedSymVar)]
-> [Either SW (SW, [SW])]
-> [(SW, CW)]
-> [((Int, (Bool, Size), (Bool, Size)), [SW])]
-> [(Int, ArrayInfo)]
-> [(String, SBVType)]
-> [(String, [String])]
-> Pgm
-> [SW]
-> SW
-> ([String], [String])
cvt hasInf isSat comments _inps skolemInps consts tbls arrs uis axs asgnsSeq cstrs out = (pre, [])
where
logic
| hasInf = ["; Has unbounded Integers; no logic specified."]
| True = ["(set-logic " ++ qs ++ as ++ ufs ++ "BV)"]
where qs | null foralls && null axs = "QF_"
| True = ""
as | null arrs = ""
| True = "A"
ufs | null uis && null tbls = ""
| True = "UF"
pre = ["; Automatically generated by SBV. Do not edit."]
++ map ("; " ++) comments
++ logic
++ [ "(set-option :produce-models true)"
, "; --- literal constants ---"
]
++ map declConst consts
++ [ "; --- skolem constants ---" ]
++ [ "(declare-fun " ++ show s ++ " " ++ swFunType ss s ++ ")" | Right (s, ss) <- skolemInps]
++ [ "; --- constant tables ---" ]
++ concatMap constTable constTables
++ [ "; --- skolemized tables ---" ]
++ map (skolemTable (unwords (map swType foralls))) skolemTables
++ [ "; --- arrays ---" ]
++ concat arrayConstants
++ [ "; --- uninterpreted constants ---" ]
++ concatMap declUI uis
++ [ "; --- user given axioms ---" ]
++ map declAx axs
++ [ "; --- formula ---" ]
++ [if null foralls
then "(assert ; no quantifiers"
else "(assert (forall (" ++ intercalate "\n "
["(" ++ show s ++ " " ++ swType s ++ ")" | s <- foralls] ++ ")"]
++ map (letAlign . mkLet) asgns
++ map letAlign (if null delayedEqualities then [] else ("(and " ++ deH) : map (align 5) deTs)
++ [ impAlign (letAlign assertOut) ++ replicate noOfCloseParens ')' ]
noOfCloseParens = length asgns + (if null foralls then 1 else 2) + (if null delayedEqualities then 0 else 1)
(constTables, skolemTables) = ([(t, d) | (t, Left d) <- allTables], [(t, d) | (t, Right d) <- allTables])
allTables = [(t, genTableData skolemMap (not (null foralls), forallArgs) (map fst consts) t) | t <- tbls]
(arrayConstants, allArrayDelayeds) = unzip $ map (declArray (not (null foralls)) (map fst consts) skolemMap) arrs
delayedEqualities@(~(deH:deTs)) = concatMap snd skolemTables ++ concat allArrayDelayeds
foralls = [s | Left s <- skolemInps]
forallArgs = concatMap ((" " ++) . show) foralls
letAlign s
| null foralls = " " ++ s
| True = " " ++ s
impAlign s
| null delayedEqualities = s
| True = " " ++ s
align n s = replicate n ' ' ++ s
assertOut
| null cstrs = o
| True = "(and " ++ unwords (map mkConj cstrs ++ [o]) ++ ")"
where mkConj x = "(= " ++ cvtSW skolemMap x ++ " #b1)"
o | isSat = mkConj out
| True = "(not " ++ mkConj out ++ ")"
skolemMap = M.fromList [(s, ss) | Right (s, ss) <- skolemInps, not (null ss)]
tableMap = IM.fromList $ map mkConstTable constTables ++ map mkSkTable skolemTables
where mkConstTable (((t, _, _), _), _) = (t, "table" ++ show t)
mkSkTable (((t, _, _), _), _) = (t, "table" ++ show t ++ forallArgs)
asgns = F.toList asgnsSeq
mkLet (s, e) = "(let ((" ++ show s ++ " " ++ cvtExp skolemMap tableMap e ++ "))"
declConst (s, c) = "(define-fun " ++ show s ++ " " ++ swFunType [] s ++ " " ++ cvtCW c ++ ")"
declUI :: (String, SBVType) -> [String]
declUI (i, t) = ["(declare-fun uninterpreted_" ++ i ++ " " ++ cvtType t ++ ")"]
declAx :: (String, [String]) -> String
declAx (nm, ls) = (";; -- user given axiom: " ++ nm ++ "\n ") ++ intercalate "\n" ls
constTable :: (((Int, (Bool, Size), (Bool, Size)), [SW]), [String]) -> [String]
constTable (((i, (_, atSz), (_, rtSz)), _elts), is) = decl : map wrap is
where t = "table" ++ show i
decl = "(declare-fun " ++ t ++ " (" ++ smtType atSz ++ ") " ++ smtType rtSz ++ ")"
wrap s = "(assert " ++ s ++ ")"
skolemTable :: String -> (((Int, (Bool, Size), (Bool, Size)), [SW]), [String]) -> String
skolemTable qsIn (((i, (_, atSz), (_, rtSz)), _elts), _) = decl
where qs = if null qsIn then "" else qsIn ++ " "
t = "table" ++ show i
decl = "(declare-fun " ++ t ++ " (" ++ qs ++ smtType atSz ++ ") " ++ smtType rtSz ++ ")"
genTableData :: SkolemMap -> (Bool, String) -> [SW] -> ((Int, (Bool, Size), (Bool, Size)), [SW]) -> Either [String] [String]
genTableData skolemMap (_quantified, args) consts ((i, (sa, at), (_, _rt)), elts)
| null post = Left (map (topLevel . snd) pre)
| True = Right (map (nested . snd) (pre ++ post))
where ssw = cvtSW skolemMap
(pre, post) = partition fst (zipWith mkElt elts [(0::Int)..])
t = "table" ++ show i
mkElt x k = (isReady, (idx, ssw x))
where idx = cvtCW (mkConstCW (sa, at) k)
isReady = x `elem` consts
topLevel (idx, v) = "(= (" ++ t ++ " " ++ idx ++ ") " ++ v ++ ")"
nested (idx, v) = "(= (" ++ t ++ args ++ " " ++ idx ++ ") " ++ v ++ ")"
declArray :: Bool -> [SW] -> SkolemMap -> (Int, ArrayInfo) -> ([String], [String])
declArray quantified consts skolemMap (i, (_, ((_, atSz), (_, rtSz)), ctx)) = (adecl : map wrap pre, map snd post)
where topLevel = not quantified || case ctx of
ArrayFree Nothing -> True
ArrayFree (Just sw) -> sw `elem` consts
ArrayReset _ sw -> sw `elem` consts
ArrayMutate _ a b -> all (`elem` consts) [a, b]
ArrayMerge c _ _ -> c `elem` consts
(pre, post) = partition fst ctxInfo
nm = "array_" ++ show i
ssw sw
| topLevel || sw `elem` consts
= cvtSW skolemMap sw
| True
= tbd "Non-constant array initializer in a quantified context"
adecl = "(declare-fun " ++ nm ++ "() (Array " ++ smtType atSz ++ " " ++ smtType rtSz ++ "))"
ctxInfo = case ctx of
ArrayFree Nothing -> []
ArrayFree (Just sw) -> declA sw
ArrayReset _ sw -> declA sw
ArrayMutate j a b -> [(all (`elem` consts) [a, b], "(= " ++ nm ++ " (store array_" ++ show j ++ " " ++ ssw a ++ " " ++ ssw b ++ "))")]
ArrayMerge t j k -> [(t `elem` consts, "(= " ++ nm ++ " (ite (= #b1 " ++ ssw t ++ ") array_" ++ show j ++ " array_" ++ show k ++ "))")]
declA sw = let iv = nm ++ "_freeInitializer"
in [ (True, "(declare-fun " ++ iv ++ "() " ++ smtType atSz ++ ")")
, (sw `elem` consts, "(= (select " ++ nm ++ " " ++ iv ++ ") " ++ ssw sw ++ ")")
]
wrap (False, s) = s
wrap (True, s) = "(assert " ++ s ++ ")"
swType :: SW -> String
swType s = smtType (sizeOf s)
swFunType :: [SW] -> SW -> String
swFunType ss s = "(" ++ unwords (map swType ss) ++ ") " ++ swType s
smtType :: Size -> String
smtType (Size Nothing) = "Int"
smtType (Size (Just sz)) = "(_ BitVec " ++ show sz ++ ")"
cvtType :: SBVType -> String
cvtType (SBVType []) = error "SBV.SMT.SMTLib2.cvtType: internal: received an empty type!"
cvtType (SBVType xs) = "(" ++ unwords (map smtType body) ++ ") " ++ smtType ret
where szs = map snd xs
(body, ret) = (init szs, last szs)
type SkolemMap = M.Map SW [SW]
type TableMap = IM.IntMap String
cvtSW :: SkolemMap -> SW -> String
cvtSW skolemMap s
| Just ss <- s `M.lookup` skolemMap
= "(" ++ show s ++ concatMap ((" " ++) . show) ss ++ ")"
| True
= show s
hex :: Int -> Integer -> String
hex 1 v = "#b" ++ show v
hex sz v = "#x" ++ pad (sz `div` 4) (showHex v "")
where pad n s = replicate (n length s) '0' ++ s
cvtCW :: CW -> String
cvtCW x | isInfPrec x = if w >= 0 then show w else "(- " ++ show (abs w) ++ ")"
where w = cwVal x
cvtCW x | not (hasSign x) = hex (intSizeOf x) (cwVal x)
cvtCW x | cwVal x == least = mkMinBound (intSizeOf x)
where least = negate (2 ^ intSizeOf x)
cvtCW x = negIf (w < 0) $ hex (intSizeOf x) (abs w)
where w = cwVal x
negIf :: Bool -> String -> String
negIf True a = "(bvneg " ++ a ++ ")"
negIf False a = a
mkMinBound :: Int -> String
mkMinBound i = "#b1" ++ replicate (i1) '0'
getTable :: TableMap -> Int -> String
getTable m i
| Just tn <- i `IM.lookup` m = tn
| True = error $ "SBV.SMTLib2: Cannot locate table " ++ show i
unbounded :: SBVExpr -> a
unbounded expr = error $ "SBV.SMTLib2: Unsupported operation on unbounded integers: " ++ show expr
cvtExp :: SkolemMap -> TableMap -> SBVExpr -> String
cvtExp skolemMap tableMap expr@(SBVApp _ arguments) = sh expr
where ssw = cvtSW skolemMap
hasInfPrecArgs = any isInfPrec arguments
ensureBV = not hasInfPrecArgs || unbounded expr
lift2 o _ [x, y] = "(" ++ o ++ " " ++ x ++ " " ++ y ++ ")"
lift2 o _ sbvs = error $ "SBV.SMTLib2.sh.lift2: Unexpected arguments: " ++ show (o, sbvs)
lift2B oU oS sgn sbvs = "(ite " ++ lift2S oU oS sgn sbvs ++ " #b1 #b0)"
lift2S oU oS sgn sbvs
| sgn
= lift2 oS sgn sbvs
| True
= lift2 oU sgn sbvs
lift2N o sgn sbvs = "(bvnot " ++ lift2 o sgn sbvs ++ ")"
lift1 o _ [x] = "(" ++ o ++ " " ++ x ++ ")"
lift1 o _ sbvs = error $ "SBV.SMT.SMTLib2.sh.lift1: Unexpected arguments: " ++ show (o, sbvs)
sh (SBVApp Ite [a, b, c]) = "(ite (= #b1 " ++ ssw a ++ ") " ++ ssw b ++ " " ++ ssw c ++ ")"
sh (SBVApp (LkUp (t, (_, atSz), _, l) i e) [])
| needsCheck = "(ite " ++ cond ++ ssw e ++ " " ++ lkUp ++ ")"
| True = lkUp
where needsCheck = maybe True (\at -> (2::Integer)^at > fromIntegral l) (unSize atSz)
lkUp = "(" ++ getTable tableMap t ++ " " ++ ssw i ++ ")"
cond
| hasSign i = "(or " ++ le0 ++ " " ++ gtl ++ ") "
| True = gtl ++ " "
(less, leq) = case atSz of
Size Nothing -> ("<", "<=")
_ -> if hasSign i then ("bvslt", "bvsle") else ("bvult", "bvule")
mkCnst = cvtCW . mkConstCW (hasSign i, sizeOf i)
le0 = "(" ++ less ++ " " ++ ssw i ++ " " ++ mkCnst 0 ++ ")"
gtl = "(" ++ leq ++ " " ++ mkCnst l ++ " " ++ ssw i ++ ")"
sh (SBVApp (ArrEq i j) []) = "(ite (= array_" ++ show i ++ " array_" ++ show j ++") #b1 #b0)"
sh (SBVApp (ArrRead i) [a]) = "(select array_" ++ show i ++ " " ++ ssw a ++ ")"
sh (SBVApp (Uninterpreted nm) []) = "uninterpreted_" ++ nm
sh (SBVApp (Uninterpreted nm) args) = "(uninterpreted_" ++ nm ++ " " ++ unwords (map ssw args) ++ ")"
sh (SBVApp (Extract i j) [a]) | ensureBV = "((_ extract " ++ show i ++ " " ++ show j ++ ") " ++ ssw a ++ ")"
sh (SBVApp (Rol i) [a])
| not hasInfPrecArgs = rot ssw "rotate_left" i a
| True = sh (SBVApp (Shl i) [a])
sh (SBVApp (Ror i) [a])
| not hasInfPrecArgs = rot ssw "rotate_right" i a
| True = sh (SBVApp (Shr i) [a])
sh (SBVApp (Shl i) [a])
| not hasInfPrecArgs = shft ssw "bvshl" "bvshl" i a
| i < 0 = sh (SBVApp (Shr (i)) [a])
| True = "(* " ++ ssw a ++ " " ++ show (bit i :: Integer) ++ ")"
sh (SBVApp (Shr i) [a])
| not hasInfPrecArgs = shft ssw "bvlshr" "bvashr" i a
| i < 0 = sh (SBVApp (Shl (i)) [a])
| True = "(div " ++ ssw a ++ " " ++ show (bit i :: Integer) ++ ")"
sh (SBVApp op args)
| Just f <- lookup op smtBVOpTable, ensureBV
= f (any hasSign args) (map ssw args)
where
smtBVOpTable = [ (And, lift2 "bvand")
, (Or, lift2 "bvor")
, (XOr, lift2 "bvxor")
, (Not, lift1 "bvnot")
, (Join, lift2 "concat")
]
sh inp@(SBVApp op args)
| hasInfPrecArgs
= case lookup op smtOpIntTable of
Just f -> f True (map ssw args)
_ -> unbounded inp
| Just f <- lookup op smtOpBVTable
= f (any hasSign args) (map ssw args)
| True
= error $ "SBV.SMT.SMTLib2.cvtExp.sh: impossible happened; can't translate: " ++ show inp
where smtOpBVTable = [ (Plus, lift2 "bvadd")
, (Minus, lift2 "bvsub")
, (Times, lift2 "bvmul")
, (Quot, lift2S "bvudiv" "bvsdiv")
, (Rem, lift2S "bvurem" "bvsrem")
, (Equal, lift2 "bvcomp")
, (NotEqual, lift2N "bvcomp")
, (LessThan, lift2B "bvult" "bvslt")
, (GreaterThan, lift2B "bvugt" "bvsgt")
, (LessEq, lift2B "bvule" "bvsle")
, (GreaterEq, lift2B "bvuge" "bvsge")
]
smtOpIntTable = [ (Plus, lift2 "+")
, (Minus, lift2 "-")
, (Times, lift2 "*")
, (Quot, lift2 "div")
, (Rem, lift2 "mod")
, (Equal, lift2B "=" "=")
, (NotEqual, lift2B "distinct" "distinct")
, (LessThan, lift2B "<" "<")
, (GreaterThan, lift2B ">" ">")
, (LessEq, lift2B "<=" "<=")
, (GreaterEq, lift2B ">=" ">=")
]
rot :: (SW -> String) -> String -> Int -> SW -> String
rot ssw o c x = "((_ " ++ o ++ " " ++ show c ++ ") " ++ ssw x ++ ")"
shft :: (SW -> String) -> String -> String -> Int -> SW -> String
shft ssw oW oS c x = "(" ++ o ++ " " ++ ssw x ++ " " ++ cvtCW c' ++ ")"
where s = hasSign x
c' = mkConstCW (s, sizeOf x) c
o = if hasSign x then oS else oW