{-# LANGUAGE BangPatterns #-}
module Data.SBV.Core.Operations
(
svTrue, svFalse, svBool
, svInteger, svFloat, svDouble, svReal, svEnumFromThenTo, svString, svChar
, svAsBool, svAsInteger, svNumerator, svDenominator
, svPlus, svTimes, svMinus, svUNeg, svAbs
, svDivide, svQuot, svRem, svQuotRem
, svEqual, svNotEqual, svStrongEqual, svSetEqual
, svLessThan, svGreaterThan, svLessEq, svGreaterEq
, svAnd, svOr, svXOr, svNot
, svShl, svShr, svRol, svRor
, svExtract, svJoin
, svIte, svLazyIte, svSymbolicMerge
, svSelect
, svSign, svUnsign, svSetBit, svWordFromBE, svWordFromLE
, svExp, svFromIntegral
, svMkOverflow
, svToWord1, svFromWord1, svTestBit
, svShiftLeft, svShiftRight
, svRotateLeft, svRotateRight
, svBarrelRotateLeft, svBarrelRotateRight
, svBlastLE, svBlastBE
, svAddConstant, svIncrement, svDecrement
, SArr, readSArr, writeSArr, mergeSArr, newSArr, eqSArr
, SFunArr(..), readSFunArr, writeSFunArr, mergeSFunArr, newSFunArr
, mkSymOp
)
where
import Data.Bits (Bits(..))
import Data.List (genericIndex, genericLength, genericTake)
import qualified Data.IORef as R (modifyIORef', newIORef, readIORef)
import qualified Data.Map.Strict as Map (toList, fromList, lookup)
import qualified Data.IntMap.Strict as IMap (IntMap, empty, toAscList, fromAscList, lookup, size, insert)
import Data.SBV.Core.AlgReals
import Data.SBV.Core.Kind
import Data.SBV.Core.Concrete
import Data.SBV.Core.Symbolic
import Data.Ratio
import Data.SBV.Utils.Numeric (fpIsEqualObjectH)
svTrue :: SVal
svTrue = SVal KBool (Left trueCV)
svFalse :: SVal
svFalse = SVal KBool (Left falseCV)
svBool :: Bool -> SVal
svBool b = if b then svTrue else svFalse
svInteger :: Kind -> Integer -> SVal
svInteger k n = SVal k (Left $! mkConstCV k n)
svFloat :: Float -> SVal
svFloat f = SVal KFloat (Left $! CV KFloat (CFloat f))
svDouble :: Double -> SVal
svDouble d = SVal KDouble (Left $! CV KDouble (CDouble d))
svString :: String -> SVal
svString s = SVal KString (Left $! CV KString (CString s))
svChar :: Char -> SVal
svChar c = SVal KChar (Left $! CV KChar (CChar c))
svReal :: Rational -> SVal
svReal d = SVal KReal (Left $! CV KReal (CAlgReal (fromRational d)))
svAsBool :: SVal -> Maybe Bool
svAsBool (SVal _ (Left cv)) = Just (cvToBool cv)
svAsBool _ = Nothing
svAsInteger :: SVal -> Maybe Integer
svAsInteger (SVal _ (Left (CV _ (CInteger n)))) = Just n
svAsInteger _ = Nothing
svNumerator :: SVal -> Maybe Integer
svNumerator (SVal KReal (Left (CV KReal (CAlgReal (AlgRational True r))))) = Just $ numerator r
svNumerator _ = Nothing
svDenominator :: SVal -> Maybe Integer
svDenominator (SVal KReal (Left (CV KReal (CAlgReal (AlgRational True r))))) = Just $ denominator r
svDenominator _ = Nothing
svEnumFromThenTo :: SVal -> Maybe SVal -> SVal -> Maybe [SVal]
svEnumFromThenTo bf mbs bt
| Just bs <- mbs, Just f <- svAsInteger bf, Just s <- svAsInteger bs, Just t <- svAsInteger bt, s /= f = Just $ map (svInteger (kindOf bf)) [f, s .. t]
| Nothing <- mbs, Just f <- svAsInteger bf, Just t <- svAsInteger bt = Just $ map (svInteger (kindOf bf)) [f .. t]
| True = Nothing
svPlus :: SVal -> SVal -> SVal
svPlus x y
| isConcreteZero x = y
| isConcreteZero y = x
| True = liftSym2 (mkSymOp Plus) rationalCheck (+) (+) (+) (+) x y
svTimes :: SVal -> SVal -> SVal
svTimes x y
| isConcreteZero x = x
| isConcreteZero y = y
| isConcreteOne x = y
| isConcreteOne y = x
| True = liftSym2 (mkSymOp Times) rationalCheck (*) (*) (*) (*) x y
svMinus :: SVal -> SVal -> SVal
svMinus x y
| isConcreteZero y = x
| True = liftSym2 (mkSymOp Minus) rationalCheck (-) (-) (-) (-) x y
svUNeg :: SVal -> SVal
svUNeg = liftSym1 (mkSymOp1 UNeg) negate negate negate negate
svAbs :: SVal -> SVal
svAbs = liftSym1 (mkSymOp1 Abs) abs abs abs abs
svDivide :: SVal -> SVal -> SVal
svDivide = liftSym2 (mkSymOp Quot) rationalCheck (/) idiv (/) (/)
where idiv x 0 = x
idiv x y = x `div` y
svExp :: SVal -> SVal -> SVal
svExp b e
| Just x <- svAsInteger e
= if x >= 0 then let go n v
| n == 0 = one
| even n = go (n `div` 2) (svTimes v v)
| True = svTimes v $ go (n `div` 2) (svTimes v v)
in go x b
else error $ "svExp: exponentiation: negative exponent: " ++ show x
| not (isBounded e) || hasSign e
= error $ "svExp: exponentiation only works with unsigned bounded symbolic exponents, kind: " ++ show (kindOf e)
| True
= prod $ zipWith (\use n -> svIte use n one)
(svBlastLE e)
(iterate (\x -> svTimes x x) b)
where prod = foldr svTimes one
one = svInteger (kindOf b) 1
svBlastLE :: SVal -> [SVal]
svBlastLE x = map (svTestBit x) [0 .. intSizeOf x - 1]
svSetBit :: SVal -> Int -> SVal
svSetBit x i = x `svOr` svInteger (kindOf x) (bit i :: Integer)
svBlastBE :: SVal -> [SVal]
svBlastBE = reverse . svBlastLE
svWordFromLE :: [SVal] -> SVal
svWordFromLE bs = go zero 0 bs
where zero = svInteger (KBounded False (length bs)) 0
go !acc _ [] = acc
go !acc !i (x:xs) = go (svIte x (svSetBit acc i) acc) (i+1) xs
svWordFromBE :: [SVal] -> SVal
svWordFromBE = svWordFromLE . reverse
svAddConstant :: Integral a => SVal -> a -> SVal
svAddConstant x i = x `svPlus` svInteger (kindOf x) (fromIntegral i)
svIncrement :: SVal -> SVal
svIncrement x = svAddConstant x (1::Integer)
svDecrement :: SVal -> SVal
svDecrement x = svAddConstant x (-1 :: Integer)
svQuot :: SVal -> SVal -> SVal
svQuot x y
| isConcreteZero x = x
| isConcreteZero y = svInteger (kindOf x) 0
| isConcreteOne y = x
| True = liftSym2 (mkSymOp Quot) nonzeroCheck
(noReal "quot") quot' (noFloat "quot") (noDouble "quot") x y
where
quot' a b | kindOf x == KUnbounded = div a (abs b) * signum b
| otherwise = quot a b
svRem :: SVal -> SVal -> SVal
svRem x y
| isConcreteZero x = x
| isConcreteZero y = x
| isConcreteOne y = svInteger (kindOf x) 0
| True = liftSym2 (mkSymOp Rem) nonzeroCheck
(noReal "rem") rem' (noFloat "rem") (noDouble "rem") x y
where
rem' a b | kindOf x == KUnbounded = mod a (abs b)
| otherwise = rem a b
svQuotRem :: SVal -> SVal -> (SVal, SVal)
svQuotRem x y = (x `svQuot` y, x `svRem` y)
eqOptBool :: Op -> SV -> SV -> SV -> Maybe SV
eqOptBool op w x y
| k == KBool && op == Equal && x == trueSV = Just y
| k == KBool && op == Equal && y == trueSV = Just x
| k == KBool && op == NotEqual && x == falseSV = Just y
| k == KBool && op == NotEqual && y == falseSV = Just x
| True = eqOpt w x y
where k = swKind x
svEqual :: SVal -> SVal -> SVal
svEqual a b
| isSet a && isSet b
= svSetEqual a b
| True
= liftSym2B (mkSymOpSC (eqOptBool Equal trueSV) Equal) rationalCheck (==) (==) (==) (==) (==) (==) (==) (==) (==) (==) (==) a b
svNotEqual :: SVal -> SVal -> SVal
svNotEqual a b
| isSet a && isSet b
= svNot $ svEqual a b
| True
= liftSym2B (mkSymOpSC (eqOptBool NotEqual falseSV) NotEqual) rationalCheck (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=) a b
svSetEqual :: SVal -> SVal -> SVal
svSetEqual sa sb
| not (isSet sa && isSet sb && kindOf sa == kindOf sb)
= error $ "Data.SBV.svSetEqual: Called on ill-typed args: " ++ show (kindOf sa, kindOf sb)
| Just (RegularSet a) <- getSet sa, Just (RegularSet b) <- getSet sb
= svBool (a == b)
| Just (ComplementSet a) <- getSet sa, Just (ComplementSet b) <- getSet sb
= svBool (a == b)
| True
= SVal KBool $ Right $ cache r
where getSet (SVal _ (Left (CV _ (CSet s)))) = Just s
getSet _ = Nothing
r st = do sva <- svToSV st sa
svb <- svToSV st sb
newExpr st KBool $ SBVApp (SetOp SetEqual) [sva, svb]
svStrongEqual :: SVal -> SVal -> SVal
svStrongEqual x y
| isFloat x, Just f1 <- getF x, Just f2 <- getF y
= svBool $ f1 `fpIsEqualObjectH` f2
| isDouble x, Just f1 <- getD x, Just f2 <- getD y
= svBool $ f1 `fpIsEqualObjectH` f2
| isFloat x || isDouble x
= SVal KBool $ Right $ cache r
| True
= svEqual x y
where getF (SVal _ (Left (CV _ (CFloat f)))) = Just f
getF _ = Nothing
getD (SVal _ (Left (CV _ (CDouble d)))) = Just d
getD _ = Nothing
r st = do sx <- svToSV st x
sy <- svToSV st y
newExpr st KBool (SBVApp (IEEEFP FP_ObjEqual) [sx, sy])
svLessThan :: SVal -> SVal -> SVal
svLessThan x y
| isConcreteMax x = svFalse
| isConcreteMin y = svFalse
| True = liftSym2B (mkSymOpSC (eqOpt falseSV) LessThan) rationalCheck (<) (<) (<) (<) (<) (<) (<) (<) (<) (<) (uiLift "<" (<)) x y
svGreaterThan :: SVal -> SVal -> SVal
svGreaterThan x y
| isConcreteMin x = svFalse
| isConcreteMax y = svFalse
| True = liftSym2B (mkSymOpSC (eqOpt falseSV) GreaterThan) rationalCheck (>) (>) (>) (>) (>) (>) (>) (>) (>) (>) (uiLift ">" (>)) x y
svLessEq :: SVal -> SVal -> SVal
svLessEq x y
| isConcreteMin x = svTrue
| isConcreteMax y = svTrue
| True = liftSym2B (mkSymOpSC (eqOpt trueSV) LessEq) rationalCheck (<=) (<=) (<=) (<=) (<=) (<=) (<=) (<=) (<=) (<=) (uiLift "<=" (<=)) x y
svGreaterEq :: SVal -> SVal -> SVal
svGreaterEq x y
| isConcreteMax x = svTrue
| isConcreteMin y = svTrue
| True = liftSym2B (mkSymOpSC (eqOpt trueSV) GreaterEq) rationalCheck (>=) (>=) (>=) (>=) (>=) (>=) (>=) (>=) (>=) (>=) (uiLift ">=" (>=)) x y
svAnd :: SVal -> SVal -> SVal
svAnd x y
| isConcreteZero x = x
| isConcreteOnes x = y
| isConcreteZero y = y
| isConcreteOnes y = x
| True = liftSym2 (mkSymOpSC opt And) (const (const True)) (noReal ".&.") (.&.) (noFloat ".&.") (noDouble ".&.") x y
where opt a b
| a == falseSV || b == falseSV = Just falseSV
| a == trueSV = Just b
| b == trueSV = Just a
| True = Nothing
svOr :: SVal -> SVal -> SVal
svOr x y
| isConcreteZero x = y
| isConcreteOnes x = x
| isConcreteZero y = x
| isConcreteOnes y = y
| True = liftSym2 (mkSymOpSC opt Or) (const (const True))
(noReal ".|.") (.|.) (noFloat ".|.") (noDouble ".|.") x y
where opt a b
| a == trueSV || b == trueSV = Just trueSV
| a == falseSV = Just b
| b == falseSV = Just a
| True = Nothing
svXOr :: SVal -> SVal -> SVal
svXOr x y
| isConcreteZero x = y
| isConcreteOnes x = svNot y
| isConcreteZero y = x
| isConcreteOnes y = svNot x
| True = liftSym2 (mkSymOpSC opt XOr) (const (const True))
(noReal "xor") xor (noFloat "xor") (noDouble "xor") x y
where opt a b
| a == b && swKind a == KBool = Just falseSV
| a == falseSV = Just b
| b == falseSV = Just a
| True = Nothing
svNot :: SVal -> SVal
svNot = liftSym1 (mkSymOp1SC opt Not)
(noRealUnary "complement") complement
(noFloatUnary "complement") (noDoubleUnary "complement")
where opt a
| a == falseSV = Just trueSV
| a == trueSV = Just falseSV
| True = Nothing
svShl :: SVal -> Int -> SVal
svShl x i
| i <= 0
= x
| isBounded x, i >= intSizeOf x
= svInteger k 0
| True
= x `svShiftLeft` svInteger k (fromIntegral i)
where k = kindOf x
svShr :: SVal -> Int -> SVal
svShr x i
| i <= 0
= x
| isBounded x, i >= intSizeOf x
= if not (hasSign x)
then z
else svIte (x `svLessThan` z) neg1 z
| True
= x `svShiftRight` svInteger k (fromIntegral i)
where k = kindOf x
z = svInteger k 0
neg1 = svInteger k (-1)
svRol :: SVal -> Int -> SVal
svRol x i
| i <= 0
= x
| True
= case kindOf x of
KBounded _ sz -> liftSym1 (mkSymOp1 (Rol (i `mod` sz)))
(noRealUnary "rotateL") (rot True sz i)
(noFloatUnary "rotateL") (noDoubleUnary "rotateL") x
_ -> svShl x i
svRor :: SVal -> Int -> SVal
svRor x i
| i <= 0
= x
| True
= case kindOf x of
KBounded _ sz -> liftSym1 (mkSymOp1 (Ror (i `mod` sz)))
(noRealUnary "rotateR") (rot False sz i)
(noFloatUnary "rotateR") (noDoubleUnary "rotateR") x
_ -> svShr x i
rot :: Bool -> Int -> Int -> Integer -> Integer
rot toLeft sz amt x
| sz < 2 = x
| True = norm x y' `shiftL` y .|. norm (x `shiftR` y') y
where (y, y') | toLeft = (amt `mod` sz, sz - y)
| True = (sz - y', amt `mod` sz)
norm v s = v .&. ((1 `shiftL` s) - 1)
svExtract :: Int -> Int -> SVal -> SVal
svExtract i j x@(SVal (KBounded s _) _)
| i < j
= SVal k (Left $! CV k (CInteger 0))
| SVal _ (Left (CV _ (CInteger v))) <- x
= SVal k (Left $! normCV (CV k (CInteger (v `shiftR` j))))
| True
= SVal k (Right (cache y))
where k = KBounded s (i - j + 1)
y st = do sv <- svToSV st x
newExpr st k (SBVApp (Extract i j) [sv])
svExtract _ _ _ = error "extract: non-bitvector type"
svJoin :: SVal -> SVal -> SVal
svJoin x@(SVal (KBounded s i) a) y@(SVal (KBounded _ j) b)
| i == 0 = y
| j == 0 = x
| Left (CV _ (CInteger m)) <- a, Left (CV _ (CInteger n)) <- b
= SVal k (Left $! CV k (CInteger (m `shiftL` j .|. n)))
| True
= SVal k (Right (cache z))
where
k = KBounded s (i + j)
z st = do xsw <- svToSV st x
ysw <- svToSV st y
newExpr st k (SBVApp Join [xsw, ysw])
svJoin _ _ = error "svJoin: non-bitvector type"
svIte :: SVal -> SVal -> SVal -> SVal
svIte t a b = svSymbolicMerge (kindOf a) True t a b
svLazyIte :: Kind -> SVal -> SVal -> SVal -> SVal
svLazyIte k t a b = svSymbolicMerge k False t a b
svSymbolicMerge :: Kind -> Bool -> SVal -> SVal -> SVal -> SVal
svSymbolicMerge k force t a b
| Just r <- svAsBool t
= if r then a else b
| force, rationalSBVCheck a b, sameResult a b
= a
| True
= SVal k $ Right $ cache c
where sameResult (SVal _ (Left c1)) (SVal _ (Left c2)) = c1 == c2
sameResult _ _ = False
c st = do swt <- svToSV st t
case () of
() | swt == trueSV -> svToSV st a
() | swt == falseSV -> svToSV st b
() -> do
let sta = st `extendSValPathCondition` svAnd t
let stb = st `extendSValPathCondition` svAnd (svNot t)
swa <- svToSV sta a
swb <- svToSV stb b
case () of
() | swa == swb -> return swa
() | swa == trueSV && swb == falseSV -> return swt
() | swa == falseSV && swb == trueSV -> newExpr st k (SBVApp Not [swt])
() | swa == trueSV -> newExpr st k (SBVApp Or [swt, swb])
() | swa == falseSV -> do swt' <- newExpr st KBool (SBVApp Not [swt])
newExpr st k (SBVApp And [swt', swb])
() | swb == trueSV -> do swt' <- newExpr st KBool (SBVApp Not [swt])
newExpr st k (SBVApp Or [swt', swa])
() | swb == falseSV -> newExpr st k (SBVApp And [swt, swa])
() -> newExpr st k (SBVApp Ite [swt, swa, swb])
svSelect :: [SVal] -> SVal -> SVal -> SVal
svSelect xs err ind
| SVal _ (Left c) <- ind =
case cvVal c of
CInteger i -> if i < 0 || i >= genericLength xs
then err
else xs `genericIndex` i
_ -> error $ "SBV.select: unsupported " ++ show (kindOf ind) ++ " valued select/index expression"
svSelect xsOrig err ind = xs `seq` SVal kElt (Right (cache r))
where
kInd = kindOf ind
kElt = kindOf err
xs = case kInd of
KBounded False i -> genericTake ((2::Integer) ^ i) xsOrig
KBounded True i -> genericTake ((2::Integer) ^ (i-1)) xsOrig
KUnbounded -> xsOrig
_ -> error $ "SBV.select: unsupported " ++ show kInd ++ " valued select/index expression"
r st = do sws <- mapM (svToSV st) xs
swe <- svToSV st err
if all (== swe) sws
then return swe
else do idx <- getTableIndex st kInd kElt sws
swi <- svToSV st ind
let len = length xs
newExpr st kElt (SBVApp (LkUp (idx, kInd, kElt, len) swi swe) [])
svChangeSign :: Bool -> SVal -> SVal
svChangeSign s x
| not (isBounded x) = error $ "Data.SBV." ++ nm ++ ": Received non bit-vector kind: " ++ show (kindOf x)
| Just n <- svAsInteger x = svInteger k n
| True = SVal k (Right (cache y))
where
nm = if s then "svSign" else "svUnsign"
k = KBounded s (intSizeOf x)
y st = do xsw <- svToSV st x
newExpr st k (SBVApp (Extract (intSizeOf x - 1) 0) [xsw])
svSign :: SVal -> SVal
svSign = svChangeSign True
svUnsign :: SVal -> SVal
svUnsign = svChangeSign False
svFromIntegral :: Kind -> SVal -> SVal
svFromIntegral kTo x
| Just v <- svAsInteger x
= svInteger kTo v
| True
= result
where result = SVal kTo (Right (cache y))
kFrom = kindOf x
y st = do xsw <- svToSV st x
newExpr st kTo (SBVApp (KindCast kFrom kTo) [xsw])
svToWord1 :: SVal -> SVal
svToWord1 b = svSymbolicMerge k True b (svInteger k 1) (svInteger k 0)
where k = KBounded False 1
svFromWord1 :: SVal -> SVal
svFromWord1 x = svNotEqual x (svInteger k 0)
where k = kindOf x
svTestBit :: SVal -> Int -> SVal
svTestBit x i
| i < intSizeOf x = svFromWord1 (svExtract i i x)
| True = svFalse
svShiftLeft :: SVal -> SVal -> SVal
svShiftLeft = svShift True
svShiftRight :: SVal -> SVal -> SVal
svShiftRight = svShift False
svShift :: Bool -> SVal -> SVal -> SVal
svShift toLeft x i
| Just r <- constFoldValue
= r
| cannotOverShift
= svIte (i `svLessThan` svInteger ki 0)
x
regularShiftValue
| True
= svIte (i `svLessThan` svInteger ki 0)
x
$ svIte (i `svGreaterEq` svInteger ki (fromIntegral (intSizeOf x)))
overShiftValue
regularShiftValue
where nm | toLeft = "shiftLeft"
| True = "shiftRight"
kx = kindOf x
ki = kindOf i
constFoldValue
| Just iv <- getConst i, iv == 0
= Just x
| Just xv <- getConst x, xv == 0
= Just x
| Just xv <- getConst x, Just iv <- getConst i
= Just $ SVal kx . Left $! normCV $ CV kx (CInteger (xv `opC` shiftAmount iv))
| isUnbounded x || isUnbounded i
= bailOut $ "Not yet implemented unbounded/non-constants shifts for " ++ show (kx, ki) ++ ", please file a request!"
| not (isBounded x && isBounded i)
= bailOut $ "Unexpected kinds: " ++ show (kx, ki)
| True
= Nothing
where bailOut m = error $ "SBV." ++ nm ++ ": " ++ m
getConst (SVal _ (Left (CV _ (CInteger val)))) = Just val
getConst _ = Nothing
opC | toLeft = shiftL
| True = shiftR
shiftAmount :: Integer -> Int
shiftAmount iv
| iv <= 0 = 0
| isUnbounded i, iv > fromIntegral (maxBound :: Int) = bailOut $ "Unsupported constant unbounded shift with amount: " ++ show iv
| isUnbounded x = fromIntegral iv
| iv >= fromIntegral ub = ub
| not (isBounded x && isBounded i) = bailOut $ "Unsupported kinds: " ++ show (kx, ki)
| True = fromIntegral iv
where ub = intSizeOf x
cannotOverShift = maxRepresentable <= fromIntegral (intSizeOf x)
where maxRepresentable :: Integer
maxRepresentable
| hasSign i = bit (intSizeOf i - 1) - 1
| True = bit (intSizeOf i ) - 1
overShiftValue | toLeft = zx
| hasSign x = svIte (x `svLessThan` zx) neg1 zx
| True = zx
where zx = svInteger kx 0
neg1 = svInteger kx (-1)
regularShiftValue = SVal kx $ Right $ cache result
where result st = do sw1 <- svToSV st x
sw2 <- svToSV st i
let op | toLeft = Shl
| True = Shr
adjustedShift <- if kx == ki
then return sw2
else newExpr st kx (SBVApp (KindCast ki kx) [sw2])
newExpr st kx (SBVApp op [sw1, adjustedShift])
svRotateLeft :: SVal -> SVal -> SVal
svRotateLeft x i
| not (isBounded x)
= svShiftLeft x i
| isBounded i && bit si <= toInteger sx
= svIte (svLessThan i zi)
(svSelect [x `svRor` k | k <- [0 .. bit si - 1]] z (svUNeg i))
(svSelect [x `svRol` k | k <- [0 .. bit si - 1]] z i)
| True
= svIte (svLessThan i zi)
(svSelect [x `svRor` k | k <- [0 .. sx - 1]] z (svUNeg i `svRem` n))
(svSelect [x `svRol` k | k <- [0 .. sx - 1]] z ( i `svRem` n))
where sx = intSizeOf x
si = intSizeOf i
z = svInteger (kindOf x) 0
zi = svInteger (kindOf i) 0
n = svInteger (kindOf i) (toInteger sx)
svBarrelRotateLeft :: SVal -> SVal -> SVal
svBarrelRotateLeft x i
| not (isBounded x && isBounded i && not (hasSign i))
= error $ "Data.SBV.Dynamic.svBarrelRotateLeft: Arguments must be bounded with second argument unsigned. Received: " ++ show (x, i)
| Just iv <- svAsInteger i
= svRol x $ fromIntegral (iv `rem` fromIntegral (intSizeOf x))
| True
= barrelRotate svRol x i
svBarrelRotateRight :: SVal -> SVal -> SVal
svBarrelRotateRight x i
| not (isBounded x && isBounded i && not (hasSign i))
= error $ "Data.SBV.Dynamic.svBarrelRotateRight: Arguments must be bounded with second argument unsigned. Received: " ++ show (x, i)
| Just iv <- svAsInteger i
= svRor x $ fromIntegral (iv `rem` fromIntegral (intSizeOf x))
| True
= barrelRotate svRor x i
barrelRotate :: (SVal -> Int -> SVal) -> SVal -> SVal -> SVal
barrelRotate f a c = loop blasted a
where loop :: [(SVal, Integer)] -> SVal -> SVal
loop [] acc = acc
loop ((b, v) : rest) acc = loop rest (svIte b (f acc (fromInteger v)) acc)
sa = toInteger $ intSizeOf a
n = svInteger (kindOf c) sa
reducedC = c `svRem` n
blasted = takeWhile significant $ zip (svBlastLE reducedC) [2^i | i <- [(0::Integer)..]]
significant (_, pos) = pos < sa
svRotateRight :: SVal -> SVal -> SVal
svRotateRight x i
| not (isBounded x)
= svShiftRight x i
| isBounded i && bit si <= toInteger sx
= svIte (svLessThan i zi)
(svSelect [x `svRol` k | k <- [0 .. bit si - 1]] z (svUNeg i))
(svSelect [x `svRor` k | k <- [0 .. bit si - 1]] z i)
| True
= svIte (svLessThan i zi)
(svSelect [x `svRol` k | k <- [0 .. sx - 1]] z (svUNeg i `svRem` n))
(svSelect [x `svRor` k | k <- [0 .. sx - 1]] z ( i `svRem` n))
where sx = intSizeOf x
si = intSizeOf i
z = svInteger (kindOf x) 0
zi = svInteger (kindOf i) 0
n = svInteger (kindOf i) (toInteger sx)
svMkOverflow :: OvOp -> SVal -> SVal -> SVal
svMkOverflow o x y = SVal KBool (Right (cache r))
where r st = do sx <- svToSV st x
sy <- svToSV st y
newExpr st KBool $ SBVApp (OverflowOp o) [sx, sy]
data SArr = SArr (Kind, Kind) (Cached ArrayIndex)
readSArr :: SArr -> SVal -> SVal
readSArr (SArr (_, bk) f) a = SVal bk $ Right $ cache r
where r st = do arr <- uncacheAI f st
i <- svToSV st a
newExpr st bk (SBVApp (ArrRead arr) [i])
writeSArr :: SArr -> SVal -> SVal -> SArr
writeSArr (SArr ainfo f) a b = SArr ainfo $ cache g
where g st = do arr <- uncacheAI f st
addr <- svToSV st a
val <- svToSV st b
amap <- R.readIORef (rArrayMap st)
let j = ArrayIndex $ IMap.size amap
upd = IMap.insert (unArrayIndex j) ("array_" ++ show j, ainfo, ArrayMutate arr addr val)
j `seq` modifyState st rArrayMap upd $ modifyIncState st rNewArrs upd
return j
mergeSArr :: SVal -> SArr -> SArr -> SArr
mergeSArr t (SArr ainfo a) (SArr _ b) = SArr ainfo $ cache h
where h st = do ai <- uncacheAI a st
bi <- uncacheAI b st
ts <- svToSV st t
amap <- R.readIORef (rArrayMap st)
let k = ArrayIndex $ IMap.size amap
upd = IMap.insert (unArrayIndex k) ("array_" ++ show k, ainfo, ArrayMerge ts ai bi)
k `seq` modifyState st rArrayMap upd $ modifyIncState st rNewArrs upd
return k
newSArr :: State -> (Kind, Kind) -> (Int -> String) -> Maybe SVal -> IO SArr
newSArr st ainfo mkNm mbDef = do
amap <- R.readIORef $ rArrayMap st
mbSWDef <- case mbDef of
Nothing -> return Nothing
Just sv -> Just <$> svToSV st sv
let i = ArrayIndex $ IMap.size amap
nm = mkNm (unArrayIndex i)
upd = IMap.insert (unArrayIndex i) (nm, ainfo, ArrayFree mbSWDef)
registerLabel "SArray declaration" st nm
modifyState st rArrayMap upd $ modifyIncState st rNewArrs upd
return $ SArr ainfo $ cache $ const $ return i
eqSArr :: SArr -> SArr -> SVal
eqSArr (SArr _ a) (SArr _ b) = SVal KBool $ Right $ cache c
where c st = do ai <- uncacheAI a st
bi <- uncacheAI b st
newExpr st KBool (SBVApp (ArrEq ai bi) [])
data SFunArr = SFunArr (Kind, Kind) (Cached FArrayIndex)
nodeIdToSVal :: Kind -> Int -> SVal
nodeIdToSVal k i = swToSVal $ SV k (NodeId i)
swToSVal :: SV -> SVal
swToSVal sv@(SV k _) = SVal k $ Right $ cache $ const $ return sv
svEqualWithConsts :: (SVal, Maybe CV) -> (SVal, Maybe CV) -> SVal
svEqualWithConsts sv1 sv2 = case (grabCV sv1, grabCV sv2) of
(Just cv, Just cv') | rationalCheck cv cv' -> if cv == cv' then svTrue else svFalse
_ -> fst sv1 `svEqual` fst sv2
where grabCV (_, Just cv) = Just cv
grabCV (SVal _ (Left cv), _ ) = Just cv
grabCV _ = Nothing
readSFunArr :: SFunArr -> SVal -> SVal
readSFunArr (SFunArr (ak, bk) f) address
| kindOf address /= ak
= error $ "Data.SBV.readSFunArr: Impossible happened, accesing with address type: " ++ show (kindOf address) ++ ", expected: " ++ show ak
| True
= SVal bk $ Right $ cache r
where r st = do fArrayIndex <- uncacheFAI f st
fArrMap <- R.readIORef (rFArrayMap st)
constMap <- R.readIORef (rconstMap st)
let consts = Map.fromList [(i, cv) | (cv, SV _ (NodeId i)) <- Map.toList constMap]
case unFArrayIndex fArrayIndex `IMap.lookup` fArrMap of
Nothing -> error $ "Data.SBV.readSFunArr: Impossible happened while trying to access SFunArray, can't find index: " ++ show fArrayIndex
Just (uninitializedRead, rCache) -> do
memoTable <- R.readIORef rCache
SV _ (NodeId addressNodeId) <- svToSV st address
case addressNodeId `IMap.lookup` memoTable of
Just v -> return v
Nothing ->
do let aInfo = (address, addressNodeId `Map.lookup` consts)
find :: [(Int, SV)] -> SVal
find [] = uninitializedRead address
find ((i, v) : ivs) = svIte (svEqualWithConsts (nodeIdToSVal ak i, i `Map.lookup` consts) aInfo) (swToSVal v) (find ivs)
finalValue = find (IMap.toAscList memoTable)
finalSW <- svToSV st finalValue
R.modifyIORef' rCache (IMap.insert addressNodeId finalSW)
return finalSW
writeSFunArr :: SFunArr -> SVal -> SVal -> SFunArr
writeSFunArr (SFunArr (ak, bk) f) address b
| kindOf address /= ak
= error $ "Data.SBV.writeSFunArr: Impossible happened, accesing with address type: " ++ show (kindOf address) ++ ", expected: " ++ show ak
| kindOf b /= bk
= error $ "Data.SBV.writeSFunArr: Impossible happened, accesing with address type: " ++ show (kindOf b) ++ ", expected: " ++ show bk
| True
= SFunArr (ak, bk) $ cache g
where g st = do fArrayIndex <- uncacheFAI f st
fArrMap <- R.readIORef (rFArrayMap st)
constMap <- R.readIORef (rconstMap st)
let consts = Map.fromList [(i, cv) | (cv, SV _ (NodeId i)) <- Map.toList constMap]
case unFArrayIndex fArrayIndex `IMap.lookup` fArrMap of
Nothing -> error $ "Data.SBV.writeSFunArr: Impossible happened while trying to access SFunArray, can't find index: " ++ show fArrayIndex
Just (aUi, rCache) -> do
memoTable <- R.readIORef rCache
SV _ (NodeId addressNodeId) <- svToSV st address
val <- svToSV st b
cont <- case addressNodeId `IMap.lookup` memoTable of
Just oldVal
| val == oldVal -> return $ Left fArrayIndex
| True -> return $ Right memoTable
Nothing -> do
let aInfo = (address, addressNodeId `Map.lookup` consts)
walk :: [(Int, SV)] -> [(Int, SV)] -> IO [(Int, SV)]
walk [] sofar = return $ reverse sofar
walk ((i, s):iss) sofar = modify i s >>= \s' -> walk iss ((i, s') : sofar)
modify :: Int -> SV -> IO SV
modify i s = svToSV st $ svIte (svEqualWithConsts (nodeIdToSVal ak i, i `Map.lookup` consts) aInfo) b (swToSVal s)
Right . IMap.fromAscList <$> walk (IMap.toAscList memoTable) []
case cont of
Left j -> return j
Right mt -> do
newMemoTable <- R.newIORef $ IMap.insert addressNodeId val mt
let j = FArrayIndex $ IMap.size fArrMap
upd = IMap.insert (unFArrayIndex j) (aUi, newMemoTable)
j `seq` modifyState st rFArrayMap upd (return ())
return j
mergeSFunArr :: SVal -> SFunArr -> SFunArr -> SFunArr
mergeSFunArr t array1@(SFunArr ainfo@(sourceKind, targetKind) a) array2@(SFunArr binfo b)
| ainfo /= binfo
= error $ "Data.SBV.mergeSFunArr: Impossible happened, merging incompatbile arrays: " ++ show (ainfo, binfo)
| Just test <- svAsBool t
= if test then array1 else array2
| True
= SFunArr ainfo $ cache c
where c st = do ai <- uncacheFAI a st
bi <- uncacheFAI b st
constMap <- R.readIORef (rconstMap st)
let consts = Map.fromList [(i, cv) | (cv, SV _ (NodeId i)) <- Map.toList constMap]
if unFArrayIndex ai == unFArrayIndex bi
then return ai
else do fArrMap <- R.readIORef (rFArrayMap st)
case (unFArrayIndex ai `IMap.lookup` fArrMap, unFArrayIndex bi `IMap.lookup` fArrMap) of
(Nothing, _) -> error $ "Data.SBV.mergeSFunArr: Impossible happened while trying to access SFunArray, can't find index: " ++ show ai
(_, Nothing) -> error $ "Data.SBV.mergeSFunArr: Impossible happened while trying to access SFunArray, can't find index: " ++ show bi
(Just (aUi, raCache), Just (bUi, rbCache)) -> do
aMemo <- R.readIORef raCache
bMemo <- R.readIORef rbCache
let aMemoT = IMap.toAscList aMemo
bMemoT = IMap.toAscList bMemo
gen :: (SVal -> SVal) -> Int -> [(Int, SV)] -> IO SV
gen mk k choices = svToSV st $ walk choices
where kInfo = (nodeIdToSVal sourceKind k, k `Map.lookup` consts)
walk :: [(Int, SV)] -> SVal
walk [] = mk (fst kInfo)
walk ((i, v) : ivs) = svIte (svEqualWithConsts (nodeIdToSVal sourceKind i, i `Map.lookup` consts) kInfo)
(swToSVal v)
(walk ivs)
fill :: Int -> SV -> SV -> IMap.IntMap SV -> IO (IMap.IntMap SV)
fill k (SV _ (NodeId ni1)) (SV _ (NodeId ni2)) m = do v <- svToSV st (svIte t sval1 sval2)
return $ IMap.insert k v m
where sval1 = nodeIdToSVal targetKind ni1
sval2 = nodeIdToSVal targetKind ni2
merge [] [] sofar = return sofar
merge ((k1, v1) : as) [] sofar = gen bUi k1 bMemoT >>= \v2 -> fill k1 v1 v2 sofar >>= merge as []
merge [] ((k2, v2) : bs) sofar = gen aUi k2 aMemoT >>= \v1 -> fill k2 v1 v2 sofar >>= merge [] bs
merge ass@((k1, v1) : as) bss@((k2, v2) : bs) sofar
| k1 < k2 = gen bUi k1 bMemoT >>= \nv2 -> fill k1 v1 nv2 sofar >>= merge as bss
| k1 > k2 = gen aUi k2 aMemoT >>= \nv1 -> fill k2 nv1 v2 sofar >>= merge ass bs
| True = fill k1 v1 v2 sofar >>= merge as bs
mergedMap <- merge aMemoT bMemoT IMap.empty
memoMerged <- R.newIORef mergedMap
let mkUninitialized i = svIte t (aUi i) (bUi i)
let j = FArrayIndex $ IMap.size fArrMap
upd = IMap.insert (unFArrayIndex j) (mkUninitialized, memoMerged)
j `seq` modifyState st rFArrayMap upd (return ())
return j
newSFunArr :: State -> (Kind, Kind) -> (Int -> String) -> Maybe SVal -> IO SFunArr
newSFunArr st (ak, bk) mkNm mbDef = do
fArrMap <- R.readIORef (rFArrayMap st)
memoTable <- R.newIORef IMap.empty
let j = FArrayIndex $ IMap.size fArrMap
nm = mkNm (unFArrayIndex j)
mkUninitialized i = case mbDef of
Just def -> def
_ -> svUninterpreted bk (nm ++ "_uninitializedRead") Nothing [i]
upd = IMap.insert (unFArrayIndex j) (mkUninitialized, memoTable)
registerLabel "SFunArray declaration" st nm
j `seq` modifyState st rFArrayMap upd (return ())
return $ SFunArr (ak, bk) $ cache $ const $ return j
noUnint :: (Maybe Int, String) -> a
noUnint x = error $ "Unexpected operation called on uninterpreted/enumerated value: " ++ show x
noUnint2 :: (Maybe Int, String) -> (Maybe Int, String) -> a
noUnint2 x y = error $ "Unexpected binary operation called on uninterpreted/enumerated values: " ++ show (x, y)
noCharLift :: Char -> a
noCharLift x = error $ "Unexpected operation called on char: " ++ show x
noStringLift :: String -> a
noStringLift x = error $ "Unexpected operation called on string: " ++ show x
noCharLift2 :: Char -> Char -> a
noCharLift2 x y = error $ "Unexpected binary operation called on chars: " ++ show (x, y)
noStringLift2 :: String -> String -> a
noStringLift2 x y = error $ "Unexpected binary operation called on strings: " ++ show (x, y)
liftSym1 :: (State -> Kind -> SV -> IO SV) -> (AlgReal -> AlgReal) -> (Integer -> Integer) -> (Float -> Float) -> (Double -> Double) -> SVal -> SVal
liftSym1 _ opCR opCI opCF opCD (SVal k (Left a)) = SVal k . Left $! mapCV opCR opCI opCF opCD noCharLift noStringLift noUnint a
liftSym1 opS _ _ _ _ a@(SVal k _) = SVal k $ Right $ cache c
where c st = do sva <- svToSV st a
opS st k sva
liftSV2 :: (State -> Kind -> SV -> SV -> IO SV) -> Kind -> SVal -> SVal -> Cached SV
liftSV2 opS k a b = cache c
where c st = do sw1 <- svToSV st a
sw2 <- svToSV st b
opS st k sw1 sw2
liftSym2 :: (State -> Kind -> SV -> SV -> IO SV)
-> (CV -> CV -> Bool)
-> (AlgReal -> AlgReal -> AlgReal)
-> (Integer -> Integer -> Integer)
-> (Float -> Float -> Float)
-> (Double -> Double -> Double)
-> SVal -> SVal -> SVal
liftSym2 _ okCV opCR opCI opCF opCD (SVal k (Left a)) (SVal _ (Left b)) | okCV a b = SVal k . Left $! mapCV2 opCR opCI opCF opCD noCharLift2 noStringLift2 noUnint2 a b
liftSym2 opS _ _ _ _ _ a@(SVal k _) b = SVal k $ Right $ liftSV2 opS k a b
liftSym2B :: (State -> Kind -> SV -> SV -> IO SV)
-> (CV -> CV -> Bool)
-> (AlgReal -> AlgReal -> Bool)
-> (Integer -> Integer -> Bool)
-> (Float -> Float -> Bool)
-> (Double -> Double -> Bool)
-> (Char -> Char -> Bool)
-> (String -> String -> Bool)
-> ([CVal] -> [CVal] -> Bool)
-> ([CVal] -> [CVal] -> Bool)
-> (Maybe CVal -> Maybe CVal -> Bool)
-> (Either CVal CVal -> Either CVal CVal -> Bool)
-> ((Maybe Int, String) -> (Maybe Int, String) -> Bool)
-> SVal -> SVal -> SVal
liftSym2B _ okCV opCR opCI opCF opCD opCC opCS opCSeq opCTup opCMaybe opCEither opUI (SVal _ (Left a)) (SVal _ (Left b)) | okCV a b = svBool (liftCV2 opCR opCI opCF opCD opCC opCS opCSeq opCTup opCMaybe opCEither opUI a b)
liftSym2B opS _ _ _ _ _ _ _ _ _ _ _ _ a b = SVal KBool $ Right $ liftSV2 opS KBool a b
mkSymOpSC :: (SV -> SV -> Maybe SV) -> Op -> State -> Kind -> SV -> SV -> IO SV
mkSymOpSC shortCut op st k a b = maybe (newExpr st k (SBVApp op [a, b])) return (shortCut a b)
mkSymOp :: Op -> State -> Kind -> SV -> SV -> IO SV
mkSymOp = mkSymOpSC (const (const Nothing))
mkSymOp1SC :: (SV -> Maybe SV) -> Op -> State -> Kind -> SV -> IO SV
mkSymOp1SC shortCut op st k a = maybe (newExpr st k (SBVApp op [a])) return (shortCut a)
mkSymOp1 :: Op -> State -> Kind -> SV -> IO SV
mkSymOp1 = mkSymOp1SC (const Nothing)
eqOpt :: SV -> SV -> SV -> Maybe SV
eqOpt w x y = case swKind x of
KFloat -> Nothing
KDouble -> Nothing
_ -> if x == y then Just w else Nothing
uiLift :: String -> (Int -> Int -> Bool) -> (Maybe Int, String) -> (Maybe Int, String) -> Bool
uiLift _ cmp (Just i, _) (Just j, _) = i `cmp` j
uiLift w _ a b = error $ "Data.SBV.Core.Operations: Impossible happened while trying to lift " ++ w ++ " over " ++ show (a, b)
isConcreteZero :: SVal -> Bool
isConcreteZero (SVal _ (Left (CV _ (CInteger n)))) = n == 0
isConcreteZero (SVal KReal (Left (CV KReal (CAlgReal v)))) = isExactRational v && v == 0
isConcreteZero _ = False
isConcreteOne :: SVal -> Bool
isConcreteOne (SVal _ (Left (CV _ (CInteger 1)))) = True
isConcreteOne (SVal KReal (Left (CV KReal (CAlgReal v)))) = isExactRational v && v == 1
isConcreteOne _ = False
isConcreteOnes :: SVal -> Bool
isConcreteOnes (SVal _ (Left (CV (KBounded b w) (CInteger n)))) = n == if b then -1 else bit w - 1
isConcreteOnes (SVal _ (Left (CV KUnbounded (CInteger n)))) = n == -1
isConcreteOnes (SVal _ (Left (CV KBool (CInteger n)))) = n == 1
isConcreteOnes _ = False
isConcreteMax :: SVal -> Bool
isConcreteMax (SVal _ (Left (CV (KBounded False w) (CInteger n)))) = n == bit w - 1
isConcreteMax (SVal _ (Left (CV (KBounded True w) (CInteger n)))) = n == bit (w - 1) - 1
isConcreteMax (SVal _ (Left (CV KBool (CInteger n)))) = n == 1
isConcreteMax _ = False
isConcreteMin :: SVal -> Bool
isConcreteMin (SVal _ (Left (CV (KBounded False _) (CInteger n)))) = n == 0
isConcreteMin (SVal _ (Left (CV (KBounded True w) (CInteger n)))) = n == - bit (w - 1)
isConcreteMin (SVal _ (Left (CV KBool (CInteger n)))) = n == 0
isConcreteMin _ = False
rationalCheck :: CV -> CV -> Bool
rationalCheck a b = case (cvVal a, cvVal b) of
(CAlgReal x, CAlgReal y) -> isExactRational x && isExactRational y
_ -> True
nonzeroCheck :: CV -> CV -> Bool
nonzeroCheck _ b = cvVal b /= CInteger 0
rationalSBVCheck :: SVal -> SVal -> Bool
rationalSBVCheck (SVal KReal (Left a)) (SVal KReal (Left b)) = rationalCheck a b
rationalSBVCheck _ _ = True
noReal :: String -> AlgReal -> AlgReal -> AlgReal
noReal o a b = error $ "SBV.AlgReal." ++ o ++ ": Unexpected arguments: " ++ show (a, b)
noFloat :: String -> Float -> Float -> Float
noFloat o a b = error $ "SBV.Float." ++ o ++ ": Unexpected arguments: " ++ show (a, b)
noDouble :: String -> Double -> Double -> Double
noDouble o a b = error $ "SBV.Double." ++ o ++ ": Unexpected arguments: " ++ show (a, b)
noRealUnary :: String -> AlgReal -> AlgReal
noRealUnary o a = error $ "SBV.AlgReal." ++ o ++ ": Unexpected argument: " ++ show a
noFloatUnary :: String -> Float -> Float
noFloatUnary o a = error $ "SBV.Float." ++ o ++ ": Unexpected argument: " ++ show a
noDoubleUnary :: String -> Double -> Double
noDoubleUnary o a = error $ "SBV.Double." ++ o ++ ": Unexpected argument: " ++ show a
{-# ANN svIte ("HLint: ignore Eta reduce" :: String) #-}
{-# ANN svLazyIte ("HLint: ignore Eta reduce" :: String) #-}
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}