sbv-14.1: Data/SBV/List.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.SBV.List
-- Copyright : (c) Joel Burget
-- Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- A collection of list utilities, useful when working with symbolic lists.
-- To the extent possible, the functions in this module follow those of "Data.List"
-- so importing qualified is the recommended workflow. Also, it is recommended
-- you use the @OverloadedLists@ and @OverloadedStrings@ extensions to allow literal
-- lists and strings to be used as symbolic literals.
--
-- You can find proofs of many list related properties in "Data.SBV.TP.List".
-----------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wall -Werror -Wno-orphans #-}
module Data.SBV.List (
-- * Length, emptiness
length, null
-- * Deconstructing/Reconstructing
, nil, (.:), snoc, head, tail, uncons, init, last, singleton, listToListAt, elemAt, (!!), implode, concat, (++)
-- * Case analysis (for sCase quasi-quoter)
, list
-- * Containment
, elem, notElem, isInfixOf, isSuffixOf, isPrefixOf
-- * List equality
, listEq
-- * Sublists
, take, drop, splitAt, subList, replace, indexOf, offsetIndexOf
-- * Reverse
, reverse
-- * Mapping
, map, concatMap
-- * Difference
, (\\)
-- * Folding
, foldl, foldr
-- * Zipping
, zip, zipWith
-- * Lookup
, lookup
-- * Filtering
, filter, partition, takeWhile, dropWhile
-- * Predicate transformers
, all, any, and, or
-- * Generators
, replicate, inits, tails
-- * Sum and product
, sum, product
-- * Minimum and maximum of a list
, minimum, maximum
-- * Conversion between strings and naturals
, strToNat, natToStr
-- * Symbolic enumerations
, EnumSymbolic(..)
) where
import Prelude hiding (head, tail, init, last, length, take, drop, splitAt, concat, null, elem,
notElem, reverse, (++), (!!), map, concatMap, foldl, foldr, zip, zipWith, filter,
all, any, and, or, replicate, fst, snd, sum, product, Enum(..), lookup,
takeWhile, dropWhile, minimum, maximum)
import qualified Prelude as P
import Data.SBV.Core.Kind
import Data.SBV.Core.Data
import Data.SBV.Core.Model
import Data.SBV.Core.SizedFloats
import Data.SBV.Core.Floating
import Data.SBV.SCase (sCase)
import Data.SBV.Tuple
import Data.Maybe (isNothing, catMaybes)
import qualified Data.Char as C
import Data.List (genericLength, genericIndex, genericDrop, genericTake, genericReplicate)
import qualified Data.List as L (inits, tails, isSuffixOf, isPrefixOf, isInfixOf, partition, (\\))
import Data.Proxy
#ifdef DOCTEST
-- $setup
-- >>> import Prelude hiding (head, tail, init, last, length, take, drop, concat, null, elem, notElem, reverse, (++), (!!), map, foldl, foldr, zip, zipWith, filter, all, any, replicate, lookup, splitAt, concatMap, and, or, sum, product, takeWhile, dropWhile, minimum, maximum)
-- >>> import qualified Prelude as P(map)
-- >>> import Data.SBV
-- >>> :set -XDataKinds
-- >>> :set -XOverloadedLists
-- >>> :set -XOverloadedStrings
-- >>> :set -XScopedTypeVariables
-- >>> :set -XTypeApplications
-- >>> :set -XQuasiQuotes
#endif
-- | Length of a list.
--
-- >>> sat $ \(l :: SList Word16) -> length l .== 2
-- Satisfiable. Model:
-- s0 = [0,0] :: [Word16]
-- >>> sat $ \(l :: SList Word16) -> length l .< 0
-- Unsatisfiable
-- >>> prove $ \(l1 :: SList Word16) (l2 :: SList Word16) -> length l1 + length l2 .== length (l1 ++ l2)
-- Q.E.D.
-- >>> sat $ \(s :: SString) -> length s .== 2
-- Satisfiable. Model:
-- s0 = "BA" :: String
-- >>> sat $ \(s :: SString) -> length s .< 0
-- Unsatisfiable
-- >>> prove $ \(s1 :: SString) s2 -> length s1 + length s2 .== length (s1 ++ s2)
-- Q.E.D.
length :: forall a. SymVal a => SList a -> SInteger
length = lift1 False (SeqLen (kindOf (Proxy @a))) (Just (fromIntegral . P.length))
-- | @`null` s@ is True iff the list is empty
--
-- >>> prove $ \(l :: SList Word16) -> null l .<=> length l .== 0
-- Q.E.D.
-- >>> prove $ \(l :: SList Word16) -> null l .<=> l .== []
-- Q.E.D.
-- >>> prove $ \(s :: SString) -> null s .<=> length s .== 0
-- Q.E.D.
-- >>> prove $ \(s :: SString) -> null s .<=> s .== ""
-- Q.E.D.
null :: SymVal a => SList a -> SBool
null l
| Just cs <- unliteral l
= literal (P.null cs)
| True
= length l .== 0
-- | @`head`@ returns the first element of a list. Unspecified if the list is empty.
--
-- >>> prove $ \c -> head [c] .== (c :: SInteger)
-- Q.E.D.
-- >>> prove $ \c -> c .== literal 'A' .=> ([c] :: SString) .== "A"
-- Q.E.D.
-- >>> prove $ \(c :: SChar) -> length ([c] :: SString) .== 1
-- Q.E.D.
-- >>> prove $ \(c :: SChar) -> head ([c] :: SString) .== c
-- Q.E.D.
head :: SymVal a => SList a -> SBV a
head = (`elemAt` 0)
-- | @`tail`@ returns the tail of a list. Unspecified if the list is empty.
--
-- >>> prove $ \(h :: SInteger) t -> tail ([h] ++ t) .== t
-- Q.E.D.
-- >>> prove $ \(l :: SList Integer) -> length l .> 0 .=> length (tail l) .== length l - 1
-- Q.E.D.
-- >>> prove $ \(l :: SList Integer) -> sNot (null l) .=> [head l] ++ tail l .== l
-- Q.E.D.
-- >>> prove $ \(h :: SChar) s -> tail ([h] ++ s) .== s
-- Q.E.D.
-- >>> prove $ \(s :: SString) -> length s .> 0 .=> length (tail s) .== length s - 1
-- Q.E.D.
-- >>> prove $ \(s :: SString) -> sNot (null s) .=> [head s] ++ tail s .== s
-- Q.E.D.
tail :: SymVal a => SList a -> SList a
tail l
| Just (_:cs) <- unliteral l
= literal cs
| True
= subList l 1 (length l - 1)
-- | @`uncons`@ returns the pair of the head and tail. Unspecified if the list is empty.
--
-- >>> prove $ \(x :: SInteger) xs -> uncons (x .: xs) .== (x, xs)
-- Q.E.D.
uncons :: SymVal a => SList a -> (SBV a, SList a)
uncons l = (head l, tail l)
-- | Case analysis on a symbolic list. If the list is empty, return the first argument.
-- Otherwise, apply the second argument to the head and tail of the list.
--
-- >>> list (0 :: SInteger) (\h _ -> h) ([] :: SList Integer)
-- 0 :: SInteger
-- >>> list (0 :: SInteger) (\h _ -> h) ([3, 4, 5] :: SList Integer)
-- 3 :: SInteger
-- >>> prove $ \(l :: SList Integer) -> null l .|| list sFalse (\_ _ -> sTrue) l
-- Q.E.D.
list :: (SymVal a, SymVal b) => SBV b -> (SBV a -> SList a -> SBV b) -> SList a -> SBV b
list nilCase consCase xs = [sCase| xs of
[] -> nilCase
h:ts -> consCase h ts
|]
-- | @`init`@ returns all but the last element of the list. Unspecified if the list is empty.
--
-- >>> prove $ \(h :: SInteger) t -> init (t ++ [h]) .== t
-- Q.E.D.
-- >>> prove $ \(c :: SChar) t -> init (t ++ [c]) .== t
-- Q.E.D.
init :: SymVal a => SList a -> SList a
init l
| Just cs@(_:_) <- unliteral l
= literal $ P.init cs
| True
= subList l 0 (length l - 1)
-- | @`last`@ returns the last element of the list. Unspecified if the list is empty.
--
-- >>> prove $ \(l :: SInteger) i -> last (i ++ [l]) .== l
-- Q.E.D.
last :: SymVal a => SList a -> SBV a
last l = l `elemAt` (length l - 1)
-- | @`singleton` x@ is the list of length 1 that contains the only value @x@.
--
-- >>> prove $ \(x :: SInteger) -> head [x] .== x
-- Q.E.D.
-- >>> prove $ \(x :: SInteger) -> length [x] .== 1
-- Q.E.D.
singleton :: forall a. SymVal a => SBV a -> SList a
singleton = lift1 False (SeqUnit (kindOf (Proxy @a))) (Just (: []))
-- | @`listToListAt` l offset@. List of length 1 at @offset@ in @l@. Unspecified if
-- index is out of bounds.
--
-- >>> prove $ \(l1 :: SList Integer) l2 -> listToListAt (l1 ++ l2) (length l1) .== listToListAt l2 0
-- Q.E.D.
-- >>> sat $ \(l :: SList Word16) -> length l .>= 2 .&& listToListAt l 0 ./= listToListAt l (length l - 1)
-- Satisfiable. Model:
-- s0 = [0,32] :: [Word16]
listToListAt :: SymVal a => SList a -> SInteger -> SList a
listToListAt s offset = subList s offset 1
-- | @`elemAt` l i@ is the value stored at location @i@, starting at 0. Unspecified if
-- index is out of bounds.
--
-- >>> prove $ \i -> i `inRange` (0, 4) .=> [1,1,1,1,1] `elemAt` i .== (1::SInteger)
-- Q.E.D.
-- >>> prove $ \i -> i .>= 0 .&& i .<= 4 .=> "AAAAA" `elemAt` i .== literal 'A'
-- Q.E.D.
elemAt :: forall a. SymVal a => SList a -> SInteger -> SBV a
elemAt l i
| Just xs <- unliteral l, Just ci <- unliteral i, ci >= 0, ci < genericLength xs, let x = xs `genericIndex` ci
= literal x
| True
= lift2 False (SeqNth (kindOf (Proxy @a))) Nothing l i
-- | Short cut for 'elemAt'
--
-- >>> prove $ \(xs :: SList Integer) i -> xs !! i .== xs `elemAt` i
-- Q.E.D.
(!!) :: SymVal a => SList a -> SInteger -> SBV a
(!!) = elemAt
-- | @`implode` es@ is the list of length @|es|@ containing precisely those
-- elements. Note that there is no corresponding function @explode@, since
-- we wouldn't know the length of a symbolic list.
--
-- >>> prove $ \(e1 :: SInteger) e2 e3 -> length (implode [e1, e2, e3]) .== 3
-- Q.E.D.
-- >>> prove $ \(e1 :: SInteger) e2 e3 -> P.map (elemAt (implode [e1, e2, e3])) (P.map literal [0 .. 2]) .== [e1, e2, e3]
-- Q.E.D.
-- >>> prove $ \(c1 :: SChar) c2 c3 -> length (implode [c1, c2, c3]) .== 3
-- Q.E.D.
-- >>> prove $ \(c1 :: SChar) c2 c3 -> P.map (elemAt (implode [c1, c2, c3])) (P.map literal [0 .. 2]) .== [c1, c2, c3]
-- Q.E.D.
implode :: SymVal a => [SBV a] -> SList a
implode = P.foldr ((++) . \x -> [x]) (literal [])
-- | Append an element
--
-- >>> [1, 2, 3 :: SInteger] `snoc` 4 `snoc` 5 `snoc` 6
-- [1,2,3,4,5,6] :: [SInteger]
snoc :: SymVal a => SList a -> SBV a -> SList a
as `snoc` a = as ++ [a]
-- nil is defined in Data.SBV.Core.Data and re-exported here.
-- | Append two lists.
--
-- >>> sat $ \x y (z :: SList Integer) -> length x .== 5 .&& length y .== 1 .&& x ++ y ++ z .== [sEnum|1 .. 12|]
-- Satisfiable. Model:
-- s0 = [1,2,3,4,5] :: [Integer]
-- s1 = [6] :: [Integer]
-- s2 = [7,8,9,10,11,12] :: [Integer]
-- >>> sat $ \(x :: SString) y z -> length x .== 5 .&& length y .== 1 .&& x ++ y ++ z .== "Hello world!"
-- Satisfiable. Model:
-- s0 = "Hello" :: String
-- s1 = " " :: String
-- s2 = "world!" :: String
infixr 5 ++
(++) :: forall a. SymVal a => SList a -> SList a -> SList a
x ++ y | isConcretelyEmpty x = y
| isConcretelyEmpty y = x
| True = lift2 False (SeqConcat (kindOf (Proxy @a))) (Just (P.++)) x y
-- | @`elem` e l@. Does @l@ contain the element @e@?
--
-- >>> prove $ \(xs :: SList Integer) x -> x `elem` xs .=> length xs .>= 1
-- Q.E.D.
elem :: (Eq a, SymVal a) => SBV a -> SList a -> SBool
e `elem` l = [e] `isInfixOf` l
-- | @`notElem` e l@. Does @l@ not contain the element @e@?
--
-- >>> prove $ \(x :: SList Integer) -> x `notElem` []
-- Q.E.D.
notElem :: (Eq a, SymVal a) => SBV a -> SList a -> SBool
e `notElem` l = sNot (e `elem` l)
-- | @`isInfixOf` sub l@. Does @l@ contain the subsequence @sub@?
--
-- >>> prove $ \(l1 :: SList Integer) l2 l3 -> l2 `isInfixOf` (l1 ++ l2 ++ l3)
-- Q.E.D.
-- >>> prove $ \(l1 :: SList Integer) l2 -> l1 `isInfixOf` l2 .&& l2 `isInfixOf` l1 .<=> l1 .== l2
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 s3 -> s2 `isInfixOf` (s1 ++ s2 ++ s3)
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 -> s1 `isInfixOf` s2 .&& s2 `isInfixOf` s1 .<=> s1 .== s2
-- Q.E.D.
isInfixOf :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SBool
sub `isInfixOf` l
| isConcretelyEmpty sub
= literal True
| True
= lift2 True (SeqContains (kindOf (Proxy @a))) (Just (flip L.isInfixOf)) l sub -- NB. flip, since `SeqContains` takes args in rev order!
-- | @`isPrefixOf` pre l@. Is @pre@ a prefix of @l@?
--
-- >>> prove $ \(l1 :: SList Integer) l2 -> l1 `isPrefixOf` (l1 ++ l2)
-- Q.E.D.
-- >>> prove $ \(l1 :: SList Integer) l2 -> l1 `isPrefixOf` l2 .=> subList l2 0 (length l1) .== l1
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 -> s1 `isPrefixOf` (s1 ++ s2)
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 -> s1 `isPrefixOf` s2 .=> subList s2 0 (length s1) .== s1
-- Q.E.D.
isPrefixOf :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SBool
pre `isPrefixOf` l
| isConcretelyEmpty pre
= literal True
| True
= lift2 True (SeqPrefixOf (kindOf (Proxy @a))) (Just L.isPrefixOf) pre l
-- | @listEq@ is a variant of equality that you can use for lists of floats. It respects @NaN /= NaN@. The reason
-- we do not do this automatically is that it complicates proof objectives usually, as it does not simply resolve to
-- the native equality check.
--
-- NB. We case-split on @x@ only and use a guard for @y@ being empty, rather than case-splitting on the
-- tuple @(x, y)@. A 4-way tuple match produces a larger and\/or\/not SMTLib tree that z3 struggles with.
listEq :: forall a. SymVal a => SList a -> SList a -> SBool
listEq
| containsFloats (kindOf (Proxy @a))
= smtFunction "listEq"
$ \x y -> [sCase| x of
[] -> null y
a:xs -> case y of
[] -> sFalse
b : ys -> a .== b .&& xs `listEq` ys
|]
| True
= (.==)
-- | @`isSuffixOf` suf l@. Is @suf@ a suffix of @l@?
--
-- >>> prove $ \(l1 :: SList Word16) l2 -> l2 `isSuffixOf` (l1 ++ l2)
-- Q.E.D.
-- >>> prove $ \(l1 :: SList Word16) l2 -> l1 `isSuffixOf` l2 .=> subList l2 (length l2 - length l1) (length l1) .== l1
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 -> s2 `isSuffixOf` (s1 ++ s2)
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 -> s1 `isSuffixOf` s2 .=> subList s2 (length s2 - length s1) (length s1) .== s1
-- Q.E.D.
isSuffixOf :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SBool
suf `isSuffixOf` l
| isConcretelyEmpty suf
= literal True
| True
= lift2 True (SeqSuffixOf (kindOf (Proxy @a))) (Just L.isSuffixOf) suf l
-- | @`take` len l@. Corresponds to Haskell's `take` on symbolic lists.
--
-- >>> prove $ \(l :: SList Integer) i -> i .>= 0 .=> length (take i l) .<= i
-- Q.E.D.
-- >>> prove $ \(s :: SString) i -> i .>= 0 .=> length (take i s) .<= i
-- Q.E.D.
take :: SymVal a => SInteger -> SList a -> SList a
take i l = ite (i .<= 0) (literal [])
$ ite (i .>= length l) l
$ subList l 0 i
-- | @`drop` len s@. Corresponds to Haskell's `drop` on symbolic-lists.
--
-- >>> prove $ \(l :: SList Word16) i -> length (drop i l) .<= length l
-- Q.E.D.
-- >>> prove $ \(l :: SList Word16) i -> take i l ++ drop i l .== l
-- Q.E.D.
-- >>> prove $ \(s :: SString) i -> length (drop i s) .<= length s
-- Q.E.D.
-- >>> prove $ \(s :: SString) i -> take i s ++ drop i s .== s
-- Q.E.D.
drop :: SymVal a => SInteger -> SList a -> SList a
drop i s = ite (i .>= ls) (literal [])
$ ite (i .<= 0) s
$ subList s i (ls - i)
where ls = length s
-- | @splitAt n xs = (take n xs, drop n xs)@
--
-- >>> prove $ \n (xs :: SList Integer) -> let (l, r) = splitAt n xs in l ++ r .== xs
-- Q.E.D.
splitAt :: SymVal a => SInteger -> SList a -> (SList a, SList a)
splitAt n xs = (take n xs, drop n xs)
-- | @`subList` s offset len@ is the sublist of @s@ at offset @offset@ with length @len@.
-- This function is under-specified when the offset is outside the range of positions in @s@ or @len@
-- is negative or @offset+len@ exceeds the length of @s@.
--
-- >>> prove $ \(l :: SList Integer) i -> i .>= 0 .&& i .< length l .=> subList l 0 i ++ subList l i (length l - i) .== l
-- Q.E.D.
-- >>> sat $ \i j -> subList [sEnum|1..5|] i j .== [sEnum|2..4::SInteger|]
-- Satisfiable. Model:
-- s0 = 1 :: Integer
-- s1 = 3 :: Integer
-- >>> sat $ \i j -> subList [sEnum|1..5|] i j .== [sEnum|6..7::SInteger|]
-- Unsatisfiable
-- >>> prove $ \(s1 :: SString) (s2 :: SString) -> subList (s1 ++ s2) (length s1) 1 .== subList s2 0 1
-- Q.E.D.
-- >>> sat $ \(s :: SString) -> length s .>= 2 .&& subList s 0 1 ./= subList s (length s - 1) 1
-- Satisfiable. Model:
-- s0 = "AB" :: String
-- >>> prove $ \(s :: SString) i -> i .>= 0 .&& i .< length s .=> subList s 0 i ++ subList s i (length s - i) .== s
-- Q.E.D.
-- >>> sat $ \i j -> subList "hello" i j .== ("ell" :: SString)
-- Satisfiable. Model:
-- s0 = 1 :: Integer
-- s1 = 3 :: Integer
-- >>> sat $ \i j -> subList "hell" i j .== ("no" :: SString)
-- Unsatisfiable
subList :: forall a. SymVal a => SList a -> SInteger -> SInteger -> SList a
subList l offset len
| Just c <- unliteral l -- a constant list
, Just o <- unliteral offset -- a constant offset
, Just sz <- unliteral len -- a constant length
, let lc = genericLength c -- length of the list
, let valid x = x >= 0 && x <= lc -- predicate that checks valid point
, valid o -- offset is valid
, sz >= 0 -- length is not-negative
, valid $ o + sz -- we don't overrun
= literal $ genericTake sz $ genericDrop o c
| True -- either symbolic, or something is out-of-bounds
= lift3 False (SeqSubseq (kindOf (Proxy @a))) Nothing l offset len
-- | @`replace` l src dst@. Replace the first occurrence of @src@ by @dst@ in @s@
--
-- >>> prove $ \l -> replace [sEnum|1..5|] l [sEnum|6..10|] .== [sEnum|6..10|] .=> l .== [sEnum|1..5::SWord8|]
-- Q.E.D.
-- >>> prove $ \(l1 :: SList Integer) l2 l3 -> length l2 .> length l1 .=> replace l1 l2 l3 .== l1
-- Q.E.D.
-- >>> prove $ \(s :: SString) -> replace "hello" s "world" .== "world" .=> s .== "hello"
-- Q.E.D.
-- >>> prove $ \(s1 :: SString) s2 s3 -> length s2 .> length s1 .=> replace s1 s2 s3 .== s1
-- Q.E.D.
replace :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SList a -> SList a
replace l src dst
| Just b <- unliteral src, P.null b -- If src is null, simply prepend
= dst ++ l
| eqCheckIsObjectEq ka
, Just a <- unliteral l
, Just b <- unliteral src
, Just c <- unliteral dst
= literal $ walk a b c
| True
= lift3 True (SeqReplace ka) Nothing l src dst
where walk haystack needle newNeedle = go haystack -- note that needle is guaranteed non-empty here.
where go [] = []
go i@(c:cs)
| needle `L.isPrefixOf` i = newNeedle P.++ genericDrop (genericLength needle :: Integer) i
| True = c : go cs
ka = kindOf (Proxy @a)
-- | @`indexOf` l sub@. Retrieves first position of @sub@ in @l@, @-1@ if there are no occurrences.
-- Equivalent to @`offsetIndexOf` l sub 0@.
--
-- >>> prove $ \(l1 :: SList Word16) l2 -> length l2 .> length l1 .=> indexOf l1 l2 .== -1
-- Q.E.D.
-- >>> prove $ \s1 s2 -> length s2 .> length s1 .=> indexOf s1 s2 .== -1
-- Q.E.D.
indexOf :: (Eq a, SymVal a) => SList a -> SList a -> SInteger
indexOf s sub = offsetIndexOf s sub 0
-- | @`offsetIndexOf` l sub offset@. Retrieves first position of @sub@ at or
-- after @offset@ in @l@, @-1@ if there are no occurrences.
--
-- >>> prove $ \(l :: SList Int8) sub -> offsetIndexOf l sub 0 .== indexOf l sub
-- Q.E.D.
-- >>> prove $ \(l :: SList Int8) sub i -> i .>= length l .&& length sub .> 0 .=> offsetIndexOf l sub i .== -1
-- Q.E.D.
-- >>> prove $ \(l :: SList Int8) sub i -> i .> length l .=> offsetIndexOf l sub i .== -1
-- Q.E.D.
-- >>> prove $ \(s :: SString) sub -> offsetIndexOf s sub 0 .== indexOf s sub
-- Q.E.D.
-- >>> prove $ \(s :: SString) sub i -> i .>= length s .&& length sub .> 0 .=> offsetIndexOf s sub i .== -1
-- Q.E.D.
-- >>> prove $ \(s :: SString) sub i -> i .> length s .=> offsetIndexOf s sub i .== -1
-- Q.E.D.
offsetIndexOf :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SInteger -> SInteger
offsetIndexOf s sub offset
| eqCheckIsObjectEq ka
, Just c <- unliteral s -- a constant list
, Just n <- unliteral sub -- a constant search pattern
, Just o <- unliteral offset -- at a constant offset
, o >= 0, o <= genericLength c -- offset is good
= case [i | (i, t) <- P.zip [o ..] (L.tails (genericDrop o c)), n `L.isPrefixOf` t] of
(i:_) -> literal i
_ -> -1
| True
= lift3 True (SeqIndexOf ka) Nothing s sub offset
where ka = kindOf (Proxy @a)
-- | @`reverse` s@ reverses the sequence.
--
-- NB. We can define @reverse@ in terms of @foldl@ as: @foldl (\soFar elt -> [elt] ++ soFar) []@
-- But in my experiments, I found that this definition performs worse instead of the recursive definition
-- SBV generates for reverse calls. So we're keeping it intact.
--
-- >>> sat $ \(l :: SList Integer) -> reverse l .== literal [3, 2, 1]
-- Satisfiable. Model:
-- s0 = [1,2,3] :: [Integer]
-- >>> prove $ \(l :: SList Word32) -> reverse l .== [] .<=> null l
-- Q.E.D.
-- >>> sat $ \(l :: SString ) -> reverse l .== "321"
-- Satisfiable. Model:
-- s0 = "123" :: String
-- >>> prove $ \(l :: SString) -> reverse l .== "" .<=> null l
-- Q.E.D.
reverse :: forall a. SymVal a => SList a -> SList a
reverse l
| Just l' <- unliteral l
= literal (P.reverse l')
| True
= def l
where def = smtFunction "sbv.reverse"
$ \xs -> [sCase| xs of
[] -> []
h:ts -> def ts ++ [h]
|]
-- | A class of mappable functions. In SBV, we make a distinction between closures and regular functions, and
-- we instantiate this class appropriately so it can handle both cases.
class (SymVal a, SymVal b) => SMap func a b | func -> a b where
-- | Map a function (or a closure) over a symbolic list.
--
-- >>> map (+ (1 :: SInteger)) [sEnum|1 .. 5 :: SInteger|]
-- [2,3,4,5,6] :: [SInteger]
-- >>> map (+ (1 :: SWord 8)) [sEnum|1 .. 5 :: SWord 8|]
-- [2,3,4,5,6] :: [SWord8]
-- >>> map (\x -> [x] :: SList Integer) [sEnum|1 .. 3 :: SInteger|]
-- [[1],[2],[3]] :: [[SInteger]]
-- >>> import Data.SBV.Tuple
-- >>> map (\t -> t^._1 + t^._2) (literal [(x, y) | x <- [1..3], y <- [4..6]] :: SList (Integer, Integer))
-- [5,6,7,6,7,8,7,8,9] :: [SInteger]
--
-- Of course, SBV's 'map' can also be reused in reverse:
--
-- >>> sat $ \l -> map (+(1 :: SInteger)) l .== [1,2,3 :: SInteger]
-- Satisfiable. Model:
-- s0 = [0,1,2] :: [Integer]
map :: func -> SList a -> SList b
-- | Handle the concrete case of mapping. Used internally only.
concreteMap :: func -> (SBV a -> SBV b) -> SList a -> Maybe [b]
concreteMap _ f sas
| Just as <- unliteral sas
= case P.map (unliteral . f . literal) as of
bs | P.any isNothing bs -> Nothing
| True -> Just (catMaybes bs)
| True
= Nothing
-- | Mapping symbolic functions.
instance (SymVal a, SymVal b) => SMap (SBV a -> SBV b) a b where
-- | @`map` f s@ maps the operation on to sequence.
map f l
| Just concResult <- concreteMap f f l
= literal concResult
| True
= sbvMap l
where sbvMap = smtHOFunction "sbv.map" f
$ \xs -> [sCase| xs of
[] -> []
h : t -> f h .: sbvMap t
|]
-- | Mapping symbolic closures.
instance (SymVal env, SymVal a, SymVal b) => SMap (Closure (SBV env) (SBV a -> SBV b)) a b where
map cls@Closure{closureEnv, closureFun} l
| Just concResult <- concreteMap cls (closureFun closureEnv) l
= literal concResult
| True
= sbvMap (tuple (closureEnv, l))
where sbvMap = smtHOFunction "sbv.closureMap" closureFun
$ \envxs -> [sCase| envxs of
(_, []) -> []
(cEnv, h : t) -> closureFun cEnv h .: sbvMap (tuple (cEnv, t))
|]
-- | @concatMap f xs@ maps f over elements and concats the result.
--
-- >>> concatMap (\x -> [x, x] :: SList Integer) [sEnum|1 .. 3|]
-- [1,1,2,2,3,3] :: [SInteger]
concatMap :: (SMap func a [b], SymVal b) => func -> SList a -> SList b
concatMap f = concat . map f
-- | A class of left foldable functions. In SBV, we make a distinction between closures and regular functions, and
-- we instantiate this class appropriately so it can handle both cases.
class (SymVal a, SymVal b) => SFoldL func a b | func -> a b where
-- | @`foldl` f base s@ folds the from the left.
--
-- >>> foldl ((+) @SInteger) 0 [sEnum|1 .. 5|]
-- 15 :: SInteger
-- >>> foldl ((*) @SInteger) 1 [sEnum|1 .. 5|]
-- 120 :: SInteger
-- >>> foldl (\soFar elt -> [elt] ++ soFar) ([] :: SList Integer) [sEnum|1 .. 5|]
-- [5,4,3,2,1] :: [SInteger]
--
-- Again, we can use 'sbv.foldl' in the reverse too:
--
-- >>> sat $ \l -> foldl (\soFar elt -> [elt] ++ soFar) ([] :: SList Integer) l .== [5, 4, 3, 2, 1 :: SInteger]
-- Satisfiable. Model:
-- s0 = [1,2,3,4,5] :: [Integer]
foldl :: (SymVal a, SymVal b) => func -> SBV b -> SList a -> SBV b
-- | Handle the concrete case for folding left. Used internally only.
concreteFoldl :: func -> (SBV b -> SBV a -> SBV b) -> SBV b -> SList a -> Maybe b
concreteFoldl _ f sb sas
| Just b <- unliteral sb, Just as <- unliteral sas
= go b as
| True
= Nothing
where go b [] = Just b
go b (e:es) = case unliteral (literal b `f` literal e) of
Nothing -> Nothing
Just b' -> go b' es
-- | Folding left with symbolic functions.
instance (SymVal a, SymVal b) => SFoldL (SBV b -> SBV a -> SBV b) a b where
-- | @`foldl` f b s@ folds the sequence from the left.
foldl f base l
| Just concResult <- concreteFoldl f f base l
= literal concResult
| True
= sbvFoldl $ tuple (base, l)
where sbvFoldl = smtHOFunction "sbv.foldl" (uncurry f . untuple)
$ \exs -> [sCase| exs of
(e, []) -> e
(e, h : t) -> sbvFoldl (tuple (e `f` h, t))
|]
-- | Folding left with symbolic closures.
instance (SymVal env, SymVal a, SymVal b) => SFoldL (Closure (SBV env) (SBV b -> SBV a -> SBV b)) a b where
foldl cls@Closure{closureEnv, closureFun} base l
| Just concResult <- concreteFoldl cls (closureFun closureEnv) base l
= literal concResult
| True
= sbvFoldl $ tuple (closureEnv, base, l)
where sbvFoldl = smtHOFunction "sbv.closureFoldl" closureFun
$ \envxs -> [sCase| envxs of
(_, e, []) -> e
(cEnv, e, h : t) -> sbvFoldl (tuple (cEnv, closureFun closureEnv e h, t))
|]
-- | A class of right foldable functions. In SBV, we make a distinction between closures and regular functions, and
-- we instantiate this class appropriately so it can handle both cases.
class (SymVal a, SymVal b) => SFoldR func a b | func -> a b where
-- | @`foldr` f base s@ folds the from the right.
--
-- >>> foldr ((+) @SInteger) 0 [sEnum|1 .. 5|]
-- 15 :: SInteger
-- >>> foldr ((*) @SInteger) 1 [sEnum|1 .. 5|]
-- 120 :: SInteger
-- >>> foldr (\elt soFar -> soFar ++ [elt]) ([] :: SList Integer) [sEnum|1 .. 5|]
-- [5,4,3,2,1] :: [SInteger]
foldr :: func -> SBV b -> SList a -> SBV b
-- | Handle the concrete case for folding left. Used internally only.
concreteFoldr :: func -> (SBV a -> SBV b -> SBV b) -> SBV b -> SList a -> Maybe b
concreteFoldr _ f sb sas
| Just b <- unliteral sb, Just as <- unliteral sas
= go b as
| True
= Nothing
where go b [] = Just b
go b (e:es) = case go b es of
Nothing -> Nothing
Just res -> unliteral (literal e `f` literal res)
-- | Folding right with symbolic functions.
instance (SymVal a, SymVal b) => SFoldR (SBV a -> SBV b -> SBV b) a b where
-- | @`foldr` f base s@ folds the sequence from the right.
foldr f base l
| Just concResult <- concreteFoldr f f base l
= literal concResult
| True
= sbvFoldr $ tuple (base, l)
where sbvFoldr = smtHOFunction "sbv.foldr" (uncurry f . untuple)
$ \exs -> [sCase| exs of
(e, []) -> e
(e, h : t) -> h `f` sbvFoldr (tuple (e, t))
|]
-- | Folding right with symbolic closures.
instance (SymVal env, SymVal a, SymVal b) => SFoldR (Closure (SBV env) (SBV a -> SBV b -> SBV b)) a b where
foldr cls@Closure{closureEnv, closureFun} base l
| Just concResult <- concreteFoldr cls (closureFun closureEnv) base l
= literal concResult
| True
= sbvFoldr $ tuple (closureEnv, base, l)
where sbvFoldr = smtHOFunction "sbv.closureFoldr" closureFun
$ \envxs -> [sCase| envxs of
(_, e, []) -> e
(cEnv, e, h : t) -> closureFun closureEnv h (sbvFoldr (tuple (cEnv, e, t)))
|]
-- | @`zip` xs ys@ zips the lists to give a list of pairs. The length of the final list is
-- the minumum of the lengths of the given lists.
--
-- >>> zip [sEnum|1..10 :: SInteger|] [sEnum|11..20 :: SInteger|]
-- [(1,11),(2,12),(3,13),(4,14),(5,15),(6,16),(7,17),(8,18),(9,19),(10,20)] :: [(SInteger, SInteger)]
-- >>> import Data.SBV.Tuple
-- >>> foldr ((+) @SInteger) 0 (map (\t -> t^._1+t^._2::SInteger) (zip [sEnum|1..10|] [sEnum|10, 9..1|]))
-- 110 :: SInteger
zip :: forall a b. (SymVal a, SymVal b) => SList a -> SList b -> SList (a, b)
zip xs ys
| Just xs' <- unliteral xs, Just ys' <- unliteral ys
= literal $ P.zip xs' ys'
| True
= def xs ys
where def = smtFunction "sbv.zip"
$ \x y -> [sCase| tuple (x, y) of
([], _ ) -> []
(_, [] ) -> []
(a:as, b:bs) -> tuple (a, b) .: def as bs
|]
-- | A class of function that we can zip-with. In SBV, we make a distinction between closures and regular
-- functions, and we instantiate this class appropriately so it can handle both cases.
class (SymVal a, SymVal b, SymVal c) => SZipWith func a b c | func -> a b c where
-- | @`zipWith` f xs ys@ zips the lists to give a list of pairs, applying the function to each pair of elements.
-- The length of the final list is the minumum of the lengths of the given lists.
--
-- >>> zipWith ((+) @SInteger) ([sEnum|1..10::SInteger|]) ([sEnum|11..20::SInteger|])
-- [12,14,16,18,20,22,24,26,28,30] :: [SInteger]
-- >>> foldr ((+) @SInteger) 0 (zipWith ((+) @SInteger) [sEnum|1..10 :: SInteger|] [sEnum|10, 9..1 :: SInteger|])
-- 110 :: SInteger
zipWith :: func -> SList a -> SList b -> SList c
-- | Handle the concrete case of zipping. Used internally only.
concreteZipWith :: func -> (SBV a -> SBV b -> SBV c) -> SList a -> SList b -> Maybe [c]
concreteZipWith _ f sas sbs
| Just as <- unliteral sas, Just bs <- unliteral sbs
= go as bs
| True
= Nothing
where go [] _ = Just []
go _ [] = Just []
go (a:as) (b:bs) = (:) <$> unliteral (literal a `f` literal b) <*> go as bs
-- | Zipping with symbolic functions.
instance (SymVal a, SymVal b, SymVal c) => SZipWith (SBV a -> SBV b -> SBV c) a b c where
-- | @`zipWith`@ zips two sequences with a symbolic function.
zipWith f xs ys
| Just concResult <- concreteZipWith f f xs ys
= literal concResult
| True
= sbvZipWith $ tuple (xs, ys)
where sbvZipWith = smtHOFunction "sbv.zipWith" (uncurry f . untuple)
$ \asbs -> [sCase| asbs of
([], _ ) -> []
(_, [] ) -> []
(a:as, b:bs) -> f a b .: sbvZipWith (tuple (as, bs))
|]
-- | Zipping with closures.
instance (SymVal env, SymVal a, SymVal b, SymVal c) => SZipWith (Closure (SBV env) (SBV a -> SBV b -> SBV c)) a b c where
zipWith cls@Closure{closureEnv, closureFun} xs ys
| Just concResult <- concreteZipWith cls (closureFun closureEnv) xs ys
= literal concResult
| True
= sbvZipWith $ tuple (closureEnv, xs, ys)
where sbvZipWith = smtHOFunction "sbv.closureZipWith" closureFun
$ \envasbs -> [sCase| envasbs of
(_, [], _ ) -> []
(_, _, [] ) -> []
(cEnv, a:as, b:bs) -> closureFun cEnv a b .: sbvZipWith (tuple (cEnv, as, bs))
|]
-- | Concatenate list of lists.
--
-- >>> concat [[sEnum|1..3::SInteger|], [sEnum|4..7|], [sEnum|8..10|]]
-- [1,2,3,4,5,6,7,8,9,10] :: [SInteger]
concat :: forall a. SymVal a => SList [a] -> SList a
concat = foldr (++) []
-- | Check all elements satisfy the predicate.
--
-- >>> let isEven x = x `sMod` 2 .== 0
-- >>> all isEven [2, 4, 6, 8, 10 :: SInteger]
-- True
-- >>> all isEven [2, 4, 6, 1, 8, 10 :: SInteger]
-- False
all :: forall a. SymVal a => (SBV a -> SBool) -> SList a -> SBool
all f = foldr ((.&&) . f) sTrue
-- | Check some element satisfies the predicate.
--
-- >>> let isEven x = x `sMod` 2 .== 0
-- >>> any (sNot . isEven) [2, 4, 6, 8, 10 :: SInteger]
-- False
-- >>> any isEven [2, 4, 6, 1, 8, 10 :: SInteger]
-- True
any :: forall a. SymVal a => (SBV a -> SBool) -> SList a -> SBool
any f = foldr ((.||) . f) sFalse
-- | Conjunction of all the elements.
--
-- >>> and []
-- True
-- >>> prove $ \s -> and [s, sNot s] .== sFalse
-- Q.E.D.
and :: SList Bool -> SBool
and = all id
-- | Disjunction of all the elements.
--
-- >>> or []
-- False
-- >>> prove $ \s -> or [s, sNot s]
-- Q.E.D.
or :: SList Bool -> SBool
or = any id
-- | Replicate an element a given number of times.
--
-- >>> replicate 3 (2 :: SInteger) .== [2, 2, 2 :: SInteger]
-- True
-- >>> replicate (-2) (2 :: SInteger) .== ([] :: SList Integer)
-- True
replicate :: forall a. SymVal a => SInteger -> SBV a -> SList a
replicate c e
| Just c' <- unliteral c, Just e' <- unliteral e
= literal (genericReplicate c' e')
| True
= def c e
where def = smtFunction "sbv.replicate"
$ \count elt -> [sCase| count of
_ | count .<= 0 -> []
_ -> elt .: def (count - 1) elt
|]
-- | inits of a list.
--
-- >>> inits ([] :: SList Integer)
-- [[]] :: [[SInteger]]
-- >>> inits [1,2,3,4::SInteger]
-- [[],[1],[1,2],[1,2,3],[1,2,3,4]] :: [[SInteger]]
inits :: forall a. SymVal a => SList a -> SList [a]
inits xs
| Just xs' <- unliteral xs
= literal (L.inits xs')
| True
= def xs
where def = smtFunction "sbv.inits"
$ \l -> [sCase| l of
[] -> [[]]
_ : _ -> def (init l) ++ [l]
|]
-- | tails of a list.
--
-- >>> tails ([] :: SList Integer)
-- [[]] :: [[SInteger]]
-- >>> tails [1,2,3,4::SInteger]
-- [[1,2,3,4],[2,3,4],[3,4],[4],[]] :: [[SInteger]]
tails :: forall a. SymVal a => SList a -> SList [a]
tails xs
| Just xs' <- unliteral xs
= literal (L.tails xs')
| True
= def xs
where def = smtFunction "sbv.tails"
$ \l -> [sCase| l of
[] -> [[]]
_ : tl -> l .: def tl
|]
-- | Minimum of a list that has symbolic-ordering. If the list is empty, then
-- the result is underspecified, i.e., it is an arbitrary element of the element type.
--
-- >>> minimum ([1,2,3] :: SList Integer)
-- 1 :: SInteger
-- >>> sat $ 512 .== minimum (literal [] :: SList Integer)
-- Satisfiable. Model:
-- SList.minimum @Integer = 512 :: Integer
minimum :: forall a. (SymVal a, Ord a, OrdSymbolic (SBV a)) => SList a -> SBV a
minimum xs
| Just lxs@(_:_) <- unliteral xs
= literal (P.minimum lxs)
| True
= foldr (smin @(SBV a)) (some "SList.minimum" (const sTrue)) xs
-- | Maximum of a list that has symbolic-ordering. If the list is empty, then
-- the result is underspecified, i.e., it is an arbitrary element of the element type.
--
-- >>> maximum ([1,2,3] :: SList Integer)
-- 3 :: SInteger
-- >>> sat $ 512 .== maximum (literal [] :: SList Integer)
-- Satisfiable. Model:
-- SList.maximum @Integer = 512 :: Integer
maximum :: forall a. (SymVal a, Ord a, OrdSymbolic (SBV a)) => SList a -> SBV a
maximum xs
| Just lxs@(_:_) <- unliteral xs
= literal (P.maximum lxs)
| True
= foldr (smax @(SBV a)) (some "SList.maximum" (const sTrue)) xs
-- | Difference.
--
-- >>> [1, 2] \\ [3, 4 :: SInteger]
-- [1,2] :: [SInteger]
-- >>> [1, 2] \\ [2, 4 :: SInteger]
-- [1] :: [SInteger]
(\\) :: forall a. (Eq a, SymVal a) => SList a -> SList a -> SList a
xs \\ ys
| Just xs' <- unliteral xs, Just ys' <- unliteral ys
= literal (xs' L.\\ ys')
| True
= def xs ys
where def = smtFunction "sbv.diff"
$ \x y -> [sCase| x of
[] -> []
h : t -> let r = def t y
in ite (h `elem` y) r (h .: r)
|]
infix 5 \\ -- CPP: do not eat the final newline
-- | A class of filtering-like functions. In SBV, we make a distinction between closures and regular functions,
-- and we instantiate this class appropriately so it can handle both cases.
class SymVal a => SFilter func a | func -> a where
-- | Filter a list via a predicate.
--
-- >>> filter (\(x :: SInteger) -> x `sMod` 2 .== 0) (literal [1 .. 10])
-- [2,4,6,8,10] :: [SInteger]
-- >>> filter (\(x :: SInteger) -> x `sMod` 2 ./= 0) (literal [1 .. 10])
-- [1,3,5,7,9] :: [SInteger]
filter :: func -> SList a -> SList a
-- | Handle the concrete case of filtering. Used internally only.
concreteFilter :: func -> (SBV a -> SBool) -> SList a -> Maybe [a]
concreteFilter _ f sas
| Just as <- unliteral sas
= case P.map (unliteral . f . literal) as of
xs | P.any isNothing xs -> Nothing
| True -> Just [e | (True, e) <- P.zip (catMaybes xs) as]
| True
= Nothing
-- | Partition a symbolic list according to a predicate.
--
-- >>> partition (\(x :: SInteger) -> x `sMod` 2 .== 0) (literal [1 .. 10])
-- ([2,4,6,8,10],[1,3,5,7,9]) :: ([SInteger], [SInteger])
partition :: func -> SList a -> STuple [a] [a]
-- | Handle the concrete case of partitioning. Used internally only.
concretePartition :: func -> (SBV a -> SBool) -> SList a -> Maybe ([a], [a])
concretePartition _ f l
| Just l' <- unliteral l
= case P.map (unliteral . f . literal) l' of
xs | P.any isNothing xs -> Nothing
| True -> let (ts, fs) = L.partition P.fst (P.zip (catMaybes xs) l')
in Just (P.map P.snd ts, P.map P.snd fs)
| True
= Nothing
-- | Symbolic equivalent of @takeWhile@
--
-- >>> takeWhile (\(x :: SInteger) -> x `sMod` 2 .== 0) (literal [1..10])
-- [] :: [SInteger]
-- >>> takeWhile (\(x :: SInteger) -> x `sMod` 2 ./= 0) (literal [1..10])
-- [1] :: [SInteger]
takeWhile :: func -> SList a -> SList a
-- | Handle the concrete case of take-while. Used internally only.
concreteTakeWhile :: func -> (SBV a -> SBool) -> SList a -> Maybe [a]
concreteTakeWhile _ f sas
| Just as <- unliteral sas
= case P.map (unliteral . f . literal) as of
xs | P.any isNothing xs -> Nothing
| True -> Just (P.map P.snd (P.takeWhile P.fst (P.zip (catMaybes xs) as)))
| True
= Nothing
-- | Symbolic equivalent of @dropWhile@
-- >>> dropWhile (\(x :: SInteger) -> x `sMod` 2 .== 0) (literal [1..10])
-- [1,2,3,4,5,6,7,8,9,10] :: [SInteger]
-- >>> dropWhile (\(x :: SInteger) -> x `sMod` 2 ./= 0) (literal [1..10])
-- [2,3,4,5,6,7,8,9,10] :: [SInteger]
dropWhile :: func -> SList a -> SList a
-- | Handle the concrete case of take-while. Used internally only.
concreteDropWhile :: func -> (SBV a -> SBool) -> SList a -> Maybe [a]
concreteDropWhile _ f sas
| Just as <- unliteral sas
= case P.map (unliteral . f . literal) as of
xs | P.any isNothing xs -> Nothing
| True -> Just (P.map P.snd (P.dropWhile P.fst (P.zip (catMaybes xs) as)))
| True
= Nothing
-- | Filtering with symbolic functions.
instance SymVal a => SFilter (SBV a -> SBool) a where
-- | @filter f xs@ filters the list with the given predicate.
filter f l
| Just concResult <- concreteFilter f f l
= literal concResult
| True
= sbvFilter l
where sbvFilter = smtHOFunction "sbv.filter" f
$ \xs -> [sCase| xs of
[] -> []
h : t -> let r = sbvFilter t
in ite (f h) (h .: r) r
|]
-- | @partition f xs@ splits the list into two and returns those that satisfy the predicate in the
-- first element, and those that don't in the second.
partition f l
| Just concResult <- concretePartition f f l
= literal concResult
| True
= sbvPartition l
where sbvPartition = smtHOFunction "sbv.partition" f
$ \xs -> [sCase| xs of
[] -> tuple ([], [])
h : t -> case sbvPartition t of
(as, bs) | f h -> tuple (h .: as, bs)
| True -> tuple (as, h .: bs)
|]
-- | @takeWhile f xs@ takes the prefix of @xs@ that satisfy the predicate.
takeWhile f l
| Just concResult <- concreteTakeWhile f f l
= literal concResult
| True
= sbvTakeWhile l
where sbvTakeWhile = smtHOFunction "sbv.takeWhile" f
$ \xs -> [sCase| xs of
[] -> []
h : t | f h -> h .: sbvTakeWhile t
| True -> []
|]
-- | @dropWhile f xs@ drops the prefix of @xs@ that satisfy the predicate.
dropWhile f l
| Just concResult <- concreteDropWhile f f l
= literal concResult
| True
= sbvDropWhile l
where sbvDropWhile = smtHOFunction "sbv.dropWhile" f
$ \xs -> [sCase| xs of
[] -> []
h : t | f h -> sbvDropWhile t
| True -> xs
|]
-- | Filtering with closures.
instance (SymVal env, SymVal a) => SFilter (Closure (SBV env) (SBV a -> SBool)) a where
filter cls@Closure{closureEnv, closureFun} l
| Just concResult <- concreteFilter cls (closureFun closureEnv) l
= literal concResult
| True
= sbvFilter (tuple (closureEnv, l))
where sbvFilter = smtHOFunction "sbv.closureFilter" closureFun
$ \envxs -> [sCase| envxs of
(_, []) -> []
(cEnv, h : t) -> let r = sbvFilter (tuple (cEnv, t))
in ite (closureFun cEnv h) (h .: r) r
|]
partition cls@Closure{closureEnv, closureFun} l
| Just concResult <- concretePartition cls (closureFun closureEnv) l
= literal concResult
| True
= sbvPartition (tuple (closureEnv, l))
where sbvPartition = smtHOFunction "sbv.closurePartition" closureFun
$ \envxs -> [sCase| envxs of
(_, []) -> tuple ([], [])
(cEnv, h : t) -> case sbvPartition (tuple (cEnv, t)) of
(as, bs) | closureFun cEnv h -> tuple (h .: as, bs)
| True -> tuple (as, h .: bs)
|]
takeWhile cls@Closure{closureEnv, closureFun} l
| Just concResult <- concreteTakeWhile cls (closureFun closureEnv) l
= literal concResult
| True
= sbvTakeWhile (tuple (closureEnv, l))
where sbvTakeWhile = smtHOFunction "sbv.closureTakeWhile" closureFun
$ \envxs -> [sCase| envxs of
(_, []) -> []
(cEnv, h : t) | closureFun cEnv h -> h .: sbvTakeWhile (tuple (cEnv, t))
| True -> []
|]
dropWhile cls@Closure{closureEnv, closureFun} l
| Just concResult <- concreteDropWhile cls (closureFun closureEnv) l
= literal concResult
| True
= sbvDropWhile (tuple (closureEnv, l))
where sbvDropWhile = smtHOFunction "sbv.closureDropWhile" closureFun
$ \envxs -> [sCase| envxs of
(_, []) -> []
(cEnv, lst@(h : t)) | closureFun cEnv h -> sbvDropWhile (tuple (cEnv, t))
| True -> lst
|]
-- | @`sum` s@. Sum the given sequence.
--
-- >>> sum [sEnum|1 .. 10::SInteger|]
-- 55 :: SInteger
sum :: forall a. (SymVal a, Num (SBV a)) => SList a -> SBV a
sum = foldr ((+) @(SBV a)) 0
-- | @`product` s@. Multiply out the given sequence.
--
-- >>> product [sEnum|1 .. 10::SInteger|]
-- 3628800 :: SInteger
product :: forall a. (SymVal a, Num (SBV a)) => SList a -> SBV a
product = foldr ((*) @(SBV a)) 1
-- | A class of symbolic aware enumerations. This is similar to Haskell's @Enum@ class,
-- except some of the methods are generalized to work with symbolic values. Together
-- with the 'Data.SBV.sEnum' quasiquoter, you can write symbolic arithmetic progressions,
-- such as:
--
-- >>> [sEnum| 5, 7 .. 16::SInteger|]
-- [5,7,9,11,13,15] :: [SInteger]
-- >>> [sEnum| 4 ..|] :: SList (WordN 4)
-- [4,5,6,7,8,9,10,11,12,13,14,15] :: [SWord 4]
-- >>> [sEnum| 9, 12 ..|] :: SList (IntN 4)
-- [-7,-4,-1,2,5] :: [SInt 4]
class EnumSymbolic a where
-- | @`succ`@, same as in the @Enum@ class
succ :: SBV a -> SBV a
-- | @`pred`@, same as in the @Enum@ class
pred :: SBV a -> SBV a
-- | @`toEnum`@, same as in the @Enum@ class, except it takes an 'SInteger'
toEnum :: SInteger -> SBV a
-- | @`fromEnum`@, same as in the @Enum@ class, except it returns an 'SInteger'
fromEnum :: SBV a -> SInteger
-- | @`enumFrom` m@. Symbolic version of @[m ..]@
enumFrom :: SBV a -> SList a
-- | @`enumFromThen` m@. Symbolic version of @[m, m' ..]@
enumFromThen :: SBV a -> SBV a -> SList a
-- | @`enumFromTo` m n@. Symbolic version of @[m .. n]@
enumFromTo :: SymVal a => SBV a -> SBV a -> SList a
-- | @`enumFromThenTo` m n@. Symbolic version of @[m, m' .. n]@
enumFromThenTo :: SymVal a => SBV a -> SBV a -> SBV a -> SList a
-- | 'EnumSymbolic' instance for words
instance {-# OVERLAPPABLE #-} (SymVal a, Bounded a, Integral a, Num a, Num (SBV a)) => EnumSymbolic a where
succ = smtFunction "EnumSymbolic.succ" (\x -> ite (x .== maxBound) (some "EnumSymbolic.succ.maxBound" (const sTrue)) (x+1))
pred = smtFunction "EnumSymbolic.pred" (\x -> ite (x .== minBound) (some "EnumSymbolic.pred.minBound" (const sTrue)) (x-1))
toEnum = smtFunction "EnumSymbolic.toEnum" $ \x ->
ite (x .< sFromIntegral (minBound @(SBV a))) (some "EnumSymbolic.toEnum.<minBound" (const sTrue))
$ ite (x .> sFromIntegral (maxBound @(SBV a))) (some "EnumSymbolic.toEnum.>maxBound" (const sTrue))
$ sFromIntegral x
fromEnum = sFromIntegral
enumFrom n = map sFromIntegral (enumFromTo @Integer (sFromIntegral n) (sFromIntegral (maxBound @(SBV a))))
enumFromThen = smtFunction "EnumSymbolic.enumFromThen" $ \n1 n2 ->
let i_n1, i_n2 :: SInteger
i_n1 = sFromIntegral n1
i_n2 = sFromIntegral n2
in map sFromIntegral (ite (i_n2 .>= i_n1)
(enumFromThenTo i_n1 i_n2 (sFromIntegral (maxBound @(SBV a))))
(enumFromThenTo i_n1 i_n2 (sFromIntegral (minBound @(SBV a)))))
enumFromTo n m = map sFromIntegral (enumFromTo @Integer (sFromIntegral n) (sFromIntegral m))
enumFromThenTo n m t = map sFromIntegral (enumFromThenTo @Integer (sFromIntegral n) (sFromIntegral m) (sFromIntegral t))
-- | 'EnumSymbolic' instance for integer. NB. The above definition goes thru integers, hence we need to define this explicitly.
instance {-# OVERLAPPING #-} EnumSymbolic Integer where
succ x = x + 1
pred x = x - 1
toEnum = id
fromEnum = id
enumFrom n = enumFromThen n (n+1)
enumFromTo n = enumFromThenTo n (n+1)
enumFromThen x y = go x (y-x)
where go = smtProductiveFunction "EnumSymbolic.Integer.enumFromThen" $ \start delta -> start .: go (start+delta) delta
enumFromThenTo x y z = ite (delta .>= 0) (up x delta z) (down x delta z)
where delta = y - x
up, down :: SInteger -> SInteger -> SInteger -> SList Integer
up = smtFunctionWithMeasure "EnumSymbolic.Integer.enumFromThenTo.up"
(\start _d end -> 0 `smax` (end - start + 1), [])
$ \start d end -> ite (start .> end .|| d .<= 0) [] (start .: up (start + d) d end)
down = smtFunctionWithMeasure "EnumSymbolic.Integer.enumFromThenTo.down"
(\start _d end -> 0 `smax` (start - end + 1), [])
$ \start d end -> ite (start .< end .|| d .>= 0) [] (start .: down (start + d) d end)
-- | 'EnumSymbolic instance for 'Float'. Note that the termination requirement as defined by the Haskell standard for floats state:
-- > For Float and Double, the semantics of the enumFrom family is given by the rules for Int above,
-- > except that the list terminates when the elements become greater than @e3 + i/2@ for positive increment @i@,
-- > or when they become less than @e3 + i/2@ for negative @i@.
instance {-# OVERLAPPING #-} EnumSymbolic Float where
succ x = x + 1
pred x = x - 1
toEnum = sFromIntegral
fromEnum = fromSFloat sRTZ
enumFrom n = enumFromThen n (n+1)
enumFromTo n = enumFromThenTo n (n+1)
enumFromThen x y = go 0 x (y-x)
where go = smtProductiveFunction "EnumSymbolic.Float.enumFromThen" $ \k n d -> (n + k * d) .: go (k+1) n d
enumFromThenTo x y zIn = ite (delta .>= 0) (up 0 x delta z) (down 0 x delta z)
where delta, z :: SFloat
delta = y - x
z = zIn + delta / 2
up, down :: SFloat -> SFloat -> SFloat -> SFloat -> SList Float
up = smtFunctionWithMeasure "EnumSymbolic.Float.enumFromThenTo.up" (\k n d end -> 0 `smax` (end - (n + k * d)), [])
$ \k n d end -> let c = n + k * d in ite (c .> end) [] (c .: up (k+1) n d end)
down = smtFunctionWithMeasure "EnumSymbolic.Float.enumFromThenTo.down" (\k n d end -> 0 `smax` ((n + k * d) - end), [])
$ \k n d end -> let c = n + k * d in ite (c .< end) [] (c .: down (k+1) n d end)
-- | 'EnumSymbolic instance for 'Double'
instance {-# OVERLAPPING #-} EnumSymbolic Double where
succ x = x + 1
pred x = x - 1
toEnum = sFromIntegral
fromEnum = fromSDouble sRTZ
enumFrom n = enumFromThen n (n+1)
enumFromTo n = enumFromThenTo n (n+1)
enumFromThen x y = go 0 x (y-x)
where go = smtProductiveFunction "EnumSymbolic.Double.enumFromThen" $ \k n d -> (n + k * d) .: go (k+1) n d
enumFromThenTo x y zIn = ite (delta .>= 0) (up 0 x delta z) (down 0 x delta z)
where delta, z :: SDouble
delta = y - x
z = zIn + delta / 2
up, down :: SDouble -> SDouble -> SDouble -> SDouble -> SList Double
up = smtFunctionWithMeasure "EnumSymbolic.Double.enumFromThenTo.up" (\k n d end -> 0 `smax` (end - (n + k * d)), [])
$ \k n d end -> let c = n + k * d in ite (c .> end) [] (c .: up (k+1) n d end)
down = smtFunctionWithMeasure "EnumSymbolic.Double.enumFromThenTo.down" (\k n d end -> 0 `smax` ((n + k * d) - end), [])
$ \k n d end -> let c = n + k * d in ite (c .< end) [] (c .: down (k+1) n d end)
-- | 'EnumSymbolic instance for arbitrary floats
instance {-# OVERLAPPING #-} ValidFloat eb sb => EnumSymbolic (FloatingPoint eb sb) where
succ x = x + 1
pred x = x - 1
toEnum = sFromIntegral
fromEnum = fromSFloatingPoint sRTZ
enumFrom n = enumFromThen n (n+1)
enumFromTo n = enumFromThenTo n (n+1)
enumFromThen x y = go 0 x (y-x)
where go = smtProductiveFunction "EnumSymbolic.FloatingPoint.enumFromThen" $ \k n d -> (n + k * d) .: go (k+1) n d
enumFromThenTo x y zIn = ite (delta .>= 0) (up 0 x delta z) (down 0 x delta z)
where delta, z :: SFloatingPoint eb sb
delta = y - x
z = zIn + delta / 2
up, down :: SFloatingPoint eb sb -> SFloatingPoint eb sb -> SFloatingPoint eb sb -> SFloatingPoint eb sb -> SList (FloatingPoint eb sb)
up = smtFunctionWithMeasure "EnumSymbolic.FloatingPoint.enumFromThenTo.up" (\k n d end -> 0 `smax` (end - (n + k * d)), [])
$ \k n d end -> let c = n + k * d in ite (c .> end) [] (c .: up (k+1) n d end)
down = smtFunctionWithMeasure "EnumSymbolic.FloatingPoint.enumFromThenTo.down" (\k n d end -> 0 `smax` ((n + k * d) - end), [])
$ \k n d end -> let c = n + k * d in ite (c .< end) [] (c .: down (k+1) n d end)
-- | 'EnumSymbolic instance for arbitrary AlgReal. We don't have to use the multiplicative trick here
-- since alg-reals are precise. But, following rational in Haskell, we do use the stopping point of @z + delta / 2@.
instance {-# OVERLAPPING #-} EnumSymbolic AlgReal where
succ x = x + 1
pred x = x - 1
toEnum = sFromIntegral
fromEnum = sRealToSIntegerTruncate
enumFrom n = enumFromThen n (n+1)
enumFromTo n = enumFromThenTo n (n+1)
enumFromThen x y = go x (y-x)
where go = smtProductiveFunction "EnumSymbolic.AlgReal.enumFromThen" $ \start delta -> start .: go (start+delta) delta
enumFromThenTo x y zIn = ite (delta .>= 0) (up x delta z) (down x delta z)
where delta, z :: SReal
delta = y - x
z = zIn + delta / 2
up, down :: SReal -> SReal -> SReal -> SList AlgReal
up = smtFunctionWithMeasure "EnumSymbolic.AlgReal.enumFromThenTo.up" (\start _d end -> 0 `smax` (end - start + 1), [])
$ \start d end -> ite (start .> end .|| d .<= 0) [] (start .: up (start + d) d end)
down = smtFunctionWithMeasure "EnumSymbolic.AlgReal.enumFromThenTo.down" (\start _d end -> 0 `smax` (start - end + 1), [])
$ \start d end -> ite (start .< end .|| d .>= 0) [] (start .: down (start + d) d end)
-- | Lookup. If we can't find, then the result is unspecified.
--
-- >>> lookup (4 :: SInteger) (literal [(5, 12), (4, 3), (2, 6 :: Integer)])
-- 3 :: SInteger
-- >>> prove $ \(x :: SInteger) -> x .== lookup 9 (literal [(5, 12), (4, 3), (2, 6 :: Integer)])
-- Falsifiable. Counter-example:
-- sbv.lookup_notFound @Integer = 0 :: Integer
-- s0 = 1 :: Integer
lookup :: (SymVal k, SymVal v) => SBV k -> SList (k, v) -> SBV v
lookup = smtFunction "sbv.lookup"
$ \k lst -> [sCase| lst of
[] -> some "sbv.lookup_notFound" (const sTrue)
(k', v) : rest | k .== k' -> v
| True -> lookup k rest
|]
-- | @`strToNat` s@. Retrieve integer encoded by string @s@ (ground rewriting only).
-- Note that by definition this function only works when @s@ only contains digits,
-- that is, if it encodes a natural number. Otherwise, it returns '-1'.
--
-- >>> prove $ \s -> let n = strToNat s in length s .== 1 .=> (-1) .<= n .&& n .<= 9
-- Q.E.D.
strToNat :: SString -> SInteger
strToNat s
| Just a <- unliteral s
= if P.all C.isDigit a && not (P.null a)
then literal (read a)
else -1
| True
= lift1Str StrStrToNat Nothing s
-- | @`natToStr` i@. Retrieve string encoded by integer @i@ (ground rewriting only).
-- Again, only naturals are supported, any input that is not a natural number
-- produces empty string, even though we take an integer as an argument.
--
-- >>> prove $ \i -> length (natToStr i) .== 3 .=> i .<= 999
-- Q.E.D.
natToStr :: SInteger -> SString
natToStr i
| Just v <- unliteral i
= literal $ if v >= 0 then show v else ""
| True
= lift1Str StrNatToStr Nothing i
-- | Lift a unary operator over lists.
lift1 :: forall a b. (SymVal a, SymVal b) => Bool -> SeqOp -> Maybe (a -> b) -> SBV a -> SBV b
lift1 simpleEq w mbOp a
| Just cv <- concEval1 simpleEq mbOp a
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf (Proxy @b)
r st = do sva <- sbvToSV st a
newExpr st k (SBVApp (SeqOp w) [sva])
-- | Lift a binary operator over lists.
lift2 :: forall a b c. (SymVal a, SymVal b, SymVal c) => Bool -> SeqOp -> Maybe (a -> b -> c) -> SBV a -> SBV b -> SBV c
lift2 simpleEq w mbOp a b
| Just cv <- concEval2 simpleEq mbOp a b
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf (Proxy @c)
r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
newExpr st k (SBVApp (SeqOp w) [sva, svb])
-- | Lift a ternary operator over lists.
lift3 :: forall a b c d. (SymVal a, SymVal b, SymVal c, SymVal d) => Bool -> SeqOp -> Maybe (a -> b -> c -> d) -> SBV a -> SBV b -> SBV c -> SBV d
lift3 simpleEq w mbOp a b c
| Just cv <- concEval3 simpleEq mbOp a b c
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf (Proxy @d)
r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
svc <- sbvToSV st c
newExpr st k (SBVApp (SeqOp w) [sva, svb, svc])
-- | Concrete evaluation for unary ops
concEval1 :: forall a b. (SymVal a, SymVal b) => Bool -> Maybe (a -> b) -> SBV a -> Maybe (SBV b)
concEval1 simpleEq mbOp a
| not simpleEq || eqCheckIsObjectEq (kindOf (Proxy @a)) = literal <$> (mbOp <*> unliteral a)
| True = Nothing
-- | Concrete evaluation for binary ops
concEval2 :: forall a b c. (SymVal a, SymVal b, SymVal c) => Bool -> Maybe (a -> b -> c) -> SBV a -> SBV b -> Maybe (SBV c)
concEval2 simpleEq mbOp a b
| not simpleEq || eqCheckIsObjectEq (kindOf (Proxy @a)) = literal <$> (mbOp <*> unliteral a <*> unliteral b)
| True = Nothing
-- | Concrete evaluation for ternary ops
concEval3 :: forall a b c d. (SymVal a, SymVal b, SymVal c, SymVal d) => Bool -> Maybe (a -> b -> c -> d) -> SBV a -> SBV b -> SBV c -> Maybe (SBV d)
concEval3 simpleEq mbOp a b c
| not simpleEq || eqCheckIsObjectEq (kindOf (Proxy @a)) = literal <$> (mbOp <*> unliteral a <*> unliteral b <*> unliteral c)
| True = Nothing
-- | Is the list concretely known empty?
isConcretelyEmpty :: SymVal a => SList a -> Bool
isConcretelyEmpty sl | Just l <- unliteral sl = P.null l
| True = False
-- | Lift a unary operator over strings.
lift1Str :: forall a b. (SymVal a, SymVal b) => StrOp -> Maybe (a -> b) -> SBV a -> SBV b
lift1Str w mbOp a
| Just cv <- literal <$> (mbOp <*> unliteral a)
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf (Proxy @b)
r st = do sva <- sbvToSV st a
newExpr st k (SBVApp (StrOp w) [sva])
{- HLint ignore implode "Use :" -}
{- HLint ignore replicate "Use const" -}