Copyright | (c) Levent Erkok |
---|---|
License | BSD3 |
Maintainer | erkokl@gmail.com |
Stability | experimental |
Safe Haskell | None |
Language | Haskell2010 |
Documentation.SBV.Examples.KnuckleDragger.Lists
Contents
- Appending null
- Moving cons over append
- Associativity of append
- Reverse and append
- Reversing twice is identity
- Lengths of lists
- Any, all, and filtering
- Map, append, and reverse
- Reverse and length
- Foldr-map fusion
- Foldr-foldr fusion
- Foldr over append
- Foldl over append
- Foldr-foldl correspondence
- Foldr-foldl duality, generalized
- Another foldl-foldr correspondence
- Bookkeeping law
- Filter-append
- Map and filter don't commute
- Partition
- Take and drop
- Summing via halving
- Zip
Description
A variety of KnuckleDragger proofs on list processing functions. Note that these proofs only hold for finite lists. SMT-solvers do not model infinite lists, and hence all claims are for finite (but arbitrary-length) lists.
Synopsis
- data A
- type SA = SBV A
- data B
- type SB = SBV B
- data C
- type SC = SBV C
- appendNull :: IO Proof
- consApp :: IO Proof
- appendAssoc :: IO Proof
- revCons :: IO Proof
- revApp :: IO Proof
- reverseReverse :: IO Proof
- lengthTail :: IO Proof
- badLengthProof :: IO ()
- lenAppend :: IO Proof
- lenAppend2 :: IO Proof
- allAny :: IO Proof
- filterEx :: IO Proof
- filterEx2 :: IO ()
- mapEquiv :: IO Proof
- mapAppend :: (SA -> SB) -> IO Proof
- mapReverse :: IO Proof
- revLen :: IO Proof
- badRevLen :: IO ()
- foldrMapFusion :: IO Proof
- foldrFusion :: IO Proof
- foldrOverAppend :: IO Proof
- foldlOverAppend :: (SB -> SA -> SB) -> IO Proof
- foldrFoldlDuality :: IO Proof
- foldrFoldlDualityGeneralized :: IO Proof
- foldrFoldl :: IO Proof
- bookKeeping :: IO Proof
- filterAppend :: (SA -> SBool) -> IO Proof
- filterConcat :: IO Proof
- mapFilter :: IO ()
- partition1 :: IO Proof
- partition2 :: IO Proof
- take_take :: IO Proof
- drop_drop :: IO Proof
- take_drop :: IO Proof
- take_cons :: IO Proof
- take_map :: IO Proof
- drop_cons :: SymVal elt => Proxy elt -> IO Proof
- drop_map :: IO Proof
- length_take :: IO Proof
- length_drop :: IO Proof
- take_all :: IO Proof
- drop_all :: IO Proof
- take_append :: IO Proof
- drop_append :: IO Proof
- sumHalves :: IO Proof
- map_fst_zip :: IO Proof
- map_snd_zip :: IO Proof
- map_fst_zip_take :: IO Proof
- map_snd_zip_take :: IO Proof
Documentation
Data declaration for an uninterpreted type, usually indicating source.
Instances
Data A Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> A -> c A # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c A # dataTypeOf :: A -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c A) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c A) # gmapT :: (forall b. Data b => b -> b) -> A -> A # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> A -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> A -> r # gmapQ :: (forall d. Data d => d -> u) -> A -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> A -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> A -> m A # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> A -> m A # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> A -> m A # | |
Read A Source # | |
Show A Source # | |
SymVal A Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods mkSymVal :: MonadSymbolic m => VarContext -> Maybe String -> m (SBV A) Source # literal :: A -> SBV A Source # isConcretely :: SBV A -> (A -> Bool) -> Bool Source # free :: MonadSymbolic m => String -> m (SBV A) Source # free_ :: MonadSymbolic m => m (SBV A) Source # mkFreeVars :: MonadSymbolic m => Int -> m [SBV A] Source # symbolic :: MonadSymbolic m => String -> m (SBV A) Source # symbolics :: MonadSymbolic m => [String] -> m [SBV A] Source # unliteral :: SBV A -> Maybe A Source # | |
HasKind A Source # | |
SatModel A Source # | |
Data declaration for an uninterpreted type, usually indicating target.
Instances
Data B Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> B -> c B # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c B # dataTypeOf :: B -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c B) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c B) # gmapT :: (forall b. Data b => b -> b) -> B -> B # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> B -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> B -> r # gmapQ :: (forall d. Data d => d -> u) -> B -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> B -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> B -> m B # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> B -> m B # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> B -> m B # | |
Read B Source # | |
Show B Source # | |
SymVal B Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods mkSymVal :: MonadSymbolic m => VarContext -> Maybe String -> m (SBV B) Source # literal :: B -> SBV B Source # isConcretely :: SBV B -> (B -> Bool) -> Bool Source # free :: MonadSymbolic m => String -> m (SBV B) Source # free_ :: MonadSymbolic m => m (SBV B) Source # mkFreeVars :: MonadSymbolic m => Int -> m [SBV B] Source # symbolic :: MonadSymbolic m => String -> m (SBV B) Source # symbolics :: MonadSymbolic m => [String] -> m [SBV B] Source # unliteral :: SBV B -> Maybe B Source # | |
HasKind B Source # | |
SatModel B Source # | |
Data declaration for an uninterpreted type, usually indicating an intermediate value.
Instances
Data C Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> C -> c C # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c C # dataTypeOf :: C -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c C) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c C) # gmapT :: (forall b. Data b => b -> b) -> C -> C # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> C -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> C -> r # gmapQ :: (forall d. Data d => d -> u) -> C -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> C -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> C -> m C # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> C -> m C # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> C -> m C # | |
Read C Source # | |
Show C Source # | |
SymVal C Source # | |
Defined in Documentation.SBV.Examples.KnuckleDragger.Lists Methods mkSymVal :: MonadSymbolic m => VarContext -> Maybe String -> m (SBV C) Source # literal :: C -> SBV C Source # isConcretely :: SBV C -> (C -> Bool) -> Bool Source # free :: MonadSymbolic m => String -> m (SBV C) Source # free_ :: MonadSymbolic m => m (SBV C) Source # mkFreeVars :: MonadSymbolic m => Int -> m [SBV C] Source # symbolic :: MonadSymbolic m => String -> m (SBV C) Source # symbolics :: MonadSymbolic m => [String] -> m [SBV C] Source # unliteral :: SBV C -> Maybe C Source # | |
HasKind C Source # | |
SatModel C Source # | |
Appending null
appendNull :: IO Proof Source #
xs ++ [] == xs
We have:
>>>
appendNull
Lemma: appendNull Q.E.D. [Proven] appendNull
Moving cons over append
(x : xs) ++ ys == x : (xs ++ ys)
We have:
>>>
consApp
Lemma: consApp Q.E.D. [Proven] consApp
Associativity of append
appendAssoc :: IO Proof Source #
(xs ++ ys) ++ zs == xs ++ (ys ++ zs)
We have:
>>>
appendAssoc
Lemma: appendAssoc Q.E.D. [Proven] appendAssoc
Surprisingly, z3 can prove this without any induction. (Since SBV's append translates directly to the concatenation of sequences in SMTLib, it must trigger an internal heuristic in z3 that proves it right out-of-the-box!)
Reverse and append
reverse (x:xs) == reverse xs ++ [x]
>>>
revCons
Lemma: revCons Q.E.D. [Proven] revCons
reverse (xs ++ ys) .== reverse ys ++ reverse xs
We have:
>>>
revApp
Inductive lemma: revApp Base: revApp.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: revApp.Step Q.E.D. [Proven] revApp
Reversing twice is identity
reverseReverse :: IO Proof Source #
reverse (reverse xs) == xs
We have:
>>>
reverseReverse
Inductive lemma: revApp Base: revApp.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: revApp.Step Q.E.D. Inductive lemma: reverseReverse Base: reverseReverse.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: reverseReverse.Step Q.E.D. [Proven] reverseReverse
Lengths of lists
lengthTail :: IO Proof Source #
length (x : xs) = 1 + length xs
We have:
>>>
lengthTail
Lemma: lengthTail Q.E.D. [Proven] lengthTail
badLengthProof :: IO () Source #
It is instructive to see what kind of counter-example we get if a lemma fails to prove. Below, we do a variant of the 'lengthTail, but with a bad implementation over integers, and see the counter-example. Our implementation returns an incorrect answer if the given list is longer than 5 elements and have 42 in it. We have:
>>>
badLengthProof `catch` (\(_ :: SomeException) -> pure ())
Lemma: badLengthProof *** Failed to prove badLengthProof. Falsifiable. Counter-example: xs = [15,11,13,16,27,42] :: [Integer] imp = 42 :: Integer spec = 6 :: Integer
lenAppend :: IO Proof Source #
length (xs ++ ys) == length xs + length ys
We have:
>>>
lenAppend
Lemma: lenAppend Q.E.D. [Proven] lenAppend
lenAppend2 :: IO Proof Source #
length xs == length ys -> length (xs ++ ys) == 2 * length xs
We have:
>>>
lenAppend2
Lemma: lenAppend2 Q.E.D. [Proven] lenAppend2
Any, all, and filtering
not (all id xs) == any not xs
A list of booleans is not all true, if any of them is false. We have:
>>>
allAny
Inductive lemma: allAny Base: allAny.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: allAny.Step Q.E.D. [Proven] allAny
If an integer list doesn't have 2 as an element, then filtering for > 2
or .>= 2
yields the same result. We have:
>>>
filterEx
Inductive lemma: filterEx Base: filterEx.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Step: filterEx.Step Q.E.D. [Proven] filterEx
The filterEx
example above, except we get a counter-example if 2
can be in the list. Note that
we don't need the induction tactic here.
>>>
filterEx2 `catch` (\(_ :: SomeException) -> pure ())
Lemma: filterEx2 *** Failed to prove filterEx2. Falsifiable. Counter-example: xs = [2] :: [Integer]
Map, append, and reverse
f = g => map f xs = map g xs
>>>
mapEquiv
Inductive lemma: mapEquiv Base: mapEquiv.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: mapEquiv.Step Q.E.D. [Proven] mapEquiv
mapAppend :: (SA -> SB) -> IO Proof Source #
map f (xs ++ ys) == map f xs ++ map f ys
>>>
mapAppend (uninterpret "f")
Inductive lemma: mapAppend Base: mapAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: mapAppend.Step Q.E.D. [Proven] mapAppend
mapReverse :: IO Proof Source #
map f . reverse == reverse . map f
>>>
mapReverse
Inductive lemma: mapAppend Base: mapAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: mapAppend.Step Q.E.D. Inductive lemma: mapReverse Base: mapReverse.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: 6 Q.E.D. Step: mapReverse.Step Q.E.D. [Proven] mapReverse
Reverse and length
length xs == length (reverse xs)
We have:
>>>
revLen
Inductive lemma: revLen Base: revLen.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: revLen.Step Q.E.D. [Proven] revLen
An example where we attempt to prove a non-theorem. Notice the counter-example generated for:
length xs = ite (length xs .== 3) 5 (length xs)
We have:
>>>
badRevLen `catch` (\(_ :: SomeException) -> pure ())
Lemma: badRevLen *** Failed to prove badRevLen. Falsifiable. Counter-example: xs = [A_1,A_2,A_1] :: [A]
Foldr-map fusion
foldrMapFusion :: IO Proof Source #
foldr f a . map g = foldr (f . g) a
We have:
>>>
foldrMapFusion
Inductive lemma: foldrMapFusion Base: foldrMapFusion.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldrMapFusion.Step Q.E.D. [Proven] foldrMapFusion
Foldr-foldr fusion
foldrFusion :: IO Proof Source #
Given f a = b and f (g x y) = h x (f y), for all x and y We have: f . foldr g a = foldr h b
>>>
foldrFusion
Inductive lemma: foldrFusion Base: foldrFusion.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldrFusion.Step Q.E.D. [Proven] foldrFusion
Foldr over append
foldrOverAppend :: IO Proof Source #
foldr f a (xs ++ ys) == foldr f (foldr f a ys) xs
We have:
>>>
foldrOverAppend
Inductive lemma: foldrOverAppend Base: foldrOverAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldrOverAppend.Step Q.E.D. [Proven] foldrOverAppend
Foldl over append
foldlOverAppend :: (SB -> SA -> SB) -> IO Proof Source #
foldl f a (xs ++ ys) == foldl f (foldl f a xs) ys
We have:
>>>
foldlOverAppend (uninterpret "f")
Inductive lemma: foldlOverAppend Base: foldlOverAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: foldlOverAppend.Step Q.E.D. [Proven] foldlOverAppend
Foldr-foldl correspondence
foldrFoldlDuality :: IO Proof Source #
foldr f e xs == foldl (flip f) e (reverse xs)
We have:
>>>
foldrFoldlDuality
Inductive lemma: foldlOverAppend Base: foldlOverAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: foldlOverAppend.Step Q.E.D. Inductive lemma: foldrFoldlDuality Base: foldrFoldlDuality.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: 6 Q.E.D. Step: foldrFoldlDuality.Step Q.E.D. [Proven] foldrFoldlDuality
Foldr-foldl duality, generalized
foldrFoldlDualityGeneralized :: IO Proof Source #
Given:
x @ (y @ z) = (x @ y) @ z (associativity of @) and e @ x = x (left unit) and x @ e = x (right unit)
Prove:
foldr (@) e xs = foldl (@) e xs
We have:
>>>
foldrFoldlDualityGeneralized
Inductive lemma: helper Base: helper.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: helper.Step Q.E.D. Inductive lemma: foldrFoldlDuality Base: foldrFoldlDuality.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Asms: 5 Q.E.D. Step: 5 Q.E.D. Step: 6 Q.E.D. Step: foldrFoldlDuality.Step Q.E.D. [Proven] foldrFoldlDuality
Another foldl-foldr correspondence
foldrFoldl :: IO Proof Source #
Given:
(x <+> y) <*> z = x <+> (y <*> z) and x <+> e = e <*> x
Proves:
foldr (<+>) e xs = foldl (<*>) e xs
In Bird's Introduction to Functional Programming book (2nd edition) this is called the second duality theorem. We have:
>>>
foldrFoldl
Inductive lemma: foldl over <*>/<+> Base: foldl over <*>/<+>.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldl over <*>/<+>.Step Q.E.D. Inductive lemma: foldrFoldl Base: foldrFoldl.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: foldrFoldl.Step Q.E.D. [Proven] foldrFoldl
Bookkeeping law
bookKeeping :: IO Proof Source #
Provided f
is associative and a
is its both left and right-unit:
foldr f a . concat == foldr f a . map (foldr f a)
We have:
>>>
bookKeeping
Inductive lemma: foldBase Base: foldBase.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldBase.Step Q.E.D. Inductive lemma: foldrOverAppend Base: foldrOverAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: foldrOverAppend.Step Q.E.D. Inductive lemma: bookKeeping Base: bookKeeping.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: 6 Q.E.D. Step: bookKeeping.Step Q.E.D. [Proven] bookKeeping
NB. As of early 2025, we cannot express the above theorem in SBV directly, since it involves nested lambdas. (On the right hand side map has an argument that is represented as a foldr, which itself has a lambda.) As SMTLib moves to a higher-order logic, we intend to make such expressions readily expressable. In the mean time, we use an equivalent (albeit roundabout) version, where we define map-foldr combo as a recursive function ourselves.
NB. This theorem does not hold if f
does not have a left-unit! Consider the input [[], [x]]
. Left hand side reduces to
x
, while the right hand side reduces to: f a x
. And unless f
is commutative or a
is not also a left-unit,
then one can find a counter-example. (Aside: if both left and right units exist for a binary operator, then they
are necessarily the same element, since l = f l r = r
. So, an equivalent statement could simply say f
has
both left and right units.) A concrete counter-example is:
data T = A | B | C f :: T -> T -> T f C A = A f C B = A f x _ = x
You can verify f
is associative. Also note that C
is the right-unit for f
, but it isn't the left-unit.
In fact, f
has no-left unit by the above argument. In this case, the bookkeeping law produces B
for
the left-hand-side, and A
for the right-hand-side for the input [[], [B]]
.
Filter-append
filterAppend :: (SA -> SBool) -> IO Proof Source #
filter p (xs ++ ys) == filter p xs ++ filter p ys
We have:
>>>
filterAppend (uninterpret "p")
Inductive lemma: filterAppend Base: filterAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: filterAppend.Step Q.E.D. [Proven] filterAppend
filterConcat :: IO Proof Source #
filter p (concat xss) == concatMap (filter p xss)
Similar to the book-keeping law, we cannot express this in SBV directly, since it involves a nested lambda.
concatMap (filter p)
maps a higher-order function filter p
, which itself has a nested lambda. So, we use
our own merged definition. Hopefully we'll relax this as SMTLib gains more higher order features.
We have:
>>>
filterConcat
Inductive lemma: filterAppend Base: filterAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: filterAppend.Step Q.E.D. Inductive lemma: filterConcat Base: filterConcat.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: filterConcat.Step Q.E.D. [Proven] filterConcat
Map and filter don't commute
In general, mapping and filtering operations do not commute. We'll see the kind of counter-example we get from SBV if we attempt to prove:
>>>
mapFilter `catch` (\(_ :: SomeException) -> pure ())
Lemma: badMapFilter *** Failed to prove badMapFilter. Falsifiable. Counter-example: xs = [A_3] :: [A] lhs = [A_0] :: [A] rhs = [] :: [A] f :: A -> A f _ = A_0 p :: A -> Bool p A_3 = True p _ = False
As expected, the function f
maps everything to A_0
, and the predicate p
only lets A_3
through. As shown in the
counter-example, for the input [A_3]
, left-hand-side filters nothing and the result is the singleton A_0
. But the
map on the right-hand side maps everything to [A_0]
and the filter gets rid of the elements, resulting in an empty list.
Partition
partition1 :: IO Proof Source #
fst (partition f xs) == filter f xs
>>>
partition1
Inductive lemma: partition1 Base: partition1.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: partition1.Step Q.E.D. [Proven] partition1
partition2 :: IO Proof Source #
snd (partition f xs) == filter (not . f) xs
>>>
partition2
Inductive lemma: partition2 Base: partition2.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: partition2.Step Q.E.D. [Proven] partition2
Take and drop
take_take :: IO Proof Source #
take n (take m xs) = take (n smin
m) xs
>>>
take_take
Lemma: take_take Q.E.D. [Proven] take_take
drop_drop :: IO Proof Source #
n >= 0 && m >= 0 => drop n (drop m xs) = drop (n + m) xs
>>>
drop_drop
Lemma: drop_drop Q.E.D. [Proven] drop_drop
take_drop :: IO Proof Source #
take n xs ++ drop n xs == xs
>>>
take_drop
Lemma: take_drop Q.E.D. [Proven] take_drop
take_cons :: IO Proof Source #
n .> 0 => take n (x .: xs) = x .: take (n - 1) xs
>>>
take_cons
Lemma: take_cons Q.E.D. [Proven] take_cons
take n (map f xs) == map f (take n xs)
>>>
take_map
Lemma: take_cons Q.E.D. Lemma: map1 Q.E.D. Lemma: take_map.n <= 0 Q.E.D. Inductive lemma: take_map.n > 0 Base: take_map.n > 0.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Asms: 5 Q.E.D. Step: 5 Q.E.D. Step: take_map.n > 0.Step Q.E.D. Lemma: take_map Q.E.D. [Proven] take_map
drop_cons :: SymVal elt => Proxy elt -> IO Proof Source #
n .> 0 => drop n (x .: xs) = drop (n - 1) xs
>>>
drop_cons (Proxy @A)
Lemma: drop_cons Q.E.D. [Proven] drop_cons
drop n (map f xs) == map f (drop n xs)
>>>
drop_map
Lemma: drop_cons Q.E.D. Lemma: drop_cons Q.E.D. Lemma: drop_map.n <= 0 Q.E.D. Inductive lemma: drop_map.n > 0 Base: drop_map.n > 0.Base Q.E.D. Step: 1 Q.E.D. Asms: 2 Q.E.D. Step: 2 Q.E.D. Asms: 3 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Step: drop_map.n > 0.Step Q.E.D. Lemma: drop_map Step : 1 Q.E.D. Step : 2 Q.E.D. Step : 3 Q.E.D. Step : 4 Q.E.D. Result: Q.E.D. [Proven] drop_map
length_take :: IO Proof Source #
n >= 0 ==> length (take n xs) = length xs `min` n
>>>
length_take
Lemma: length_take Q.E.D. [Proven] length_take
length_drop :: IO Proof Source #
n >= 0 ==> length (drop n xs) = (length xs - n) `max` 0
>>>
length_drop
Lemma: length_drop Q.E.D. [Proven] length_drop
length xs <= n ==> take n xs == xs
>>>
take_all
Lemma: take_all Q.E.D. [Proven] take_all
length xs <= n ==> drop n xs == nil
>>>
drop_all
Lemma: drop_all Q.E.D. [Proven] drop_all
take_append :: IO Proof Source #
take n (xs ++ ys) = (take n xs ++ take (n - length xs) ys)
>>>
take_append
Lemma: take_append Step : 1 Q.E.D. Result: Q.E.D. [Proven] take_append
drop_append :: IO Proof Source #
drop n (xs ++ ys) = drop n xs ++ drop (n - length xs) ys
NB. As of Feb 2025, z3 struggles to prove this, but cvc5 gets it out-of-the-box.
>>>
drop_append
Lemma: drop_append Q.E.D. [Proven] drop_append
Summing via halving
sumHalves :: IO Proof Source #
We prove that summing a list can be done by halving the list, summing parts, and adding the results. The proof uses strong induction. We have:
>>>
sumHalves
Inductive lemma: sumAppend Base: sumAppend.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: sumAppend.Step Q.E.D. Inductive lemma (strong): sumHalves Base: sumHalves.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: sumHalves.Step Q.E.D. [Proven] sumHalves
Zip
map_fst_zip :: IO Proof Source #
length xs = length ys ⟹ map fst (zip xs ys) = xs
>>>
map_fst_zip
Inductive lemma: map_fst_zip Base: map_fst_zip.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Step: map_fst_zip.Step Q.E.D. [Proven] map_fst_zip
map_snd_zip :: IO Proof Source #
length xs = length ys ⟹ map snd (zip xs ys) = xs
>>>
map_snd_zip
Inductive lemma: map_snd_zip Base: map_snd_zip.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Asms: 4 Q.E.D. Step: 4 Q.E.D. Step: map_snd_zip.Step Q.E.D. [Proven] map_snd_zip
map_fst_zip_take :: IO Proof Source #
map fst (zip xs ys) = take (min (length xs) (length ys)) xs
>>>
map_fst_zip_take
Lemma: take_cons Q.E.D. Inductive lemma: map_fst_zip_take Base: map_fst_zip_take.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: map_fst_zip_take.Step Q.E.D. [Proven] map_fst_zip_take
map_snd_zip_take :: IO Proof Source #
map snd (zip xs ys) = take (min (length xs) (length ys)) xs
>>>
map_snd_zip_take
Lemma: take_cons Q.E.D. Inductive lemma: map_snd_zip_take Base: map_snd_zip_take.Base Q.E.D. Step: 1 Q.E.D. Step: 2 Q.E.D. Step: 3 Q.E.D. Step: 4 Q.E.D. Step: 5 Q.E.D. Step: map_snd_zip_take.Step Q.E.D. [Proven] map_snd_zip_take