sbv-14.0: Documentation/SBV/Examples/TP/ConstFold.hs
-----------------------------------------------------------------------------
-- |
-- Module : Documentation.SBV.Examples.TP.ConstFold
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Correctness of constant folding for a simple expression language.
-----------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Documentation.SBV.Examples.TP.ConstFold where
import Prelude hiding ((++), snd)
import Data.SBV
import Data.SBV.List as SL
import Data.SBV.Tuple as ST
import Data.SBV.TP
-- Get the expression language definitions
import Documentation.SBV.Examples.TP.VM
#ifdef DOCTEST
-- $setup
-- >>> import Data.SBV
-- >>> import Data.SBV.TP
-- >>> :set -XTypeApplications
#endif
-- | Base expression type (used in quantifiers).
type Exp = Expr String Integer
-- | Base environment-list type (used in quantifiers).
type EL = [(String, Integer)]
-- | Symbolic expression over strings and integers.
type SE = SExpr String Integer
-- | Symbolic environment over strings and integers.
type E = Env String Integer
-- * Simplification
-- | Simplify an expression at the top level, assuming sub-expressions are already folded.
-- The rules are:
--
-- * @Sqr (Con v) → Con (v*v)@
-- * @Inc (Con v) → Con (v+1)@
-- * @Add (Con 0) x → x@
-- * @Add x (Con 0) → x@
-- * @Add (Con a) (Con b) → Con (a+b)@
-- * @Mul (Con 0) x → Con 0@
-- * @Mul x (Con 0) → Con 0@
-- * @Mul (Con 1) x → x@
-- * @Mul x (Con 1) → x@
-- * @Mul (Con a) (Con b) → Con (a*b)@
-- * @Let nm (Con v) b → subst nm v b@
simplify :: SE -> SE
simplify = smtFunction "simplify" $ \expr ->
[sCase| expr of
Sqr (Con v) -> sCon (v * v)
Inc (Con v) -> sCon (v + 1)
Add (Con 0) r -> r
Add l (Con 0) -> l
Add (Con a) (Con b) -> sCon (a + b)
Mul (Con 0) _ -> sCon 0
Mul _ (Con 0) -> sCon 0
Mul (Con 1) r -> r
Mul l (Con 1) -> l
Mul (Con a) (Con b) -> sCon (a * b)
Let nm (Con v) b -> subst nm v b
-- fall-thru
_ -> expr
|]
-- * Substitution
-- | Substitute a variable with a value in an expression. Capture-avoiding:
-- if a @Let@-bound variable shadows the target, we do not substitute in the body.
--
-- * @Var x → if x == nm then Con v else Var x@
-- * @Con c → Con c@
-- * @Sqr a → Sqr (subst nm v a)@
-- * @Inc a → Inc (subst nm v a)@
-- * @Add a b → Add (subst nm v a) (subst nm v b)@
-- * @Mul a b → Mul (subst nm v a) (subst nm v b)@
-- * @Let x a b → Let x (subst nm v a) (if x == nm then b else subst nm v b)@
subst :: SString -> SInteger -> SE -> SE
subst = smtFunction "subst" $ \nm v expr ->
[sCase| expr of
-- Substitute for vars if name matches
Var x | x .== nm -> sCon v
| True -> sVar x
-- pass thru
Con c -> sCon c
Sqr a -> sSqr (subst nm v a)
Inc a -> sInc (subst nm v a)
Add a b -> sAdd (subst nm v a) (subst nm v b)
Mul a b -> sMul (subst nm v a) (subst nm v b)
-- substitute in the definition, but only substitute in the body if the name is not shadowing
Let x a b | x .== nm -> sLet x (subst nm v a) b
| True -> sLet x (subst nm v a) (subst nm v b)
|]
-- * Constant folding
-- | Constant fold an expression bottom-up: first fold sub-expressions, then simplify.
cfold :: SE -> SE
cfold = smtFunction "cfold" $ \expr ->
[sCase| expr of
Var nm -> sVar nm
Con v -> sCon v
Sqr a -> simplify (sSqr (cfold a))
Inc a -> simplify (sInc (cfold a))
Add a b -> simplify (sAdd (cfold a) (cfold b))
Mul a b -> simplify (sMul (cfold a) (cfold b))
Let nm a b -> simplify (sLet nm (cfold a) (cfold b))
|]
-- * Correctness
-- | The size measure is always non-negative.
--
-- >>> runTP measureNonNeg
-- Lemma: measureNonNeg Q.E.D.
-- Functions proven terminating: exprSize
-- [Proven] measureNonNeg :: Ɐe ∷ (Expr String Integer) → Bool
measureNonNeg :: TP (Proof (Forall "e" Exp -> SBool))
measureNonNeg = inductiveLemma "measureNonNeg"
(\(Forall @"e" (e :: SE)) -> size e .>= 0)
[]
-- | Congruence for squaring: if @a == b@ then @a*a == b*b@.
--
-- >>> runTP sqrCong
-- Lemma: sqrCong Q.E.D.
-- [Proven] sqrCong :: Ɐa ∷ Integer → Ɐb ∷ Integer → Bool
sqrCong :: TP (Proof (Forall "a" Integer -> Forall "b" Integer -> SBool))
sqrCong = lemma "sqrCong"
(\(Forall @"a" (a :: SInteger)) (Forall @"b" b) ->
a .== b .=> a * a .== b * b) []
-- | Congruence for addition on the left: if @a == b@ then @a+c == b+c@.
--
-- >>> runTP addCongL
-- Lemma: addCongL Q.E.D.
-- [Proven] addCongL :: Ɐa ∷ Integer → Ɐb ∷ Integer → Ɐc ∷ Integer → Bool
addCongL :: TP (Proof (Forall "a" Integer -> Forall "b" Integer -> Forall "c" Integer -> SBool))
addCongL = lemma "addCongL"
(\(Forall @"a" (a :: SInteger)) (Forall @"b" b) (Forall @"c" c) ->
a .== b .=> a + c .== b + c) []
-- | Congruence for addition on the right: if @b == c@ then @a+b == a+c@.
--
-- >>> runTP addCongR
-- Lemma: addCongR Q.E.D.
-- [Proven] addCongR :: Ɐa ∷ Integer → Ɐb ∷ Integer → Ɐc ∷ Integer → Bool
addCongR :: TP (Proof (Forall "a" Integer -> Forall "b" Integer -> Forall "c" Integer -> SBool))
addCongR = lemma "addCongR"
(\(Forall @"a" (a :: SInteger)) (Forall @"b" b) (Forall @"c" c) ->
b .== c .=> a + b .== a + c) []
-- | Congruence for multiplication on the left: if @a == b@ then @a*c == b*c@.
--
-- >>> runTP mulCongL
-- Lemma: mulCongL Q.E.D.
-- [Proven] mulCongL :: Ɐa ∷ Integer → Ɐb ∷ Integer → Ɐc ∷ Integer → Bool
mulCongL :: TP (Proof (Forall "a" Integer -> Forall "b" Integer -> Forall "c" Integer -> SBool))
mulCongL = lemma "mulCongL"
(\(Forall @"a" (a :: SInteger)) (Forall @"b" b) (Forall @"c" c) ->
a .== b .=> a * c .== b * c) []
-- | Congruence for multiplication on the right: if @b == c@ then @a*b == a*c@.
--
-- >>> runTP mulCongR
-- Lemma: mulCongR Q.E.D.
-- [Proven] mulCongR :: Ɐa ∷ Integer → Ɐb ∷ Integer → Ɐc ∷ Integer → Bool
mulCongR :: TP (Proof (Forall "a" Integer -> Forall "b" Integer -> Forall "c" Integer -> SBool))
mulCongR = lemma "mulCongR"
(\(Forall @"a" (a :: SInteger)) (Forall @"b" b) (Forall @"c" c) ->
b .== c .=> a * b .== a * c) []
-- | Unfolding @interpInEnv@ over @Sqr@.
--
-- >>> runTP sqrHelper
-- Lemma: sqrHelper Q.E.D.
-- Functions proven terminating: interpInEnv, sbv.lookup
-- [Proven] sqrHelper :: Ɐenv ∷ [(String, Integer)] → Ɐa ∷ (Expr String Integer) → Bool
sqrHelper :: TP (Proof (Forall "env" EL -> Forall "a" Exp -> SBool))
sqrHelper = lemma "sqrHelper"
(\(Forall @"env" (env :: E)) (Forall @"a" a) ->
interpInEnv env (sSqr a) .== interpInEnv env a * interpInEnv env a) []
-- | Unfolding @interpInEnv@ over @Add@.
--
-- >>> runTP addHelper
-- Lemma: addHelper Q.E.D.
-- Functions proven terminating: interpInEnv, sbv.lookup
-- [Proven] addHelper :: Ɐenv ∷ [(String, Integer)] → Ɐa ∷ (Expr String Integer) → Ɐb ∷ (Expr String Integer) → Bool
addHelper :: TP (Proof (Forall "env" EL -> Forall "a" Exp -> Forall "b" Exp -> SBool))
addHelper = lemma "addHelper"
(\(Forall @"env" (env :: E)) (Forall @"a" a) (Forall @"b" b) ->
interpInEnv env (sAdd a b) .== interpInEnv env a + interpInEnv env b) []
-- | Unfolding @interpInEnv@ over @Mul@.
--
-- >>> runTP mulHelper
-- Lemma: mulHelper Q.E.D.
-- Functions proven terminating: interpInEnv, sbv.lookup
-- [Proven] mulHelper :: Ɐenv ∷ [(String, Integer)] → Ɐa ∷ (Expr String Integer) → Ɐb ∷ (Expr String Integer) → Bool
mulHelper :: TP (Proof (Forall "env" EL -> Forall "a" Exp -> Forall "b" Exp -> SBool))
mulHelper = lemma "mulHelper"
(\(Forall @"env" (env :: E)) (Forall @"a" a) (Forall @"b" b) ->
interpInEnv env (sMul a b) .== interpInEnv env a * interpInEnv env b) []
-- | Unfolding @interpInEnv@ over @Let@.
--
-- >>> runTP letHelper
-- Lemma: letHelper Q.E.D.
-- Functions proven terminating: interpInEnv, sbv.lookup
-- [Proven] letHelper :: Ɐenv ∷ [(String, Integer)] → Ɐnm ∷ String → Ɐa ∷ (Expr String Integer) → Ɐb ∷ (Expr String Integer) → Bool
letHelper :: TP (Proof (Forall "env" EL -> Forall "nm" String -> Forall "a" Exp -> Forall "b" Exp -> SBool))
letHelper = lemma "letHelper"
(\(Forall @"env" (env :: E)) (Forall @"nm" nm) (Forall @"a" a) (Forall @"b" b) ->
interpInEnv env (sLet nm a b) .== interpInEnv (ST.tuple (nm, interpInEnv env a) .: env) b) []
-- * Environment lemmas
-- | Swapping two adjacent bindings with distinct keys does not affect lookup.
--
-- >>> runTP lookupSwap
-- Lemma: lookupSwap
-- Step: 1 (2 way case split)
-- Step: 1.1 Q.E.D.
-- Step: 1.2.1 Q.E.D.
-- Step: 1.2.2 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: sbv.lookup
-- [Proven] lookupSwap :: Ɐk ∷ String → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Ɐenv ∷ [(String, Integer)] → Bool
lookupSwap :: TP (Proof (Forall "k" String -> Forall "b1" (String, Integer)
-> Forall "b2" (String, Integer) -> Forall "env" EL -> SBool))
lookupSwap = calc "lookupSwap"
(\(Forall @"k" (k :: SString)) (Forall @"b1" (b1 :: STuple String Integer))
(Forall @"b2" (b2 :: STuple String Integer)) (Forall @"env" (env :: E)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x ./= y .=> SL.lookup k (b1 .: b2 .: env) .== SL.lookup k (b2 .: b1 .: env)) $
\k b1 b2 env ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in [x ./= y]
|- cases [ k .== x
==> SL.lookup k (b1 .: b2 .: env)
=: SL.lookup k (b2 .: b1 .: env)
=: qed
, k ./= x
==> SL.lookup k (b1 .: b2 .: env)
=: SL.lookup k (b2 .: env)
=: SL.lookup k (b2 .: b1 .: env)
=: qed
]
-- | One-step unfolding of 'SL.lookup' on a cons cell. The solver can expand the
-- @define-fun-rec@ but struggles to fold it back, so we provide this as a reusable hint.
--
-- >>> runTP lookupCons
-- Lemma: lookupCons Q.E.D.
-- Functions proven terminating: sbv.lookup
-- [Proven] lookupCons :: Ɐk ∷ String → Ɐb ∷ (String, Integer) → Ɐrest ∷ [(String, Integer)] → Bool
lookupCons :: TP (Proof (Forall "k" String -> Forall "b" (String, Integer) -> Forall "rest" EL -> SBool))
lookupCons = lemma "lookupCons"
(\(Forall @"k" (k :: SString)) (Forall @"b" (b :: STuple String Integer)) (Forall @"rest" (rest :: E)) ->
let (bk, bv) = ST.untuple b
in SL.lookup k (b .: rest) .== ite (k .== bk) bv (SL.lookup k rest))
[]
-- | Generalized swap: swapping two adjacent distinct-keyed bindings behind
-- a prefix does not affect lookup.
--
-- >>> runTP lookupSwapPfx
-- Lemma: lookupSwap Q.E.D.
-- Lemma: lookupCons Q.E.D.
-- Inductive lemma (strong): lookupSwapPfx
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (2 way case split)
-- Step: 1.1 (base) Q.E.D.
-- Step: 1.2.1 (cons) Q.E.D.
-- Step: 1.2.2 Q.E.D.
-- Step: 1.2.3 Q.E.D.
-- Step: 1.2.4 Q.E.D.
-- Step: 1.2.5 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: sbv.lookup
-- [Proven] lookupSwapPfx :: Ɐpfx ∷ [(String, Integer)] → Ɐk ∷ String → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Ɐenv ∷ [(String, Integer)] → Bool
lookupSwapPfx :: TP (Proof (Forall "pfx" EL -> Forall "k" String -> Forall "b1" (String, Integer)
-> Forall "b2" (String, Integer) -> Forall "env" EL -> SBool))
lookupSwapPfx = do
lkS <- recall lookupSwap
lkC <- recall lookupCons
sInduct "lookupSwapPfx"
(\(Forall @"pfx" (pfx :: E)) (Forall @"k" (k :: SString)) (Forall @"b1" (b1 :: STuple String Integer))
(Forall @"b2" (b2 :: STuple String Integer)) (Forall @"env" (env :: E)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x ./= y .=> SL.lookup k (pfx ++ b1 .: b2 .: env)
.== SL.lookup k (pfx ++ b2 .: b1 .: env))
(\pfx _ _ _ _ -> SL.length pfx :: SInteger, []) $
\ih pfx k b1 b2 env ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in [x ./= y]
|- cases [ SL.null pfx
==> SL.lookup k (pfx ++ b1 .: b2 .: env)
?? "base"
?? lkS `at` (Inst @"k" k, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: SL.lookup k (pfx ++ b2 .: b1 .: env)
=: qed
, sNot (SL.null pfx)
==> let h = SL.head pfx
t = SL.tail pfx
(hk, hv) = ST.untuple h
in SL.lookup k (pfx ++ b1 .: b2 .: env)
?? "cons"
?? pfx .== h .: t
=: SL.lookup k (h .: (t ++ b1 .: b2 .: env))
=: ite (k .== hk) hv (SL.lookup k (t ++ b1 .: b2 .: env))
?? ih `at` (Inst @"pfx" t, Inst @"k" k, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: ite (k .== hk) hv (SL.lookup k (t ++ b2 .: b1 .: env))
?? lkC `at` (Inst @"k" k, Inst @"b" h, Inst @"rest" (t ++ b2 .: b1 .: env))
=: SL.lookup k (h .: (t ++ b2 .: b1 .: env))
=: SL.lookup k (pfx ++ b2 .: b1 .: env)
=: qed
]
-- | A shadowed binding does not affect lookup: if the same key appears first, the second is irrelevant.
--
-- >>> runTP lookupShadow
-- Lemma: lookupShadow Q.E.D.
-- Functions proven terminating: sbv.lookup
-- [Proven] lookupShadow :: Ɐk ∷ String → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Ɐenv ∷ [(String, Integer)] → Bool
lookupShadow :: TP (Proof (Forall "k" String -> Forall "b1" (String, Integer)
-> Forall "b2" (String, Integer) -> Forall "env" EL -> SBool))
lookupShadow = lemma "lookupShadow"
(\(Forall @"k" (k :: SString)) (Forall @"b1" (b1 :: STuple String Integer))
(Forall @"b2" (b2 :: STuple String Integer)) (Forall @"env" (env :: E)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x .== y .=> SL.lookup k (b1 .: b2 .: env)
.== SL.lookup k (b1 .: env))
[]
-- | Generalized shadow: a shadowed binding behind a prefix does not affect lookup.
--
-- >>> runTP lookupShadowPfx
-- Lemma: lookupShadow Q.E.D.
-- Lemma: lookupCons Q.E.D.
-- Inductive lemma (strong): lookupShadowPfx
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (2 way case split)
-- Step: 1.1 (base) Q.E.D.
-- Step: 1.2.1 (cons) Q.E.D.
-- Step: 1.2.2 Q.E.D.
-- Step: 1.2.3 Q.E.D.
-- Step: 1.2.4 Q.E.D.
-- Step: 1.2.5 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: sbv.lookup
-- [Proven] lookupShadowPfx :: Ɐpfx ∷ [(String, Integer)] → Ɐk ∷ String → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Ɐenv ∷ [(String, Integer)] → Bool
lookupShadowPfx :: TP (Proof (Forall "pfx" EL -> Forall "k" String -> Forall "b1" (String, Integer)
-> Forall "b2" (String, Integer) -> Forall "env" EL -> SBool))
lookupShadowPfx = do
lkSh <- recall lookupShadow
lkC <- recall lookupCons
sInduct "lookupShadowPfx"
(\(Forall @"pfx" (pfx :: E)) (Forall @"k" (k :: SString)) (Forall @"b1" (b1 :: STuple String Integer))
(Forall @"b2" (b2 :: STuple String Integer)) (Forall @"env" (env :: E)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x .== y .=> SL.lookup k (pfx ++ b1 .: b2 .: env)
.== SL.lookup k (pfx ++ b1 .: env))
(\pfx _ _ _ _ -> SL.length pfx :: SInteger, []) $
\ih pfx k b1 b2 env ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in [x .== y]
|- cases [ SL.null pfx
==> SL.lookup k (pfx ++ b1 .: b2 .: env)
?? "base"
?? lkSh `at` (Inst @"k" k, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: SL.lookup k (pfx ++ b1 .: env)
=: qed
, sNot (SL.null pfx)
==> let h = SL.head pfx
t = SL.tail pfx
(hk, hv) = ST.untuple h
in SL.lookup k (pfx ++ b1 .: b2 .: env)
?? "cons"
?? pfx .== h .: t
=: SL.lookup k (h .: (t ++ b1 .: b2 .: env))
=: ite (k .== hk) hv (SL.lookup k (t ++ b1 .: b2 .: env))
?? ih `at` (Inst @"pfx" t, Inst @"k" k, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: ite (k .== hk) hv (SL.lookup k (t ++ b1 .: env))
?? lkC `at` (Inst @"k" k, Inst @"b" h, Inst @"rest" (t ++ b1 .: env))
=: SL.lookup k (h .: (t ++ b1 .: env))
=: SL.lookup k (pfx ++ b1 .: env)
=: qed
]
-- | Swapping two adjacent distinct-keyed bindings in the environment
-- does not affect interpretation. The @pfx@ parameter allows the swap
-- to happen at any depth in the environment.
--
-- >>> runTPWith cvc5 envSwap
-- Lemma: measureNonNeg Q.E.D.
-- Lemma: lookupSwapPfx Q.E.D.
-- Lemma: sqrCong Q.E.D.
-- Lemma: sqrHelper Q.E.D.
-- Lemma: addCongL Q.E.D.
-- Lemma: addCongR Q.E.D.
-- Lemma: addHelper Q.E.D.
-- Lemma: mulCongL Q.E.D.
-- Lemma: mulCongR Q.E.D.
-- Lemma: mulHelper Q.E.D.
-- Lemma: letHelper Q.E.D.
-- Inductive lemma (strong): envSwap
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (7 way case split)
-- Step: 1.1 (Var) Q.E.D.
-- Step: 1.2 (Con) Q.E.D.
-- Step: 1.3.1 (Sqr) Q.E.D.
-- Step: 1.3.2 Q.E.D.
-- Step: 1.3.3 Q.E.D.
-- Step: 1.4 (Inc) Q.E.D.
-- Step: 1.5.1 (Add) Q.E.D.
-- Step: 1.5.2 Q.E.D.
-- Step: 1.5.3 Q.E.D.
-- Step: 1.5.4 Q.E.D.
-- Step: 1.6.1 (Mul) Q.E.D.
-- Step: 1.6.2 Q.E.D.
-- Step: 1.6.3 Q.E.D.
-- Step: 1.6.4 Q.E.D.
-- Step: 1.7.1 (Let) Q.E.D.
-- Step: 1.7.2 Q.E.D.
-- Step: 1.7.3 Q.E.D.
-- Step: 1.7.4 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: exprSize, interpInEnv, sbv.lookup
-- [Proven] envSwap :: Ɐe ∷ (Expr String Integer) → Ɐpfx ∷ [(String, Integer)] → Ɐenv ∷ [(String, Integer)] → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Bool
envSwap :: TP (Proof (Forall "e" Exp -> Forall "pfx" EL -> Forall "env" EL
-> Forall "b1" (String, Integer) -> Forall "b2" (String, Integer) -> SBool))
envSwap = do
mnn <- recall measureNonNeg
lkSP <- recall lookupSwapPfx
sqrC <- recall sqrCong
sqrH <- recall sqrHelper
addCL <- recall addCongL
addCR <- recall addCongR
addH <- recall addHelper
mulCL <- recall mulCongL
mulCR <- recall mulCongR
mulH <- recall mulHelper
letH <- recall letHelper
sInduct "envSwap"
(\(Forall @"e" (e :: SE)) (Forall @"pfx" (pfx :: E)) (Forall @"env" (env :: E))
(Forall @"b1" (b1 :: STuple String Integer)) (Forall @"b2" (b2 :: STuple String Integer)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x ./= y .=> interpInEnv (pfx ++ b1 .: b2 .: env) e .== interpInEnv (pfx ++ b2 .: b1 .: env) e)
(\e _ _ _ _ -> size e :: SInteger, [proofOf mnn]) $
\ih e pfx env b1 b2 ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
env1 = pfx ++ b1 .: b2 .: env
env2 = pfx ++ b2 .: b1 .: env
in [x ./= y]
|- cases [ isVar e
==> let nm = svar e
in interpInEnv env1 (sVar nm)
?? "Var"
?? lkSP `at` (Inst @"pfx" pfx, Inst @"k" nm, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: interpInEnv env2 (sVar nm)
=: qed
, isCon e
==> let v = scon e
in interpInEnv env1 (sCon v)
?? "Con"
=: interpInEnv env2 (sCon v)
=: qed
, isSqr e
==> let a = ssqrVal e
in interpInEnv env1 (sSqr a)
?? "Sqr"
?? sqrH `at` (Inst @"env" env1, Inst @"a" a)
=: interpInEnv env1 a * interpInEnv env1 a
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? sqrC `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a))
=: interpInEnv env2 a * interpInEnv env2 a
?? sqrH `at` (Inst @"env" env2, Inst @"a" a)
=: interpInEnv env2 (sSqr a)
=: qed
, isInc e
==> let a = sincVal e
in interpInEnv env1 (sInc a)
?? "Inc"
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv env2 (sInc a)
=: qed
, isAdd e
==> let a = sadd1 e
b = sadd2 e
in interpInEnv env1 (sAdd a b)
?? "Add"
?? addH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a + interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? addCL `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a), Inst @"c" (interpInEnv env1 b))
=: interpInEnv env2 a + interpInEnv env1 b
?? ih `at` (Inst @"e" b, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? addCR `at` (Inst @"a" (interpInEnv env2 a), Inst @"b" (interpInEnv env1 b), Inst @"c" (interpInEnv env2 b))
=: interpInEnv env2 a + interpInEnv env2 b
?? addH `at` (Inst @"env" env2, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sAdd a b)
=: qed
, isMul e
==> let a = smul1 e
b = smul2 e
in interpInEnv env1 (sMul a b)
?? "Mul"
?? mulH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a * interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? mulCL `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a), Inst @"c" (interpInEnv env1 b))
=: interpInEnv env2 a * interpInEnv env1 b
?? ih `at` (Inst @"e" b, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? mulCR `at` (Inst @"a" (interpInEnv env2 a), Inst @"b" (interpInEnv env1 b), Inst @"c" (interpInEnv env2 b))
=: interpInEnv env2 a * interpInEnv env2 b
?? mulH `at` (Inst @"env" env2, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sMul a b)
=: qed
, isLet e
==> let nm = slvar e
a = slval e
b = slbody e
val1 = interpInEnv env1 a
val2 = interpInEnv env2 a
in interpInEnv env1 (sLet nm a b)
?? "Let"
?? letH `at` (Inst @"env" env1, Inst @"nm" nm, Inst @"a" a, Inst @"b" b)
=: interpInEnv (ST.tuple (nm, val1) .: env1) b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv (ST.tuple (nm, val2) .: env1) b
?? ih `at` (Inst @"e" b, Inst @"pfx" (ST.tuple (nm, val2) .: pfx), Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv (ST.tuple (nm, val2) .: env2) b
?? letH `at` (Inst @"env" env2, Inst @"nm" nm, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sLet nm a b)
=: qed
]
-- | A shadowed binding in the environment does not affect interpretation.
-- The @pfx@ parameter allows the shadow to occur at any depth.
--
-- >>> runTPWith cvc5 envShadow
-- Lemma: measureNonNeg Q.E.D.
-- Lemma: lookupShadowPfx Q.E.D.
-- Lemma: sqrCong Q.E.D.
-- Lemma: sqrHelper Q.E.D.
-- Lemma: addCongL Q.E.D.
-- Lemma: addCongR Q.E.D.
-- Lemma: addHelper Q.E.D.
-- Lemma: mulCongL Q.E.D.
-- Lemma: mulCongR Q.E.D.
-- Lemma: mulHelper Q.E.D.
-- Lemma: letHelper Q.E.D.
-- Inductive lemma (strong): envShadow
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (7 way case split)
-- Step: 1.1 (Var) Q.E.D.
-- Step: 1.2 (Con) Q.E.D.
-- Step: 1.3.1 (Sqr) Q.E.D.
-- Step: 1.3.2 Q.E.D.
-- Step: 1.3.3 Q.E.D.
-- Step: 1.4 (Inc) Q.E.D.
-- Step: 1.5.1 (Add) Q.E.D.
-- Step: 1.5.2 Q.E.D.
-- Step: 1.5.3 Q.E.D.
-- Step: 1.5.4 Q.E.D.
-- Step: 1.6.1 (Mul) Q.E.D.
-- Step: 1.6.2 Q.E.D.
-- Step: 1.6.3 Q.E.D.
-- Step: 1.6.4 Q.E.D.
-- Step: 1.7.1 (Let) Q.E.D.
-- Step: 1.7.2 Q.E.D.
-- Step: 1.7.3 Q.E.D.
-- Step: 1.7.4 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: exprSize, interpInEnv, sbv.lookup
-- [Proven] envShadow :: Ɐe ∷ (Expr String Integer) → Ɐpfx ∷ [(String, Integer)] → Ɐenv ∷ [(String, Integer)] → Ɐb1 ∷ (String, Integer) → Ɐb2 ∷ (String, Integer) → Bool
envShadow :: TP (Proof (Forall "e" Exp -> Forall "pfx" EL -> Forall "env" EL
-> Forall "b1" (String, Integer) -> Forall "b2" (String, Integer) -> SBool))
envShadow = do
mnn <- recall measureNonNeg
lkShP <- recall lookupShadowPfx
sqrC <- recall sqrCong
sqrH <- recall sqrHelper
addCL <- recall addCongL
addCR <- recall addCongR
addH <- recall addHelper
mulCL <- recall mulCongL
mulCR <- recall mulCongR
mulH <- recall mulHelper
letH <- recall letHelper
sInduct "envShadow"
(\(Forall @"e" (e :: SE)) (Forall @"pfx" (pfx :: E)) (Forall @"env" (env :: E))
(Forall @"b1" (b1 :: STuple String Integer)) (Forall @"b2" (b2 :: STuple String Integer)) ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
in x .== y .=> interpInEnv (pfx ++ b1 .: b2 .: env) e .== interpInEnv (pfx ++ b1 .: env) e)
(\e _ _ _ _ -> size e :: SInteger, [proofOf mnn]) $
\ih e pfx env b1 b2 ->
let (x, _) = ST.untuple b1
(y, _) = ST.untuple b2
env1 = pfx ++ b1 .: b2 .: env
env2 = pfx ++ b1 .: env
in [x .== y]
|- cases [ isVar e
==> let nm = svar e
in interpInEnv env1 (sVar nm)
?? "Var"
?? lkShP `at` (Inst @"pfx" pfx, Inst @"k" nm, Inst @"b1" b1, Inst @"b2" b2, Inst @"env" env)
=: interpInEnv env2 (sVar nm)
=: qed
, isCon e
==> let v = scon e
in interpInEnv env1 (sCon v)
?? "Con"
=: interpInEnv env2 (sCon v)
=: qed
, isSqr e
==> let a = ssqrVal e
in interpInEnv env1 (sSqr a)
?? "Sqr"
?? sqrH `at` (Inst @"env" env1, Inst @"a" a)
=: interpInEnv env1 a * interpInEnv env1 a
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? sqrC `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a))
=: interpInEnv env2 a * interpInEnv env2 a
?? sqrH `at` (Inst @"env" env2, Inst @"a" a)
=: interpInEnv env2 (sSqr a)
=: qed
, isInc e
==> let a = sincVal e
in interpInEnv env1 (sInc a)
?? "Inc"
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv env2 (sInc a)
=: qed
, isAdd e
==> let a = sadd1 e
b = sadd2 e
in interpInEnv env1 (sAdd a b)
?? "Add"
?? addH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a + interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? addCL `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a), Inst @"c" (interpInEnv env1 b))
=: interpInEnv env2 a + interpInEnv env1 b
?? ih `at` (Inst @"e" b, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? addCR `at` (Inst @"a" (interpInEnv env2 a), Inst @"b" (interpInEnv env1 b), Inst @"c" (interpInEnv env2 b))
=: interpInEnv env2 a + interpInEnv env2 b
?? addH `at` (Inst @"env" env2, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sAdd a b)
=: qed
, isMul e
==> let a = smul1 e
b = smul2 e
in interpInEnv env1 (sMul a b)
?? "Mul"
?? mulH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a * interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? mulCL `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env2 a), Inst @"c" (interpInEnv env1 b))
=: interpInEnv env2 a * interpInEnv env1 b
?? ih `at` (Inst @"e" b, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
?? mulCR `at` (Inst @"a" (interpInEnv env2 a), Inst @"b" (interpInEnv env1 b), Inst @"c" (interpInEnv env2 b))
=: interpInEnv env2 a * interpInEnv env2 b
?? mulH `at` (Inst @"env" env2, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sMul a b)
=: qed
, isLet e
==> let nm = slvar e
a = slval e
b = slbody e
val1 = interpInEnv env1 a
val2 = interpInEnv env2 a
in interpInEnv env1 (sLet nm a b)
?? "Let"
?? letH `at` (Inst @"env" env1, Inst @"nm" nm, Inst @"a" a, Inst @"b" b)
=: interpInEnv (ST.tuple (nm, val1) .: env1) b
?? ih `at` (Inst @"e" a, Inst @"pfx" pfx, Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv (ST.tuple (nm, val2) .: env1) b
?? ih `at` (Inst @"e" b, Inst @"pfx" (ST.tuple (nm, val2) .: pfx), Inst @"env" env, Inst @"b1" b1, Inst @"b2" b2)
=: interpInEnv (ST.tuple (nm, val2) .: env2) b
?? letH `at` (Inst @"env" env2, Inst @"nm" nm, Inst @"a" a, Inst @"b" b)
=: interpInEnv env2 (sLet nm a b)
=: qed
]
-- * Substitution correctness
-- | Unfolding @interpInEnv@ over @Var@.
--
-- >>> runTP varHelper
-- Lemma: varHelper Q.E.D.
-- Functions proven terminating: interpInEnv, sbv.lookup
-- [Proven] varHelper :: Ɐenv ∷ [(String, Integer)] → Ɐnm ∷ String → Bool
varHelper :: TP (Proof (Forall "env" EL -> Forall "nm" String -> SBool))
varHelper = lemma "varHelper"
(\(Forall @"env" (env :: E)) (Forall @"nm" nm) ->
interpInEnv env (sVar nm) .== SL.lookup nm env) []
-- | Substitution preserves semantics: interpreting in an extended environment
-- is the same as substituting and interpreting in the original environment.
--
-- >>> runTPWith cvc5 substCorrect
-- Lemma: measureNonNeg Q.E.D.
-- Lemma: sqrCong Q.E.D.
-- Lemma: sqrHelper Q.E.D.
-- Lemma: addHelper Q.E.D.
-- Lemma: mulCongL Q.E.D.
-- Lemma: mulCongR Q.E.D.
-- Lemma: mulHelper Q.E.D.
-- Lemma: letHelper Q.E.D.
-- Lemma: varHelper Q.E.D.
-- Lemma: envSwap Q.E.D.
-- Lemma: envShadow Q.E.D.
-- Inductive lemma (strong): substCorrect
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (7 way case split)
-- Step: 1.1 (2 way case split)
-- Step: 1.1.1.1 (Var) Q.E.D.
-- Step: 1.1.1.2 Q.E.D.
-- Step: 1.1.1.3 Q.E.D.
-- Step: 1.1.1.4 Q.E.D.
-- Step: 1.1.1.5 Q.E.D.
-- Step: 1.1.2.1 (Var) Q.E.D.
-- Step: 1.1.2.2 Q.E.D.
-- Step: 1.1.2.3 Q.E.D.
-- Step: 1.1.2.4 Q.E.D.
-- Step: 1.1.2.5 Q.E.D.
-- Step: 1.1.Completeness Q.E.D.
-- Step: 1.2 (Con) Q.E.D.
-- Step: 1.3.1 (Sqr) Q.E.D.
-- Step: 1.3.2 Q.E.D.
-- Step: 1.3.3 Q.E.D.
-- Step: 1.3.4 Q.E.D.
-- Step: 1.4 (Inc) Q.E.D.
-- Step: 1.5.1 (Add) Q.E.D.
-- Step: 1.5.2 Q.E.D.
-- Step: 1.5.3 Q.E.D.
-- Step: 1.5.4 Q.E.D.
-- Step: 1.6.1 (Mul) Q.E.D.
-- Step: 1.6.2 Q.E.D.
-- Step: 1.6.3 Q.E.D.
-- Step: 1.6.4 Q.E.D.
-- Step: 1.6.5 Q.E.D.
-- Step: 1.7.1 (Let) Q.E.D.
-- Step: 1.7.2 (2 way case split)
-- Step: 1.7.2.1.1 Q.E.D.
-- Step: 1.7.2.1.2 (shadow) Q.E.D.
-- Step: 1.7.2.1.3 Q.E.D.
-- Step: 1.7.2.1.4 Q.E.D.
-- Step: 1.7.2.1.5 Q.E.D.
-- Step: 1.7.2.2.1 Q.E.D.
-- Step: 1.7.2.2.2 (swap) Q.E.D.
-- Step: 1.7.2.2.3 Q.E.D.
-- Step: 1.7.2.2.4 Q.E.D.
-- Step: 1.7.2.2.5 Q.E.D.
-- Step: 1.7.2.2.6 Q.E.D.
-- Step: 1.7.2.Completeness Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: exprSize, interpInEnv, sbv.lookup, subst
-- [Proven] substCorrect :: Ɐe ∷ (Expr String Integer) → Ɐnm ∷ String → Ɐv ∷ Integer → Ɐenv ∷ [(String, Integer)] → Bool
substCorrect :: TP (Proof (Forall "e" Exp -> Forall "nm" String -> Forall "v" Integer -> Forall "env" EL -> SBool))
substCorrect = do
mnn <- recall measureNonNeg
sqrC <- recall sqrCong
sqrH <- recall sqrHelper
addH <- recall addHelper
mulCL <- recall mulCongL
mulCR <- recall mulCongR
mulH <- recall mulHelper
letH <- recall letHelper
varH <- recall varHelper
eSwp <- recall envSwap
eShd <- recall envShadow
sInduct "substCorrect"
(\(Forall @"e" (e :: SE)) (Forall @"nm" (nm :: SString)) (Forall @"v" (v :: SInteger)) (Forall @"env" (env :: E)) ->
interpInEnv (ST.tuple (nm, v) .: env) e .== interpInEnv env (subst nm v e))
(\e _ _ _ -> size e :: SInteger, [proofOf mnn]) $
\ih e nm v env ->
let nmv = ST.tuple (nm, v)
env1 = nmv .: env
in []
|- cases [ isVar e
==> let x = svar e
in interpInEnv env1 (sVar x)
?? "Var"
=: cases [ x .== nm
==> interpInEnv env1 (sVar nm)
?? varH `at` (Inst @"env" env1, Inst @"nm" nm)
=: SL.lookup nm env1
=: v
=: interpInEnv env (sCon v)
=: interpInEnv env (subst nm v (sVar nm))
=: qed
, x ./= nm
==> interpInEnv env1 (sVar x)
?? varH `at` (Inst @"env" env1, Inst @"nm" x)
=: SL.lookup x env1
=: SL.lookup x env
?? varH `at` (Inst @"env" env, Inst @"nm" x)
=: interpInEnv env (sVar x)
=: interpInEnv env (subst nm v (sVar x))
=: qed
]
, isCon e
==> let c = scon e
in interpInEnv env1 (sCon c)
?? "Con"
=: interpInEnv env (subst nm v (sCon c))
=: qed
, isSqr e
==> let a = ssqrVal e
in interpInEnv env1 (sSqr a)
?? "Sqr"
?? sqrH `at` (Inst @"env" env1, Inst @"a" a)
=: interpInEnv env1 a * interpInEnv env1 a
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
?? sqrC `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env (subst nm v a)))
=: interpInEnv env (subst nm v a) * interpInEnv env (subst nm v a)
?? sqrH `at` (Inst @"env" env, Inst @"a" (subst nm v a))
=: interpInEnv env (sSqr (subst nm v a))
=: interpInEnv env (subst nm v (sSqr a))
=: qed
, isInc e
==> let a = sincVal e
in interpInEnv env1 (sInc a)
?? "Inc"
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
=: interpInEnv env (subst nm v (sInc a))
=: qed
, isAdd e
==> let a = sadd1 e
b = sadd2 e
in interpInEnv env1 (sAdd a b)
?? "Add"
?? addH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a + interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
?? ih `at` (Inst @"e" b, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
=: interpInEnv env (subst nm v a) + interpInEnv env (subst nm v b)
?? addH `at` (Inst @"env" env, Inst @"a" (subst nm v a), Inst @"b" (subst nm v b))
=: interpInEnv env (sAdd (subst nm v a) (subst nm v b))
=: interpInEnv env (subst nm v (sAdd a b))
=: qed
, isMul e
==> let a = smul1 e
b = smul2 e
in interpInEnv env1 (sMul a b)
?? "Mul"
?? mulH `at` (Inst @"env" env1, Inst @"a" a, Inst @"b" b)
=: interpInEnv env1 a * interpInEnv env1 b
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
?? mulCL `at` (Inst @"a" (interpInEnv env1 a), Inst @"b" (interpInEnv env (subst nm v a)), Inst @"c" (interpInEnv env1 b))
=: interpInEnv env (subst nm v a) * interpInEnv env1 b
?? ih `at` (Inst @"e" b, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
?? mulCR `at` (Inst @"a" (interpInEnv env (subst nm v a)), Inst @"b" (interpInEnv env1 b), Inst @"c" (interpInEnv env (subst nm v b)))
=: interpInEnv env (subst nm v a) * interpInEnv env (subst nm v b)
?? mulH `at` (Inst @"env" env, Inst @"a" (subst nm v a), Inst @"b" (subst nm v b))
=: interpInEnv env (sMul (subst nm v a) (subst nm v b))
=: interpInEnv env (subst nm v (sMul a b))
=: qed
, isLet e
==> let x = slvar e
a = slval e
b = slbody e
val = interpInEnv env1 a
in interpInEnv env1 (sLet x a b)
?? "Let"
?? letH `at` (Inst @"env" env1, Inst @"nm" x, Inst @"a" a, Inst @"b" b)
=: interpInEnv (ST.tuple (x, val) .: env1) b
=: cases [ x .== nm
==> let xv = ST.tuple (x, val)
in interpInEnv (xv .: nmv .: env) b
?? "shadow"
?? eShd `at` (Inst @"e" b, Inst @"pfx" (SL.nil :: E), Inst @"env" env, Inst @"b1" xv, Inst @"b2" nmv)
=: interpInEnv (xv .: env) b
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
=: interpInEnv (ST.tuple (x, interpInEnv env (subst nm v a)) .: env) b
?? letH `at` (Inst @"env" env, Inst @"nm" x, Inst @"a" (subst nm v a), Inst @"b" b)
=: interpInEnv env (sLet x (subst nm v a) b)
=: interpInEnv env (subst nm v (sLet x a b))
=: qed
, x ./= nm
==> let xv = ST.tuple (x, val)
in interpInEnv (xv .: nmv .: env) b
?? "swap"
?? eSwp `at` (Inst @"e" b, Inst @"pfx" (SL.nil :: E), Inst @"env" env, Inst @"b1" xv, Inst @"b2" nmv)
=: interpInEnv (nmv .: xv .: env) b
?? ih `at` (Inst @"e" b, Inst @"nm" nm, Inst @"v" v, Inst @"env" (xv .: env))
=: interpInEnv (xv .: env) (subst nm v b)
?? ih `at` (Inst @"e" a, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
=: interpInEnv (ST.tuple (x, interpInEnv env (subst nm v a)) .: env) (subst nm v b)
?? letH `at` (Inst @"env" env, Inst @"nm" x, Inst @"a" (subst nm v a), Inst @"b" (subst nm v b))
=: interpInEnv env (sLet x (subst nm v a) (subst nm v b))
=: interpInEnv env (subst nm v (sLet x a b))
=: qed
]
]
-- | Simplification preserves semantics.
--
-- >>> runTPWith cvc5 simpCorrect
-- Lemma: sqrCong Q.E.D.
-- Lemma: sqrHelper Q.E.D.
-- Lemma: addHelper Q.E.D.
-- Lemma: mulCongL Q.E.D.
-- Lemma: mulCongR Q.E.D.
-- Lemma: mulHelper Q.E.D.
-- Lemma: letHelper Q.E.D.
-- Lemma: substCorrect Q.E.D.
-- Lemma: simpCorrect
-- Step: 1 (7 way case split)
-- Step: 1.1.1 (Var) Q.E.D.
-- Step: 1.1.2 Q.E.D.
-- Step: 1.1.3 Q.E.D.
-- Step: 1.2.1 (Con) Q.E.D.
-- Step: 1.2.2 Q.E.D.
-- Step: 1.2.3 Q.E.D.
-- Step: 1.3.1 (Sqr) Q.E.D.
-- Step: 1.3.2 (2 way case split)
-- Step: 1.3.2.1.1 Q.E.D.
-- Step: 1.3.2.1.2 (Sqr Con) Q.E.D.
-- Step: 1.3.2.1.3 Q.E.D.
-- Step: 1.3.2.1.4 Q.E.D.
-- Step: 1.3.2.1.5 Q.E.D.
-- Step: 1.3.2.2.1 Q.E.D.
-- Step: 1.3.2.2.2 (Sqr _) Q.E.D.
-- Step: 1.3.2.Completeness Q.E.D.
-- Step: 1.4.1 (Inc) Q.E.D.
-- Step: 1.4.2 (2 way case split)
-- Step: 1.4.2.1.1 Q.E.D.
-- Step: 1.4.2.1.2 (Inc Con) Q.E.D.
-- Step: 1.4.2.1.3 Q.E.D.
-- Step: 1.4.2.2.1 Q.E.D.
-- Step: 1.4.2.2.2 (Inc _) Q.E.D.
-- Step: 1.4.2.Completeness Q.E.D.
-- Step: 1.5.1 (Add) Q.E.D.
-- Step: 1.5.2 (6 way case split)
-- Step: 1.5.2.1.1 Q.E.D.
-- Step: 1.5.2.1.2 (Add 0+b) Q.E.D.
-- Step: 1.5.2.1.3 Q.E.D.
-- Step: 1.5.2.2.1 Q.E.D.
-- Step: 1.5.2.2.2 (Add a+0) Q.E.D.
-- Step: 1.5.2.2.3 Q.E.D.
-- Step: 1.5.2.3.1 Q.E.D.
-- Step: 1.5.2.3.2 (Add Con) Q.E.D.
-- Step: 1.5.2.3.3 Q.E.D.
-- Step: 1.5.2.4 (2 way case split)
-- Step: 1.5.2.4.1.1 Q.E.D.
-- Step: 1.5.2.4.1.2 (Add 0,_) Q.E.D.
-- Step: 1.5.2.4.1.3 Q.E.D.
-- Step: 1.5.2.4.2.1 Q.E.D.
-- Step: 1.5.2.4.2.2 (Add C,_) Q.E.D.
-- Step: 1.5.2.4.Completeness Q.E.D.
-- Step: 1.5.2.5 (2 way case split)
-- Step: 1.5.2.5.1.1 Q.E.D.
-- Step: 1.5.2.5.1.2 (Add _,0) Q.E.D.
-- Step: 1.5.2.5.1.3 Q.E.D.
-- Step: 1.5.2.5.2.1 Q.E.D.
-- Step: 1.5.2.5.2.2 (Add _,C) Q.E.D.
-- Step: 1.5.2.5.Completeness Q.E.D.
-- Step: 1.5.2.6.1 Q.E.D.
-- Step: 1.5.2.6.2 (Add _,_) Q.E.D.
-- Step: 1.5.2.Completeness Q.E.D.
-- Step: 1.6.1 (Mul) Q.E.D.
-- Step: 1.6.2 (8 way case split)
-- Step: 1.6.2.1.1 Q.E.D.
-- Step: 1.6.2.1.2 (Mul 0*b) Q.E.D.
-- Step: 1.6.2.1.3 Q.E.D.
-- Step: 1.6.2.2.1 Q.E.D.
-- Step: 1.6.2.2.2 (Mul a*0) Q.E.D.
-- Step: 1.6.2.2.3 Q.E.D.
-- Step: 1.6.2.3.1 Q.E.D.
-- Step: 1.6.2.3.2 (Mul 1*b) Q.E.D.
-- Step: 1.6.2.3.3 Q.E.D.
-- Step: 1.6.2.3.4 Q.E.D.
-- Step: 1.6.2.3.5 Q.E.D.
-- Step: 1.6.2.4.1 Q.E.D.
-- Step: 1.6.2.4.2 (Mul a*1) Q.E.D.
-- Step: 1.6.2.4.3 Q.E.D.
-- Step: 1.6.2.4.4 Q.E.D.
-- Step: 1.6.2.4.5 Q.E.D.
-- Step: 1.6.2.5.1 Q.E.D.
-- Step: 1.6.2.5.2 (Mul Con) Q.E.D.
-- Step: 1.6.2.5.3 Q.E.D.
-- Step: 1.6.2.5.4 Q.E.D.
-- Step: 1.6.2.5.5 Q.E.D.
-- Step: 1.6.2.5.6 Q.E.D.
-- Step: 1.6.2.6 (3 way case split)
-- Step: 1.6.2.6.1.1 Q.E.D.
-- Step: 1.6.2.6.1.2 (Mul 0,_) Q.E.D.
-- Step: 1.6.2.6.1.3 Q.E.D.
-- Step: 1.6.2.6.2.1 Q.E.D.
-- Step: 1.6.2.6.2.2 (Mul 1,_) Q.E.D.
-- Step: 1.6.2.6.2.3 Q.E.D.
-- Step: 1.6.2.6.2.4 Q.E.D.
-- Step: 1.6.2.6.2.5 Q.E.D.
-- Step: 1.6.2.6.3.1 Q.E.D.
-- Step: 1.6.2.6.3.2 (Mul C,_) Q.E.D.
-- Step: 1.6.2.6.Completeness Q.E.D.
-- Step: 1.6.2.7 (3 way case split)
-- Step: 1.6.2.7.1.1 Q.E.D.
-- Step: 1.6.2.7.1.2 (Mul _,0) Q.E.D.
-- Step: 1.6.2.7.1.3 Q.E.D.
-- Step: 1.6.2.7.2.1 Q.E.D.
-- Step: 1.6.2.7.2.2 (Mul _,1) Q.E.D.
-- Step: 1.6.2.7.2.3 Q.E.D.
-- Step: 1.6.2.7.2.4 Q.E.D.
-- Step: 1.6.2.7.2.5 Q.E.D.
-- Step: 1.6.2.7.3.1 Q.E.D.
-- Step: 1.6.2.7.3.2 (Mul _,C) Q.E.D.
-- Step: 1.6.2.7.Completeness Q.E.D.
-- Step: 1.6.2.8.1 Q.E.D.
-- Step: 1.6.2.8.2 (Mul _,_) Q.E.D.
-- Step: 1.6.2.Completeness Q.E.D.
-- Step: 1.7.1 (Let) Q.E.D.
-- Step: 1.7.2 (2 way case split)
-- Step: 1.7.2.1.1 Q.E.D.
-- Step: 1.7.2.1.2 (Let Con) Q.E.D.
-- Step: 1.7.2.1.3 Q.E.D.
-- Step: 1.7.2.1.4 Q.E.D.
-- Step: 1.7.2.2.1 Q.E.D.
-- Step: 1.7.2.2.2 (Let _) Q.E.D.
-- Step: 1.7.2.Completeness Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: exprSize, interpInEnv, sbv.lookup, simplify, subst
-- [Proven] simpCorrect :: Ɐe ∷ (Expr String Integer) → Ɐenv ∷ [(String, Integer)] → Bool
simpCorrect :: TP (Proof (Forall "e" Exp -> Forall "env" EL -> SBool))
simpCorrect = do
sqrC <- recall sqrCong
sqrH <- recall sqrHelper
addH <- recall addHelper
mulCL <- recall mulCongL
mulCR <- recall mulCongR
mulH <- recall mulHelper
letH <- recall letHelper
subC <- recall substCorrect
calc "simpCorrect"
(\(Forall @"e" (e :: SE)) (Forall @"env" (env :: E)) -> interpInEnv env (simplify e) .== interpInEnv env e) $
\e env -> []
|- [pCase| e of
Var nm -> interpInEnv env (simplify e)
?? "Var"
=: interpInEnv env (simplify (sVar nm))
=: interpInEnv env (sVar nm)
=: interpInEnv env e
=: qed
Con c -> interpInEnv env (simplify e)
?? "Con"
=: interpInEnv env (simplify (sCon c))
=: interpInEnv env (sCon c)
=: interpInEnv env e
=: qed
Sqr a -> interpInEnv env (simplify e)
?? "Sqr"
=: interpInEnv env (simplify (sSqr a))
=: cases [ isCon a
==> let v = scon a
in interpInEnv env (simplify (sSqr (sCon v)))
?? "Sqr Con"
=: interpInEnv env (sCon (v * v))
?? interpInEnv env (sCon (v * v)) .== v * v
=: v * v
?? sqrC `at` (Inst @"a" (interpInEnv env (sCon v)), Inst @"b" v)
=: interpInEnv env (sCon v) * interpInEnv env (sCon v)
?? sqrH `at` (Inst @"env" env, Inst @"a" (sCon v))
=: interpInEnv env (sSqr (sCon v))
=: qed
, sNot (isCon a)
==> interpInEnv env (simplify (sSqr a))
?? "Sqr _"
=: interpInEnv env (sSqr a)
=: qed
]
Inc a -> interpInEnv env (simplify e)
?? "Inc"
=: interpInEnv env (simplify (sInc a))
=: cases [ isCon a
==> let v = scon a
in interpInEnv env (simplify (sInc (sCon v)))
?? "Inc Con"
=: interpInEnv env (sCon (v + 1))
=: interpInEnv env (sInc (sCon v))
=: qed
, sNot (isCon a)
==> interpInEnv env (simplify (sInc a))
?? "Inc _"
=: interpInEnv env (sInc a)
=: qed
]
Add a b -> interpInEnv env (simplify e)
?? "Add"
=: interpInEnv env (simplify (sAdd a b))
=: cases [ isCon a .&& scon a .== 0
==> interpInEnv env (simplify (sAdd (sCon 0) b))
?? "Add 0+b"
=: interpInEnv env b
?? addH `at` (Inst @"env" env, Inst @"a" (sCon 0), Inst @"b" b)
=: interpInEnv env (sAdd (sCon 0) b)
=: qed
, isCon b .&& scon b .== 0
==> interpInEnv env (simplify (sAdd a (sCon 0)))
?? "Add a+0"
=: interpInEnv env a
?? addH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 0))
=: interpInEnv env (sAdd a (sCon 0))
=: qed
, isCon a .&& isCon b
==> let va = scon a; vb = scon b
in interpInEnv env (simplify (sAdd (sCon va) (sCon vb)))
?? "Add Con"
=: interpInEnv env (sCon (va + vb))
?? addH `at` (Inst @"env" env, Inst @"a" (sCon va), Inst @"b" (sCon vb))
=: interpInEnv env (sAdd (sCon va) (sCon vb))
=: qed
, isCon a .&& sNot (isCon b)
==> let va = scon a
in cases [ va .== 0
==> interpInEnv env (simplify (sAdd (sCon 0) b))
?? "Add 0,_"
=: interpInEnv env b
?? addH `at` (Inst @"env" env, Inst @"a" (sCon 0), Inst @"b" b)
=: interpInEnv env (sAdd (sCon 0) b)
=: qed
, va ./= 0
==> interpInEnv env (simplify (sAdd (sCon va) b))
?? "Add C,_"
=: interpInEnv env (sAdd (sCon va) b)
=: qed
]
, sNot (isCon a) .&& isCon b
==> let vb = scon b
in cases [ vb .== 0
==> interpInEnv env (simplify (sAdd a (sCon 0)))
?? "Add _,0"
=: interpInEnv env a
?? addH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 0))
=: interpInEnv env (sAdd a (sCon 0))
=: qed
, vb ./= 0
==> interpInEnv env (simplify (sAdd a (sCon vb)))
?? "Add _,C"
=: interpInEnv env (sAdd a (sCon vb))
=: qed
]
, sNot (isCon a) .&& sNot (isCon b)
==> interpInEnv env (simplify (sAdd a b))
?? "Add _,_"
=: interpInEnv env (sAdd a b)
=: qed
]
Mul a b -> interpInEnv env (simplify e)
?? "Mul"
=: interpInEnv env (simplify (sMul a b))
=: cases [ isCon a .&& scon a .== 0
==> interpInEnv env (simplify (sMul (sCon 0) b))
?? "Mul 0*b"
=: interpInEnv env (sCon 0)
?? mulH `at` (Inst @"env" env, Inst @"a" (sCon 0), Inst @"b" b)
=: interpInEnv env (sMul (sCon 0) b)
=: qed
, isCon b .&& scon b .== 0
==> interpInEnv env (simplify (sMul a (sCon 0)))
?? "Mul a*0"
=: interpInEnv env (sCon 0)
?? mulH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 0))
=: interpInEnv env (sMul a (sCon 0))
=: qed
, isCon a .&& scon a .== 1
==> interpInEnv env (simplify (sMul (sCon 1) b))
?? "Mul 1*b"
=: interpInEnv env b
=: 1 * interpInEnv env b
?? interpInEnv env (sCon 1) .== 1
=: interpInEnv env (sCon 1) * interpInEnv env b
?? mulH `at` (Inst @"env" env, Inst @"a" (sCon 1), Inst @"b" b)
=: interpInEnv env (sMul (sCon 1) b)
=: qed
, isCon b .&& scon b .== 1
==> interpInEnv env (simplify (sMul a (sCon 1)))
?? "Mul a*1"
=: interpInEnv env a
=: interpInEnv env a * 1
?? interpInEnv env (sCon 1) .== 1
=: interpInEnv env a * interpInEnv env (sCon 1)
?? mulH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 1))
=: interpInEnv env (sMul a (sCon 1))
=: qed
, isCon a .&& isCon b
==> let va = scon a; vb = scon b
in interpInEnv env (simplify (sMul (sCon va) (sCon vb)))
?? "Mul Con"
?? simplify (sMul (sCon va) (sCon vb)) .== sCon (va * vb)
=: interpInEnv env (sCon (va * vb))
?? interpInEnv env (sCon (va * vb)) .== va * vb
=: va * vb
?? mulCL `at` (Inst @"a" (interpInEnv env (sCon va)), Inst @"b" va, Inst @"c" vb)
=: interpInEnv env (sCon va) * vb
?? mulCR `at` (Inst @"a" (interpInEnv env (sCon va)), Inst @"b" (interpInEnv env (sCon vb)), Inst @"c" vb)
=: interpInEnv env (sCon va) * interpInEnv env (sCon vb)
?? mulH `at` (Inst @"env" env, Inst @"a" (sCon va), Inst @"b" (sCon vb))
=: interpInEnv env (sMul (sCon va) (sCon vb))
=: qed
, isCon a .&& sNot (isCon b)
==> let va = scon a
in cases [ va .== 0
==> interpInEnv env (simplify (sMul (sCon 0) b))
?? "Mul 0,_"
=: interpInEnv env (sCon 0)
?? mulH `at` (Inst @"env" env, Inst @"a" (sCon 0), Inst @"b" b)
=: interpInEnv env (sMul (sCon 0) b)
=: qed
, va .== 1
==> interpInEnv env (simplify (sMul (sCon 1) b))
?? "Mul 1,_"
=: interpInEnv env b
=: 1 * interpInEnv env b
?? interpInEnv env (sCon 1) .== 1
=: interpInEnv env (sCon 1) * interpInEnv env b
?? mulH `at` (Inst @"env" env, Inst @"a" (sCon 1), Inst @"b" b)
=: interpInEnv env (sMul (sCon 1) b)
=: qed
, va ./= 0 .&& va ./= 1
==> interpInEnv env (simplify (sMul (sCon va) b))
?? "Mul C,_"
=: interpInEnv env (sMul (sCon va) b)
=: qed
]
, sNot (isCon a) .&& isCon b
==> let vb = scon b
in cases [ vb .== 0
==> interpInEnv env (simplify (sMul a (sCon 0)))
?? "Mul _,0"
=: interpInEnv env (sCon 0)
?? mulH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 0))
=: interpInEnv env (sMul a (sCon 0))
=: qed
, vb .== 1
==> interpInEnv env (simplify (sMul a (sCon 1)))
?? "Mul _,1"
=: interpInEnv env a
=: interpInEnv env a * 1
?? interpInEnv env (sCon 1) .== 1
=: interpInEnv env a * interpInEnv env (sCon 1)
?? mulH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" (sCon 1))
=: interpInEnv env (sMul a (sCon 1))
=: qed
, vb ./= 0 .&& vb ./= 1
==> interpInEnv env (simplify (sMul a (sCon vb)))
?? "Mul _,C"
=: interpInEnv env (sMul a (sCon vb))
=: qed
]
, sNot (isCon a) .&& sNot (isCon b)
==> interpInEnv env (simplify (sMul a b))
?? "Mul _,_"
=: interpInEnv env (sMul a b)
=: qed
]
Let nm a b -> interpInEnv env (simplify e)
?? "Let"
=: interpInEnv env (simplify (sLet nm a b))
=: cases [ isCon a
==> let v = scon a
in interpInEnv env (simplify (sLet nm (sCon v) b))
?? "Let Con"
=: interpInEnv env (subst nm v b)
?? subC `at` (Inst @"e" b, Inst @"nm" nm, Inst @"v" v, Inst @"env" env)
=: interpInEnv (ST.tuple (nm, v) .: env) b
?? letH `at` (Inst @"env" env, Inst @"nm" nm, Inst @"a" (sCon v), Inst @"b" b)
=: interpInEnv env (sLet nm (sCon v) b)
=: qed
, sNot (isCon a)
==> interpInEnv env (simplify (sLet nm a b))
?? "Let _"
=: interpInEnv env (sLet nm a b)
=: qed
]
|]
-- | Constant folding preserves the semantics: interpreting an expression
-- is the same as constant-folding it first and then interpreting the result.
--
-- >>> runTPWith cvc5 cfoldCorrect
-- Lemma: measureNonNeg Q.E.D.
-- Lemma: simpCorrect Q.E.D.
-- Cached: sqrCong Q.E.D.
-- Cached: sqrHelper Q.E.D.
-- Cached: mulCongL Q.E.D.
-- Cached: mulCongR Q.E.D.
-- Cached: mulHelper Q.E.D.
-- Inductive lemma (strong): cfoldCorrect
-- Step: Measure is non-negative Q.E.D.
-- Step: 1 (7 way case split)
-- Step: 1.1.1 (case Var) Q.E.D.
-- Step: 1.1.2 Q.E.D.
-- Step: 1.1.3 Q.E.D.
-- Step: 1.2.1 (case Con) Q.E.D.
-- Step: 1.2.2 Q.E.D.
-- Step: 1.2.3 Q.E.D.
-- Step: 1.3.1 (case Sqr) Q.E.D.
-- Step: 1.3.2 Q.E.D.
-- Step: 1.3.3 Q.E.D.
-- Step: 1.3.4 Q.E.D.
-- Step: 1.3.5 Q.E.D.
-- Step: 1.3.6 Q.E.D.
-- Step: 1.3.7 Q.E.D.
-- Step: 1.4.1 (case Inc) Q.E.D.
-- Step: 1.4.2 Q.E.D.
-- Step: 1.4.3 Q.E.D.
-- Step: 1.4.4 Q.E.D.
-- Step: 1.4.5 Q.E.D.
-- Step: 1.5.1 (case Add) Q.E.D.
-- Step: 1.5.2 Q.E.D.
-- Step: 1.5.3 Q.E.D.
-- Step: 1.5.4 Q.E.D.
-- Step: 1.5.5 Q.E.D.
-- Step: 1.6.1 (case Mul) Q.E.D.
-- Step: 1.6.2 Q.E.D.
-- Step: 1.6.3 Q.E.D.
-- Step: 1.6.4 Q.E.D.
-- Step: 1.6.5 Q.E.D.
-- Step: 1.6.6 Q.E.D.
-- Step: 1.6.7 Q.E.D.
-- Step: 1.6.8 Q.E.D.
-- Step: 1.7.1 (case Let) Q.E.D.
-- Step: 1.7.2 Q.E.D.
-- Step: 1.7.3 Q.E.D.
-- Step: 1.7.4 Q.E.D.
-- Step: 1.7.5 Q.E.D.
-- Step: 1.Completeness Q.E.D.
-- Result: Q.E.D.
-- Functions proven terminating: cfold, exprSize, interpInEnv, sbv.lookup, simplify, subst
-- [Proven] cfoldCorrect :: Ɐe ∷ (Expr String Integer) → Ɐenv ∷ [(String, Integer)] → Bool
cfoldCorrect :: TP (Proof (Forall "e" Exp -> Forall "env" EL -> SBool))
cfoldCorrect = do
mnn <- recall measureNonNeg
sc <- recall simpCorrect
sqrC <- recall sqrCong
sqrH <- recall sqrHelper
mulCL <- recall mulCongL
mulCR <- recall mulCongR
mulH <- recall mulHelper
sInduct "cfoldCorrect"
(\(Forall @"e" (e :: SE)) (Forall @"env" (env :: E)) -> interpInEnv env (cfold e) .== interpInEnv env e)
(\e _ -> size e, [proofOf mnn]) $
\ih e env -> []
|- [pCase| e of
Var nm -> interpInEnv env (cfold e)
?? "case Var"
=: interpInEnv env (cfold (sVar nm))
=: interpInEnv env (sVar nm)
=: interpInEnv env e
=: qed
Con v -> interpInEnv env (cfold e)
?? "case Con"
=: interpInEnv env (cfold (sCon v))
=: interpInEnv env (sCon v)
=: interpInEnv env e
=: qed
Sqr a -> interpInEnv env (cfold e)
?? "case Sqr"
=: interpInEnv env (cfold (sSqr a))
=: interpInEnv env (simplify (sSqr (cfold a)))
?? sc `at` (Inst @"e" (sSqr (cfold a)), Inst @"env" env)
=: interpInEnv env (sSqr (cfold a))
?? sqrH `at` (Inst @"env" env, Inst @"a" (cfold a))
=: interpInEnv env (cfold a) * interpInEnv env (cfold a)
?? ih `at` (Inst @"e" a, Inst @"env" env)
?? sqrC `at` (Inst @"a" (interpInEnv env (cfold a)), Inst @"b" (interpInEnv env a))
=: interpInEnv env a * interpInEnv env a
?? sqrH `at` (Inst @"env" env, Inst @"a" a)
=: interpInEnv env (sSqr a)
=: interpInEnv env e
=: qed
Inc a -> interpInEnv env (cfold e)
?? "case Inc"
=: interpInEnv env (cfold (sInc a))
=: interpInEnv env (simplify (sInc (cfold a)))
?? sc `at` (Inst @"e" (sInc (cfold a)), Inst @"env" env)
=: interpInEnv env (sInc (cfold a))
?? ih `at` (Inst @"e" a, Inst @"env" env)
=: interpInEnv env (sInc a)
=: interpInEnv env e
=: qed
Add a b -> interpInEnv env (cfold e)
?? "case Add"
=: interpInEnv env (cfold (sAdd a b))
=: interpInEnv env (simplify (sAdd (cfold a) (cfold b)))
?? sc `at` (Inst @"e" (sAdd (cfold a) (cfold b)), Inst @"env" env)
=: interpInEnv env (sAdd (cfold a) (cfold b))
?? ih `at` (Inst @"e" a, Inst @"env" env)
?? ih `at` (Inst @"e" b, Inst @"env" env)
=: interpInEnv env (sAdd a b)
=: interpInEnv env e
=: qed
Mul a b -> interpInEnv env (cfold e)
?? "case Mul"
=: interpInEnv env (cfold (sMul a b))
=: interpInEnv env (simplify (sMul (cfold a) (cfold b)))
?? sc `at` (Inst @"e" (sMul (cfold a) (cfold b)), Inst @"env" env)
=: interpInEnv env (sMul (cfold a) (cfold b))
?? mulH `at` (Inst @"env" env, Inst @"a" (cfold a), Inst @"b" (cfold b))
=: interpInEnv env (cfold a) * interpInEnv env (cfold b)
?? ih `at` (Inst @"e" a, Inst @"env" env)
?? mulCL `at` (Inst @"a" (interpInEnv env (cfold a)), Inst @"b" (interpInEnv env a), Inst @"c" (interpInEnv env (cfold b)))
=: interpInEnv env a * interpInEnv env (cfold b)
?? ih `at` (Inst @"e" b, Inst @"env" env)
?? mulCR `at` (Inst @"a" (interpInEnv env a), Inst @"b" (interpInEnv env (cfold b)), Inst @"c" (interpInEnv env b))
=: interpInEnv env a * interpInEnv env b
?? mulH `at` (Inst @"env" env, Inst @"a" a, Inst @"b" b)
=: interpInEnv env (sMul a b)
=: interpInEnv env e
=: qed
Let nm a b -> interpInEnv env (cfold e)
?? "case Let"
=: interpInEnv env (cfold (sLet nm a b))
=: interpInEnv env (simplify (sLet nm (cfold a) (cfold b)))
?? sc `at` (Inst @"e" (sLet nm (cfold a) (cfold b)), Inst @"env" env)
=: interpInEnv env (sLet nm (cfold a) (cfold b))
?? ih `at` (Inst @"e" a, Inst @"env" env)
?? ih `at` (Inst @"e" b, Inst @"env" (ST.tuple (nm, interpInEnv env a) .: env))
=: interpInEnv env (sLet nm a b)
=: interpInEnv env e
=: qed
|]
{-# ANN simpCorrect ("HLint: ignore Evaluate" :: String) #-}
{-# ANN cfoldCorrect ("HLint: ignore Evaluate" :: String) #-}