Hoare Logic

4. Equiv: Program Equivalence🔗

open scoped HasEval MyGetElem Com

4.1. Behavioral Equivalence🔗

In an earlier chapter, we investigated the correctness of a very simple program transformation: the optimize0plus function. The programming language we were considering was the first version of the language of arithmetic expressions - with no variables - so in that setting it was very easy to define what it means for a program transformation to be correct: it should always yield a program that evaluates to the same number as the original.

To talk about the correctness of program transformations for the full Imp language - in particular, assignment - we need to consider the role of mutable state and develop a more sophisticated notion of correctness, which we'll call behavioral equivalence.

For example:

  • X + 2 is behaviorally equivalent to 1 + X + 1

  • X - X is behaviorally equivalent to 0

  • (X - 1) + 1 is not behaviorally equivalent to X

4.1.1. Definitions🔗

For Aexps and Bexps with variables, the definition we want is clear: Two Aexps or Bexps are "behaviorally equivalent" if they evaluate to the same result in every state.

def Aexp.Equiv (a₁ a₂ : Aexp) : Prop := ∀ (st : State), a₁.eval st = a₂.eval st def Bexp.Equiv (b₁ b₂ : Bexp) : Prop := ∀ (st : State), b₁.eval st = b₂.eval st

We'll also define a notation for Equiv:

class Equiv (α : Type) where equiv : α → α → Prop infix:70 " ≃ " => Equiv.equiv -- you can type `≃` as \equiv instance : Equiv Aexp where equiv := Aexp.Equiv instance : Equiv Bexp where equiv := Bexp.Equiv @[simp] theorem Aexp.equiv_notation {a₁ a₂ : Aexp} : a₁.Equiv a₂ ↔ a₁ ≃ a₂ := a₁:Aexpa₂:Aexp⊢ a₁.Equiv a₂ ↔ a₁ ≃ a₂ All goals completed! 🐙 @[simp] theorem Aexp.equiv_def {a₁ a₂ : Aexp} : a₁ ≃ a₂ ↔ ∀ (st : State), a₁.eval st = a₂.eval st := a₁:Aexpa₂:Aexp⊢ a₁ ≃ a₂ ↔ ∀ (st : State), eval st a₁ = eval st a₂ All goals completed! 🐙 @[simp] theorem Bexp.equiv_notation {b₁ b₂ : Bexp} : b₁.Equiv b₂ ↔ b₁ ≃ b₂ := b₁:Bexpb₂:Bexp⊢ b₁.Equiv b₂ ↔ b₁ ≃ b₂ All goals completed! 🐙 @[simp] theorem Bexp.equiv_def {b₁ b₂ : Bexp} : b₁ ≃ b₂ ↔ ∀ (st : State), b₁.eval st = b₂.eval st := b₁:Bexpb₂:Bexp⊢ b₁ ≃ b₂ ↔ ∀ (st : State), eval st b₁ = eval st b₂ All goals completed! 🐙

Here are some simple examples of equivalences of arithmetic and boolean expressions.

example : aexp { X - X } ≃ aexp { 0 } := ⊢ aexp {X - X} ≃ aexp {0} All goals completed! 🐙 example : bexp { X - X = 0 } ≃ bexp { true } := ⊢ bexp {X - X = 0} ≃ bexp {true} All goals completed! 🐙

For commands, the situation is a little more subtle. We can't simply say "two commands are behaviorally equivalent if they evaluate to the same ending state whenever they are started in the same initial state," because some commands, when run in some starting states, don't terminate in any final state at all!

What we need instead is this: two commands are behaviorally equivalent if, for any given starting state, they either (1) both diverge or else (2) both terminate in the same final state. A compact way to express this is "if the first one terminates in a particular state then so does the second, and vice versa."

def Com.Equiv (c₁ c₂ : Com) : Prop := ∀ {st st' : State}, (st =[ c₁ ]=> st') ↔ (st =[ c₂ ]=> st') instance : Equiv Com where equiv := Com.Equiv @[simp] theorem Com.equiv_notation {c₁ c₂ : Com} : c₁.Equiv c₂ ↔ c₁ ≃ c₂ := c₁:Comc₂:Com⊢ c₁.Equiv c₂ ↔ c₁ ≃ c₂ All goals completed! 🐙 @[simp] theorem Com.equiv_def {c₁ c₂ : Com} : c₁ ≃ c₂ ↔ ∀ {st st' : State}, (st =[ c₁ ]=> st') ↔ (st =[ c₂ ]=> st') := c₁:Comc₂:Com⊢ c₁ ≃ c₂ ↔ ∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₂ ]=> st' All goals completed! 🐙

4.1.2. Simple Examples🔗

namespace Com

For examples of command equivalence, let's start by looking at a trivial equivalence involving skip.

theorem skip_left {c : Com} : imp { skip; c } ≃ c := c:Com⊢ imp {skip; ~c} ≃ c workinclass! c:Com⊢ ∀ {st st' : State}, (st =[ skip; ~c ]=> st') ↔ st =[ ~c ]=> st' c:Comst:Statest'':State⊢ (st =[ skip; ~c ]=> st'') ↔ st =[ ~c ]=> st'' c:Comst:Statest'':State⊢ (st =[ skip; ~c ]=> st'') → st =[ ~c ]=> st''c:Comst:Statest'':State⊢ (st =[ ~c ]=> st'') → st =[ skip; ~c ]=> st'' c:Comst:Statest'':State⊢ (st =[ skip; ~c ]=> st'') → st =[ ~c ]=> st'' c:Comst:Statest'':Stateh:st =[ skip; ~c ]=> st''⊢ st =[ ~c ]=> st'' inversion h with | seq st' h1 h2 => c:Comst:Statest'':Stateh2:c.EvalR st st''⊢ st =[ ~c ]=> st'' All goals completed! 🐙 c:Comst:Statest'':State⊢ (st =[ ~c ]=> st'') → st =[ skip; ~c ]=> st'' c:Comst:Statest'':Stateh:st =[ ~c ]=> st''⊢ st =[ skip; ~c ]=> st'' All goals completed! 🐙
Exercise★★(skip_right)

Prove that adding a skip after a command also results in an equivalent program.

theorem skip_right {c : Com} : imp { c; skip } ≃ c := c:Com⊢ imp {~c; skip} ≃ c solution! c:Com⊢ ∀ {st st' : State}, (st =[ ~c; skip ]=> st') ↔ st =[ ~c ]=> st' c:Comst:Statest'':State⊢ (st =[ ~c; skip ]=> st'') ↔ st =[ ~c ]=> st'' c:Comst:Statest'':State⊢ (st =[ ~c; skip ]=> st'') → st =[ ~c ]=> st''c:Comst:Statest'':State⊢ (st =[ ~c ]=> st'') → st =[ ~c; skip ]=> st'' c:Comst:Statest'':State⊢ (st =[ ~c; skip ]=> st'') → st =[ ~c ]=> st'' c:Comst:Statest'':Stateh:st =[ ~c; skip ]=> st''⊢ st =[ ~c ]=> st'' inversion h with | seq st' h1 h2 => c:Comst:Statest'':Stateh1:c.EvalR st st''⊢ st =[ ~c ]=> st'' All goals completed! 🐙 c:Comst:Statest'':State⊢ (st =[ ~c ]=> st'') → st =[ ~c; skip ]=> st'' c:Comst:Statest'':Stateh:st =[ ~c ]=> st''⊢ st =[ ~c; skip ]=> st'' All goals completed! 🐙

Similarly, here is a simple equivalence that optimises if commands.

theorem if_true_simple {c₁ c₂ : Com} : imp {if (true) {c₁} else {c₂}} ≃ c₁ := c₁:Comc₂:Com⊢ imp {if (true) {~c₁} else {~c₂}} ≃ c₁ c₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (true) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₁ ]=> st' c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₁ ]=> st' c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {~c₁} else {~c₂} ]=> st') → st =[ ~c₁ ]=> st'c₁:Comc₂:Comst:Statest':State⊢ (st =[ ~c₁ ]=> st') → st =[ if (true) {~c₁} else {~c₂} ]=> st' c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {~c₁} else {~c₂} ]=> st') → st =[ ~c₁ ]=> st' c₁:Comc₂:Comst:Statest':Stateh:st =[ if (true) {~c₁} else {~c₂} ]=> st'⊢ st =[ ~c₁ ]=> st' inversion h with | ifTrue hb hc => All goals completed! 🐙 | ifFalse hb hc => All goals completed! 🐙 c₁:Comc₂:Comst:Statest':State⊢ (st =[ ~c₁ ]=> st') → st =[ if (true) {~c₁} else {~c₂} ]=> st' c₁:Comc₂:Comst:Statest':Stateh:st =[ ~c₁ ]=> st'⊢ st =[ if (true) {~c₁} else {~c₂} ]=> st' c₁:Comc₂:Comst:Statest':Stateh:st =[ ~c₁ ]=> st'⊢ Bexp.eval st (bexp {true}) = true All goals completed! 🐙

Of course, no programmer would write a conditional whose condition is literally true. (At least, no human programmer - compilers and macro preprocessors do this sort of thing internally all the time!) But they might write one whose condition is equivalent to true:

Theorem: If b is equivalent to true, then if (b) {c₁} else {c₂} is equivalent to c₁. Proof:

  • (→) We must show, for all st and st', that if st =[ imp {if (b) {c₁} else {c₂}} ]=> st' then st =[ c₁ ]=> st'.

    Proceed by cases on the rules that could possibly have been used to show st =[ imp {if (b) {c₁} else {c₂}} ]=> st', namely Com.EvalR.ifTrue and Com.EvalR.ifFalse.

    • Suppose the final rule in the derivation of st =[ imp {if (b) {c₁} else {c₂}} ]=> st' was Com.EvalR.ifTrue. We then have, by the premises of Com.EvalR.ifTrue, that st =[ c₁ ]=> st'. This is exactly what we set out to prove.

    • On the other hand, suppose the final rule in the derivation of st =[ imp {if (b) {c₁} else {c₂}} ]=> st' was Com.EvalR.ifFalse. We then know that b.eval st = false and st =[ c₂ ]=> st'.

      Recall that b is equivalent to true, i.e., forall st, b.eval st = (Bexp {true}).eval st. In particular, this means that b.eval st = true, since (Bexp {true}).eval st = true. But this is a contradiction, since Com.EvalR.ifFalse requires that b.eval st = false. Thus, the final rule could not have been Com.EvalR.ifFalse.

  • (<-) We must show, for all st and st', that if st =[ c₁ ]=> st' then st =[ imp {if (b) {c₁} else {c₂}} ]=> st'.

    Since b is equivalent to true, we know that b.eval st = (Bexp {true}).eval st = true = true. Together with the assumption that st =[ c₁ ]=> st', we can apply Com.EvalR.ifTrue to derive st =[ imp {if (b) {c₁} else {c₂}} ]=> st'.

Here is the formal version of this proof:

theorem if_true {b : Bexp} {c₁ c₂ : Com} (hb : b ≃ bexp {true}) : imp {if (b) {c₁} else {c₂}} ≃ c₁ := b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ c₁ b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}⊢ ∀ {st st' : State}, (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₁ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₁ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ ~c₁ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ ~c₁ ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ ~c₁ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ if (~b) {~c₁} else {~c₂} ]=> st'⊢ st =[ ~c₁ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ ~c₁ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ ~c₁ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ ~c₁ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ ~c₁ ]=> st' All goals completed! 🐙 b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ ~c₁ ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ ~c₁ ]=> st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ ~c₁ ]=> st'⊢ Bexp.eval st b = true All goals completed! 🐙
Exercise★★(if_false_equiv)
theorem if_false {b : Bexp} {c₁ c₂ : Com} (hb : b ≃ bexp {false}) : imp {if (b) {c₁} else {c₂}} ≃ c₂ := b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ c₂ solution! b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}⊢ ∀ {st st' : State}, (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₂ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ ~c₂ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ ~c₂ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':State⊢ (st =[ ~c₂ ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ ~c₂ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Stateh:st =[ if (~b) {~c₁} else {~c₂} ]=> st'⊢ st =[ ~c₂ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ ~c₂ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ ~c₂ ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ ~c₂ ]=> st'b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ ~c₂ ]=> st' All goals completed! 🐙 b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':State⊢ (st =[ ~c₂ ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Stateh:st =[ ~c₂ ]=> st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comhb:b ≃ bexp {false}st:Statest':Stateh:st =[ ~c₂ ]=> st'⊢ Bexp.eval st b = false All goals completed! 🐙
Exercise★★★(swap_if_branches)

Show that we can swap the branches of an if if we also negate its condition.

theorem swap_if_branches {b : Bexp} {c₁ c₂ : Com} : imp {if (b) {c₁} else {c₂}} ≃ imp {if (¬ b) {c₂} else {c₁}} := b:Bexpc₁:Comc₂:Com⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ imp {if (¬ ~b) {~c₂} else {~c₁}} solution! b:Bexpc₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st' b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st' b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st'b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st' b:Bexpc₁:Comc₂:Comst:Statest':Stateh:st =[ if (~b) {~c₁} else {~c₂} ]=> st'⊢ st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st' inversion h with | ifTrue hb hc => b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st b = truehc:c₁.EvalR st st'⊢ Bexp.eval st (bexp {¬ ~b}) = false All goals completed! 🐙 | ifFalse hb hc => b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st b = falsehc:c₂.EvalR st st'⊢ Bexp.eval st (bexp {¬ ~b}) = true All goals completed! 🐙 b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpc₁:Comc₂:Comst:Statest':Stateh:st =[ if (¬ ~b) {~c₂} else {~c₁} ]=> st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' inversion h with | ifTrue hb hc => b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st (bexp {¬ ~b}) = truehc:c₂.EvalR st st'⊢ Bexp.eval st b = false All goals completed! 🐙 | ifFalse hb hc => b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st (bexp {¬ ~b}) = falsehc:c₁.EvalR st st'⊢ Bexp.eval st b = true All goals completed! 🐙

For while loops, we can give a similar pair of theorems. A loop whose guard is equivalent to false is equivalent to skip, while a loop whose guard is equivalent to true is equivalent to while (true) {skip} end (or any other non-terminating program).

The first of these facts is easy.

theorem while_false {b : Bexp} {c : Com} (hb : b ≃ bexp {false}) : imp {while (b) {c}} ≃ imp {skip} := b:Bexpc:Comhb:b ≃ bexp {false}⊢ imp {while (~b) {~c}} ≃ imp {skip} b:Bexpc:Comhb:b ≃ bexp {false}⊢ ∀ {st st' : State}, (st =[ while (~b) {~c} ]=> st') ↔ st =[ skip ]=> st' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (~b) {~c} ]=> st'') ↔ st =[ skip ]=> st'' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (~b) {~c} ]=> st'') → st =[ skip ]=> st''b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ skip ]=> st'') → st =[ while (~b) {~c} ]=> st'' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (~b) {~c} ]=> st'') → st =[ skip ]=> st'' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':Stateh:st =[ while (~b) {~c} ]=> st''⊢ st =[ skip ]=> st'' inversion h with | whileFalse => All goals completed! 🐙 | whileTrue st' hb' hc hloop => All goals completed! 🐙 b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ skip ]=> st'') → st =[ while (~b) {~c} ]=> st'' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':Stateh:st =[ skip ]=> st''⊢ st =[ while (~b) {~c} ]=> st'' b:Bexpc:Comhb:b ≃ bexp {false}st:State⊢ st =[ while (~b) {~c} ]=> st b:Bexpc:Comhb:b ≃ bexp {false}st:State⊢ Bexp.eval st b = false All goals completed! 🐙
Exercise★★(while_false_informal) (Advanced, Manually graded)

Write an informal proof of while_false.

To prove the second fact, we need an auxiliary lemma stating that while loops whose guards are equivalent to true never terminate.

Lemma: If b is equivalent to true, then it cannot be the case that st =[ while (b) {c} ]=> st'.

Proof: Suppose that st =[ while (b) {c} ]=> st'. We show, by induction on a derivation of st =[ while (b) {c} ]=> st', that this assumption leads to a contradiction. The only two cases to consider are Com.EvalR.whileFalse and Com.EvalR.whileTrue; the others are contradictory.

  • Suppose st =[ while (b) {c} ]=> st' is proved using rule Com.EvalR.whileFalse. Then by assumption b.eval st = false. But this contradicts the assumption that b is equivalent to true.

  • Suppose st =[ while (b) {c} ]=> st' is proved using rule Com.EvalR.whileTrue. We must have:

    1. b.eval st = true, and

    2. there is some st₀ such that st =[ c ] => st₀ and st₀ =[ while (b) {c} ]=> st'.

    3. Also, we are given an induction hypothesis saying that st₀ =[ while (b) {c} ]=> st' leads to a contradiction,

    We obtain a contradiction by 2 and 3.

theorem while_true_nonterm {b : Bexp} {c : Com} {st st' : State} (hb : b ≃ bexp {true}) : ¬ st =[ while (b) {c} ]=> st' := b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}⊢ ¬st =[ while (~b) {~c} ]=> st' workinclass! b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}contra:st =[ while (~b) {~c} ]=> st'⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comheq:imp {while (~b) {~c}} = comcontra:st =[ ~com ]=> st'⊢ False induction contra with b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falseheq:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falsehbeq:b = b✝hceq:c = c✝⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statec✝:Comhceq:c = c✝hb':Bexp.eval st✝ b = false⊢ False All goals completed! 🐙 -- hb and hb' are contradictory b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statest'✝:Statest''✝:Stateb✝:Bexpc✝:Comhb':Bexp.eval st✝ b✝ = truehc':c✝.EvalR st✝ st'✝hwhile:imp {while (~b✝) {~c✝}}.EvalR st'✝ st''✝ih1:imp {while (~b) {~c}} = c✝ → Falseih2:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}} → Falseheq:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}}⊢ False All goals completed! 🐙 b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Stateheq:imp {while (~b) {~c}} = imp {skip}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b) {~c}} = imp {x✝ := ~a✝}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b) {~c}} = c₁✝ → Falseh₂_ih✝:imp {while (~b) {~c}} = c₂✝ → Falseheq:imp {while (~b) {~c}} = imp {~c₁✝; ~c₂✝}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₁✝ → Falseheq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₂✝ → Falseheq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ False b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₂✝ → Falseheq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ Falseb:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₁✝ → Falseheq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ Falseb:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b) {~c}} = c₁✝ → Falseh₂_ih✝:imp {while (~b) {~c}} = c₂✝ → Falseheq:imp {while (~b) {~c}} = imp {~c₁✝; ~c₂✝}⊢ Falseb:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b) {~c}} = imp {x✝ := ~a✝}⊢ Falseb:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Stateheq:imp {while (~b) {~c}} = imp {skip}⊢ False All goals completed! 🐙 -- `heq` says that different commands are equal
Exercise★★(while_true_nonterm_informal) (Manually graded)

Explain what the lemma while_true_nonterm means in English.

Exercise★★(while_true)

Prove the following theorem. Hint: You'll want to use while_true_nonterm here.

theorem while_true {b : Bexp} {c : Com} (hb : b ≃ bexp {true}) : imp {while (b) {c}} ≃ imp {while (true) {skip}} := b:Bexpc:Comhb:b ≃ bexp {true}⊢ imp {while (~b) {~c}} ≃ imp {while (true) {skip}} solution! b:Bexpc:Comhb:b ≃ bexp {true}⊢ ∀ {st st' : State}, (st =[ while (~b) {~c} ]=> st') ↔ st =[ while (true) {skip} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') ↔ st =[ while (true) {skip} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ while (true) {skip} ]=> st'b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (true) {skip} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ while (true) {skip} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (~b) {~c} ]=> st'⊢ st =[ while (true) {skip} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (~b) {~c} ]=> st'⊢ False All goals completed! 🐙 b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (true) {skip} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ st =[ while (~b) {~c} ]=> st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ False b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ bexp {true} ≃ bexp {true} b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ ∀ (st : State), Bexp.eval st (bexp {true}) = Bexp.eval st (bexp {true}) b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'st✝:State⊢ Bexp.eval st✝ (bexp {true}) = Bexp.eval st✝ (bexp {true}) All goals completed! 🐙

A more interesting fact about while commands is that any number of copies of the body can be "unrolled" without changing meaning.

Loop unrolling is an important transformation in any real compiler, so its correctness is of more than just academic interest!

theorem loop_unrolling {b : Bexp} {c : Com} : imp { while (b) {c} } ≃ imp { if (b) {c} else {skip}; while (b) {c} } := b:Bexpc:Com⊢ imp {while (~b) {~c}} ≃ imp {if (~b) {~c} else {skip}; while (~b) {~c}} workinclass! b:Bexpc:Com⊢ ∀ {st st' : State}, (st =[ while (~b) {~c} ]=> st') ↔ st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st' b:Bexpc:Comst:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') ↔ st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st' b:Bexpc:Comst:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st'b:Bexpc:Comst:Statest':State⊢ (st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpc:Comst:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st' b:Bexpc:Comst:Statest':Stateh:st =[ while (~b) {~c} ]=> st'⊢ st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st' inversion h with | whileFalse hb => b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {if (~b) {~c} else {skip}}.EvalR st stb:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {while (~b) {~c}}.EvalR st st b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {if (~b) {~c} else {skip}}.EvalR st st All goals completed! 🐙 b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {while (~b) {~c}}.EvalR st st All goals completed! 🐙 | whileTrue stmid hb hc hloop => b:Bexpc:Comst:Statest':Statestmid:Statehb:Bexp.eval st b = truehc:c.EvalR st stmidhloop:imp {while (~b) {~c}}.EvalR stmid st'⊢ imp {if (~b) {~c} else {skip}}.EvalR st stmid All goals completed! 🐙 b:Bexpc:Comst:Statest':State⊢ (st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpc:Comst:Statest':Stateh:st =[ if (~b) {~c} else {skip}; while (~b) {~c} ]=> st'⊢ st =[ while (~b) {~c} ]=> st' inversion h with | seq stmid h1 h2 => inversion h1 with | ifTrue hb hc => All goals completed! 🐙 | ifFalse hb hc => b:Bexpc:Comst:Statest':Statehb:Bexp.eval st b = falseh2:imp {while (~b) {~c}}.EvalR st st'⊢ st =[ while (~b) {~c} ]=> st' All goals completed! 🐙
Exercise★★(seq_assoc) (Optional)
theorem seq_assoc {c₁ c₂ c₃ : Com} : imp {~(imp {c₁; c₂}); c₃} ≃ imp {c₁; c₂; c₃} := c₁:Comc₂:Comc₃:Com⊢ imp {~(imp {~c₁; ~c₂}); ~c₃} ≃ imp {~c₁; ~c₂; ~c₃} solution! c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂) ↔ st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂) → st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂) → st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂) → st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂) → st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂⊢ st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂⊢ st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂ inversion h with | seq h₁ h₂ => c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ c₁.EvalR st₁ ?seq.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ imp {~c₂; ~c₃}.EvalR ?seq.st' st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ State; c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ imp {~c₂; ~c₃}.EvalR st'✝ st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ c₂.EvalR st'✝ ?seq.h₂.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ c₃.EvalR ?seq.h₂.st' st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ State c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ c₂.EvalR st'✝ ?seq.h₂.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ c₃.EvalR ?seq.h₂.st' st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ State All goals completed! 🐙 c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ ~c₁; ~c₂; ~c₃ ]=> st₂⊢ st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂ inversion h with | seq h₁ h₂ => c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ st₁ =[ ~(imp {~c₁; ~c₂}); ~c₃ ]=> st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ imp {~c₁; ~c₂}.EvalR st₁ ?seq.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₃.EvalR ?seq.st' st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ State c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ imp {~c₁; ~c₂}.EvalR st₁ ?seq.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₃.EvalR ?seq.st' st₂c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ State try All goals completed! 🐙 c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₁.EvalR st₁ ?seq.h₁.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₂.EvalR ?seq.h₁.st' st'✝c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ State c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₁.EvalR st₁ ?seq.h₁.st'c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ c₂.EvalR ?seq.h₁.st' st'✝c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ State All goals completed! 🐙

Proving program properties involving assignments is one place where the fact that we are treating equality on program states extensionally (e.g., x →ₜ m[x] ; m and m are equal maps) comes in handy.

theorem identity_assignment {X : Ident} : imp { X := X } ≃ imp { skip } := X:Ident⊢ imp {X := X} ≃ imp {skip} X:Ident⊢ ∀ {st st' : State}, (st =[ X := X ]=> st') ↔ st =[ skip ]=> st' X:Identst:Statest':State⊢ (st =[ X := X ]=> st') ↔ st =[ skip ]=> st' X:Identst:Statest':State⊢ (st =[ X := X ]=> st') → st =[ skip ]=> st'X:Identst:Statest':State⊢ (st =[ skip ]=> st') → st =[ X := X ]=> st' X:Identst:Statest':State⊢ (st =[ X := X ]=> st') → st =[ skip ]=> st' X:Identst:Statest':Stateh:st =[ X := X ]=> st'⊢ st =[ skip ]=> st' inversion h with | asgn n h => X:Identst:State⊢ st =[ skip ]=> X →ₜ Aexp.eval st (aexp {X}) ; st X:Identst:State⊢ st =[ skip ]=> st All goals completed! 🐙 X:Identst:Statest':State⊢ (st =[ skip ]=> st') → st =[ X := X ]=> st' X:Identst:Statest':Stateh:st =[ skip ]=> st'⊢ st =[ X := X ]=> st' X:Identst:State⊢ st =[ X := X ]=> st X:Identst:Stateh':st =[ X := X ]=> X →ₜ st[X] ; st⊢ st =[ X := X ]=> st All goals completed! 🐙
Exercise★★(assign_equiv)
theorem assign_equiv {X : Ident} {a : Aexp} (ha : aexp { X } ≃ a) : imp { skip } ≃ imp { X := a } := X:Identa:Aexpha:aexp {X} ≃ a⊢ imp {skip} ≃ imp {X := ~a} solution! X:Identa:Aexpha:aexp {X} ≃ a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ X := ~a ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ X := ~a ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') ↔ st =[ X := ~a ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') → st =[ X := ~a ]=> st'X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':State⊢ (st =[ X := ~a ]=> st') → st =[ skip ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') → st =[ X := ~a ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':Stateh:st =[ skip ]=> st'⊢ st =[ X := ~a ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:State⊢ st =[ X := ~a ]=> st X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Stateh':st =[ X := ~a ]=> X →ₜ st[X] ; st⊢ st =[ X := ~a ]=> st All goals completed! 🐙 X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':State⊢ (st =[ X := ~a ]=> st') → st =[ skip ]=> st' X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:Statest':Stateh:st =[ X := ~a ]=> st'⊢ st =[ skip ]=> st' inversion h with | asgn n h => X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:State⊢ st =[ skip ]=> X →ₜ Aexp.eval st a ; st X:Identa:Aexpha:∀ (st : State), Aexp.eval st (aexp {X}) = Aexp.eval st ast:State⊢ st =[ skip ]=> st All goals completed! 🐙
Exercise★★(equiv_classes) (Optional, Manually graded)

Given the following programs, group together those that are equivalent in Imp. Your answer should be given as a list of lists, where each sub-list represents a group of equivalent programs. For example, if you think programs (a) through (h) are all equivalent to each other, but not to (i), your answer should look like this:

[ [progA, progB, progC, progD, progE, progF, progG, progH], [progI] ]

Write down your answer below in the definition of equiv_classes

def progA : Com := imp { while (X > 0) { X := X + 1 } } def progB : Com := imp { if (X = 0) { X := X + 1; Y := 1 } else { Y := 0 }; X := X - Y; Y := 0 } def progC : Com := imp { skip } def progD : Com := imp { while (X ≠ 0) { X := (X * Y) + 1 } } def progE : Com := imp { Y := 0 } def progF : Com := imp { Y := X + 1; while (X ≠ Y) { Y := X + 1 } } def progG : Com := imp { while (true) { skip } } def progH : Com := imp { while (X ≠ X) { X := X + 1 } } def progI : Com := imp { while (X ≠ Y) { X := Y + 1 } } def equiv_classes : List (List Com) := solution!( [ [progA, progD] , [progB, progE] , [progC, progH] , [progF, progG] , [progI] ] )

4.2. Properties of Behavior Equivalence🔗

We next consider some fundamental properties of program equivalence.

4.2.1. Behavioral Equivalence is an Equivalence🔗

First, let's verify that the equivalences on Aexps, Bexps, and Coms really are equivalences - ie, that they are reflexive, symmetric, and transitive. These proofs are all easy.

end Com theorem Aexp.equiv_refl (a : Aexp) : a ≃ a := a:Aexp⊢ a ≃ a All goals completed! 🐙 theorem Aexp.equiv_symm {a₁ a₂ : Aexp} (h : a₁ ≃ a₂) : a₂ ≃ a₁ := a₁:Aexpa₂:Aexph:a₁ ≃ a₂⊢ a₂ ≃ a₁ All goals completed! 🐙 theorem Aexp.equiv_trans {a₁ a₂ a₃ : Aexp} (h₁ : a₁ ≃ a₂) (h₂ : a₂ ≃ a₃) : a₁ ≃ a₃ := a₁:Aexpa₂:Aexpa₃:Aexph₁:a₁ ≃ a₂h₂:a₂ ≃ a₃⊢ a₁ ≃ a₃ All goals completed! 🐙 theorem Bexp.equiv_refl {b : Bexp} : b ≃ b := b:Bexp⊢ b ≃ b All goals completed! 🐙 theorem Bexp.equiv_symm {b₁ b₂ : Bexp} (h : b₁ ≃ b₂) : b₂ ≃ b₁ := b₁:Bexpb₂:Bexph:b₁ ≃ b₂⊢ b₂ ≃ b₁ All goals completed! 🐙 theorem Bexp.equiv_trans {b₁ b₂ b₃ : Bexp} (h₁ : b₁ ≃ b₂) (h₂ : b₂ ≃ b₃) : b₁ ≃ b₃ := b₁:Bexpb₂:Bexpb₃:Bexph₁:b₁ ≃ b₂h₂:b₂ ≃ b₃⊢ b₁ ≃ b₃ All goals completed! 🐙 theorem Com.equiv_refl {c : Com} : c ≃ c := c:Com⊢ c ≃ c All goals completed! 🐙 theorem Com.equiv_symm {c₁ c₂ : Com} (h : c₁ ≃ c₂) : c₂ ≃ c₁ := c₁:Comc₂:Comh:c₁ ≃ c₂⊢ c₂ ≃ c₁ All goals completed! 🐙 theorem Com.equiv_trans {c₁ c₂ c₃ : Com} (h₁ : c₁ ≃ c₂) (h₂ : c₂ ≃ c₃) : c₁ ≃ c₃ := c₁:Comc₂:Comc₃:Comh₁:c₁ ≃ c₂h₂:c₂ ≃ c₃⊢ c₁ ≃ c₃ All goals completed! 🐙

Lean has a standard library definition for relations that are equivalences, unsurprisingly called Equivalence. To show that a relation is an Equivalence, one needs only supply proofs of the three properties above:

theorem Com.equiv_equivalence : Equivalence Com.Equiv where refl := @Com.equiv_refl symm := Com.equiv_symm trans := Com.equiv_trans

4.2.2. Behavioral Equivalence is a Congruence🔗

Less obviously, behavioral equivalence is also a congruence. That is, the equivalence of two subprograms implies the equivalence of the larger programs in which they are embedded:

a ≃ a' ------------------------- (x := a) ≃ (x := a')

c₁ ≃ c₁' c₂ ≃ c₂' -------------------------- (c₁ ; c₂) ≃ (c₁' ; c₂')

... and so on for the other forms of commands.

(Note that we are using the inference rule notation here not as part of an inductive definition, but simply to write down some valid implications in a readable format. We prove these implications below.)

We will see a concrete example of why these congruence properties are important in the following section (in the proof of fold_constants_com_sound), but the main idea is that they allow us to replace a small part of a large program with an equivalent small part and know that the whole large programs are equivalent without doing an explicit proof about the parts that didn't change - i.e., the "proof burden" of a small change to a large program is proportional to the size of the change, not the program!

theorem Com.congruence_asgn {x : Ident} {a a' : Aexp} (ha : a ≃ a') : imp {x := a} ≃ imp {x := a'} := x:Identa:Aexpa':Aexpha:a ≃ a'⊢ imp {x := ~a} ≃ imp {x := ~a'} x:Identa:Aexpa':Aexpha:a ≃ a'⊢ ∀ {st st' : State}, (st =[ x := ~a ]=> st') ↔ st =[ x := ~a' ]=> st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a ]=> st') ↔ st =[ x := ~a' ]=> st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a ]=> st') → st =[ x := ~a' ]=> st'x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a' ]=> st') → st =[ x := ~a ]=> st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a ]=> st') → st =[ x := ~a' ]=> st'x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a' ]=> st') → st =[ x := ~a ]=> st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := ~a' ]=> st') → st =[ x := ~a ]=> st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':Stateh:st =[ x := ~a' ]=> st'⊢ st =[ x := ~a ]=> st' inversion h with | asgn n h => x:Identa:Aexpa':Aexpha:a ≃ a'st:State⊢ st =[ x := ~a ]=> x →ₜ Aexp.eval st a' ; st x:Identa:Aexpa':Aexpha:a ≃ a'st:State⊢ Aexp.eval st a = Aexp.eval st a' All goals completed! 🐙

The congruence property for loops is a little more interesting, since it requires induction.

Theorem: Equivalence is a congruence for while -- that is, if b is equivalent to b' and c is equivalent to c', then while (b) {c} is equivalent to while (b') {c'}.

Proof: Suppose b is equivalent to b' and c is equivalent to c'. We must show, for every st and st', that st =[ while (b) {c} ]=> st' iff st = while (b') {c'}]=> st'. We consider the two directions separately.

  • (→) We show that st =[ while (b) {c} ]=> st' implies st =[ while (b') {c'} ]=> st', by induction on a derivation of st =[ while (b) {c} ]=> st'. The only nontrivial cases are when the final rule in the derivation is Com.EvalR.whileFalse or Com.EvalR.whileTrue.

    • Com.EvalR.whileFalse: In this case, the form of the rule gives us b.eval st = false and st = st'. But then, since b and b' are equivalent, we have b'.eval st = false, and Com.EvalR.whileFalse applies, giving us st =[ while (b') {c'} ]=> st', as required.

    • Com.EvalR.whileTrue: The form of the rule now gives us b.eval st = true, with st =[ c ]=> st'₀ and st'₀ =[ while {b} {c} ]=> st' for some state st'₀, with the induction hypothesis st'₀ =[ while (b') {c'} ]=> st'.

      Since c and c' are equivalent, we know that st =[ c']=> st'0. And since b and b' are equivalent, we have b'.eval st = true. Now Com.EvalR.whileTrue applies, giving us st =[ while (b') {c'} ]=> st', as required.

  • (←) Similar.

theorem Com.congruence_while {b b' : Bexp} {c c' : Com} (hb : b ≃ b') (hc : c ≃ c') : imp {while (b) {c}} ≃ imp {while (b') {c'}} := b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'⊢ imp {while (~b) {~c}} ≃ imp {while (~b') {~c'}} workinclass! b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'⊢ ∀ {st st' : State}, (st =[ while (~b) {~c} ]=> st') ↔ st =[ while (~b') {~c'} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') ↔ st =[ while (~b') {~c'} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ while (~b') {~c'} ]=> st'b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (~b') {~c'} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (~b) {~c} ]=> st') → st =[ while (~b') {~c'} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Stateh:st =[ while (~b) {~c} ]=> st'⊢ st =[ while (~b') {~c'} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comheq:imp {while (~b) {~c}} = comh:st =[ ~com ]=> st'⊢ st =[ while (~b') {~c'} ]=> st' induction h with b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falseheq:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falsehbeq:b = b✝hceq:c = c✝⊢ st✝ =[ while (~b') {~c'} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c = c✝hb':Bexp.eval st✝ b = false⊢ st✝ =[ while (~b') {~c'} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c = c✝hb':Bexp.eval st✝ b = false⊢ Bexp.eval st✝ b' = false b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c = c✝hb':Bexp.eval st✝ b = false⊢ Bexp.eval st✝ b = false All goals completed! 🐙 b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Stateb✝:Bexpc✝:Comhb':Bexp.eval st✝ b✝ = truehc':c✝.EvalR st✝ st'✝hwhile:imp {while (~b✝) {~c✝}}.EvalR st'✝ st''✝hc_ih✝:imp {while (~b) {~c}} = c✝ → st✝ =[ while (~b') {~c'} ]=> st'✝ih2:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}} → st'✝ =[ while (~b') {~c'} ]=> st''✝heq:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Stateb✝:Bexpc✝:Comhb':Bexp.eval st✝ b✝ = truehc':c✝.EvalR st✝ st'✝hwhile:imp {while (~b✝) {~c✝}}.EvalR st'✝ st''✝hc_ih✝:imp {while (~b) {~c}} = c✝ → st✝ =[ while (~b') {~c'} ]=> st'✝ih2:imp {while (~b) {~c}} = imp {while (~b✝) {~c✝}} → st'✝ =[ while (~b') {~c'} ]=> st''✝beq:b = b✝ceq:c = c✝⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:imp {while (~b) {~c}} = imp {while (~b) {~c}} → st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:imp {while (~b) {~c}} = imp {while (~b) {~c}} → st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ c'.EvalR st✝ st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ c'.EvalR st✝ st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:∀ {st st' : State}, (st =[ ~c ]=> st') ↔ st =[ ~c' ]=> st'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c → st✝ =[ while (~b') {~c'} ]=> st'✝hwhile:imp {while (~b) {~c}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b') {~c'} ]=> st''✝⊢ c'.EvalR st✝ st'✝ All goals completed! 🐙 b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Stateheq:imp {while (~b) {~c}} = imp {skip}⊢ st✝ =[ while (~b') {~c'} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b) {~c}} = imp {x✝ := ~a✝}⊢ st✝ =[ while (~b') {~c'} ]=> x✝ →ₜ n✝ ; st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b) {~c}} = c₁✝ → st✝ =[ while (~b') {~c'} ]=> st'✝h₂_ih✝:imp {while (~b) {~c}} = c₂✝ → st'✝ =[ while (~b') {~c'} ]=> st''✝heq:imp {while (~b) {~c}} = imp {~c₁✝; ~c₂✝}⊢ st✝ =[ while (~b') {~c'} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₁✝ → st✝ =[ while (~b') {~c'} ]=> st'✝heq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₂✝ → st✝ =[ while (~b') {~c'} ]=> st'✝heq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₂✝ → st✝ =[ while (~b') {~c'} ]=> st'✝heq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st'✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b) {~c}} = c₁✝ → st✝ =[ while (~b') {~c'} ]=> st'✝heq:imp {while (~b) {~c}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b') {~c'} ]=> st'✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b) {~c}} = c₁✝ → st✝ =[ while (~b') {~c'} ]=> st'✝h₂_ih✝:imp {while (~b) {~c}} = c₂✝ → st'✝ =[ while (~b') {~c'} ]=> st''✝heq:imp {while (~b) {~c}} = imp {~c₁✝; ~c₂✝}⊢ st✝ =[ while (~b') {~c'} ]=> st''✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b) {~c}} = imp {x✝ := ~a✝}⊢ st✝ =[ while (~b') {~c'} ]=> x✝ →ₜ n✝ ; st✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Stateheq:imp {while (~b) {~c}} = imp {skip}⊢ st✝ =[ while (~b') {~c'} ]=> st✝ All goals completed! 🐙 b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (~b') {~c'} ]=> st') → st =[ while (~b) {~c} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Stateh:st =[ while (~b') {~c'} ]=> st'⊢ st =[ while (~b) {~c} ]=> st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comheq:imp {while (~b') {~c'}} = comh:st =[ ~com ]=> st'⊢ st =[ while (~b) {~c} ]=> st' induction h with b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falseheq:imp {while (~b') {~c'}} = imp {while (~b✝) {~c✝}}⊢ st✝ =[ while (~b) {~c} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falsehbeq:b' = b✝hceq:c' = c✝⊢ st✝ =[ while (~b) {~c} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c' = c✝hb':Bexp.eval st✝ b' = false⊢ st✝ =[ while (~b) {~c} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c' = c✝hb':Bexp.eval st✝ b' = false⊢ Bexp.eval st✝ b = false b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statec✝:Comhceq:c' = c✝hb':Bexp.eval st✝ b' = false⊢ Bexp.eval st✝ b' = false All goals completed! 🐙 b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Stateb✝:Bexpc✝:Comhb':Bexp.eval st✝ b✝ = truehc':c✝.EvalR st✝ st'✝hwhile:imp {while (~b✝) {~c✝}}.EvalR st'✝ st''✝hc_ih✝:imp {while (~b') {~c'}} = c✝ → st✝ =[ while (~b) {~c} ]=> st'✝ih2:imp {while (~b') {~c'}} = imp {while (~b✝) {~c✝}} → st'✝ =[ while (~b) {~c} ]=> st''✝heq:imp {while (~b') {~c'}} = imp {while (~b✝) {~c✝}}⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Stateb✝:Bexpc✝:Comhb':Bexp.eval st✝ b✝ = truehc':c✝.EvalR st✝ st'✝hwhile:imp {while (~b✝) {~c✝}}.EvalR st'✝ st''✝hc_ih✝:imp {while (~b') {~c'}} = c✝ → st✝ =[ while (~b) {~c} ]=> st'✝ih2:imp {while (~b') {~c'}} = imp {while (~b✝) {~c✝}} → st'✝ =[ while (~b) {~c} ]=> st''✝beq:b' = b✝ceq:c' = c✝⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b' = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:imp {while (~b') {~c'}} = imp {while (~b') {~c'}} → st'✝ =[ while (~b) {~c} ]=> st''✝⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:imp {while (~b') {~c'}} = imp {while (~b') {~c'}} → st'✝ =[ while (~b) {~c} ]=> st''✝⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b) {~c} ]=> st''✝⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b) {~c} ]=> st''✝⊢ c.EvalR st✝ st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b) {~c} ]=> st''✝⊢ c.EvalR st✝ st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:∀ {st st' : State}, (st =[ ~c ]=> st') ↔ st =[ ~c' ]=> st'st:Statest':Statecom:Comst✝:Statest'✝:Statest''✝:Statehb':Bexp.eval st✝ b = truehc':c'.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c' → st✝ =[ while (~b) {~c} ]=> st'✝hwhile:imp {while (~b') {~c'}}.EvalR st'✝ st''✝ih2:st'✝ =[ while (~b) {~c} ]=> st''✝⊢ c.EvalR st✝ st'✝ All goals completed! 🐙 b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Stateheq:imp {while (~b') {~c'}} = imp {skip}⊢ st✝ =[ while (~b) {~c} ]=> st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b') {~c'}} = imp {x✝ := ~a✝}⊢ st✝ =[ while (~b) {~c} ]=> x✝ →ₜ n✝ ; st✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b') {~c'}} = c₁✝ → st✝ =[ while (~b) {~c} ]=> st'✝h₂_ih✝:imp {while (~b') {~c'}} = c₂✝ → st'✝ =[ while (~b) {~c} ]=> st''✝heq:imp {while (~b') {~c'}} = imp {~c₁✝; ~c₂✝}⊢ st✝ =[ while (~b) {~c} ]=> st''✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c₁✝ → st✝ =[ while (~b) {~c} ]=> st'✝heq:imp {while (~b') {~c'}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b) {~c} ]=> st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c₂✝ → st✝ =[ while (~b) {~c} ]=> st'✝heq:imp {while (~b') {~c'}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b) {~c} ]=> st'✝ b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = falsehc✝:c₂✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c₂✝ → st✝ =[ while (~b) {~c} ]=> st'✝heq:imp {while (~b') {~c'}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b) {~c} ]=> st'✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statest'✝:Stateb✝:Bexpc₁✝:Comc₂✝:Comhb✝:Bexp.eval st✝ b✝ = truehc✝:c₁✝.EvalR st✝ st'✝hc_ih✝:imp {while (~b') {~c'}} = c₁✝ → st✝ =[ while (~b) {~c} ]=> st'✝heq:imp {while (~b') {~c'}} = imp {if (~b✝) {~c₁✝} else {~c₂✝}}⊢ st✝ =[ while (~b) {~c} ]=> st'✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comc₁✝:Comc₂✝:Comst✝:Statest'✝:Statest''✝:Stateh₁✝:c₁✝.EvalR st✝ st'✝h₂✝:c₂✝.EvalR st'✝ st''✝h₁_ih✝:imp {while (~b') {~c'}} = c₁✝ → st✝ =[ while (~b) {~c} ]=> st'✝h₂_ih✝:imp {while (~b') {~c'}} = c₂✝ → st'✝ =[ while (~b) {~c} ]=> st''✝heq:imp {while (~b') {~c'}} = imp {~c₁✝; ~c₂✝}⊢ st✝ =[ while (~b) {~c} ]=> st''✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Statea✝:Aexpn✝:Natx✝:Identh✝:Aexp.eval st✝ a✝ = n✝heq:imp {while (~b') {~c'}} = imp {x✝ := ~a✝}⊢ st✝ =[ while (~b) {~c} ]=> x✝ →ₜ n✝ ; st✝b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Statecom:Comst✝:Stateheq:imp {while (~b') {~c'}} = imp {skip}⊢ st✝ =[ while (~b) {~c} ]=> st✝ All goals completed! 🐙
Exercise★★★(Com.congruence_seq) (Optional)
theorem Com.congruence_seq {c₁ c₁' c₂ c₂' : Com} (hc₁ : c₁ ≃ c₁') (hc₂ : c₂ ≃ c₂') : imp {c₁ ; c₂} ≃ imp {c₁' ; c₂'} := c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'⊢ imp {~c₁; ~c₂} ≃ imp {~c₁'; ~c₂'} solution!( c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ ~c₁; ~c₂ ]=> st') ↔ st =[ ~c₁'; ~c₂' ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ ~c₁; ~c₂ ]=> st') → st =[ ~c₁'; ~c₂' ]=> st'c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ ~c₁'; ~c₂' ]=> st') → st =[ ~c₁; ~c₂ ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ ~c₁; ~c₂ ]=> st') → st =[ ~c₁'; ~c₂' ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Stateh:st =[ ~c₁; ~c₂ ]=> st'⊢ st =[ ~c₁'; ~c₂' ]=> st' inversion h with | seq hc₁' hc₂' => c₁:Comc₁':Comc₂:Comc₂':Comhc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:c₂ ≃ c₂'st:Statest':Statest'✝:Statehc₁':c₁.EvalR st st'✝hc₂':c₂.EvalR st'✝ st'⊢ st =[ ~c₁'; ~c₂' ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:∀ {st st' : State}, (st =[ ~c₂ ]=> st') ↔ st =[ ~c₂' ]=> st'st:Statest':Statest'✝:Statehc₁':c₁.EvalR st st'✝hc₂':c₂.EvalR st'✝ st'⊢ st =[ ~c₁'; ~c₂' ]=> st' All goals completed! 🐙 c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ ~c₁'; ~c₂' ]=> st') → st =[ ~c₁; ~c₂ ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Stateh:st =[ ~c₁'; ~c₂' ]=> st'⊢ st =[ ~c₁; ~c₂ ]=> st' inversion h with | seq hc₁' hc₂' => c₁:Comc₁':Comc₂:Comc₂':Comhc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:c₂ ≃ c₂'st:Statest':Statest'✝:Statehc₁':c₁'.EvalR st st'✝hc₂':c₂'.EvalR st'✝ st'⊢ st =[ ~c₁; ~c₂ ]=> st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:∀ {st st' : State}, (st =[ ~c₂ ]=> st') ↔ st =[ ~c₂' ]=> st'st:Statest':Statest'✝:Statehc₁':c₁'.EvalR st st'✝hc₂':c₂'.EvalR st'✝ st'⊢ st =[ ~c₁; ~c₂ ]=> st' All goals completed! 🐙 )
Exercise★★★(Com.congruence_if)
theorem Com.congruence_if {b b' : Bexp} {c₁ c₁' c₂ c₂' : Com} (hb : b ≃ b') (hc₁ : c₁ ≃ c₁') (hc₂ : c₂ ≃ c₂') : (imp {if (b) {c₁} else {c₂}}).Equiv (imp {if (b') {c₁'} else {c₂'}}) := b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'⊢ imp {if (~b) {~c₁} else {~c₂}}.Equiv (imp {if (~b') {~c₁'} else {~c₂'}}) solution!( b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') ↔ st =[ if (~b') {~c₁'} else {~c₂'} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ if (~b') {~c₁'} else {~c₂'} ]=> st'b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ if (~b') {~c₁'} else {~c₂'} ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ if (~b) {~c₁} else {~c₂} ]=> st') → st =[ if (~b') {~c₁'} else {~c₂'} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Stateh:st =[ if (~b) {~c₁} else {~c₂} ]=> st'⊢ st =[ if (~b') {~c₁'} else {~c₂'} ]=> st' inversion h with | ifTrue hb' hc₁' => b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ st =[ if (~b') {~c₁'} else {~c₂'} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ Bexp.eval st b' = trueb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ c₁'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ Bexp.eval st b' = trueb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ c₁'.EvalR st st' try b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ c₁'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ c₁'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = truehc₁':c₁.EvalR st st'⊢ c₁'.EvalR st st' All goals completed! 🐙 | ifFalse hb' hc₂' => b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ st =[ if (~b') {~c₁'} else {~c₂'} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ Bexp.eval st b' = falseb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ c₂'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ Bexp.eval st b' = falseb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ c₂'.EvalR st st' try b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ c₂'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ c₂'.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:∀ {st st' : State}, (st =[ ~c₂ ]=> st') ↔ st =[ ~c₂' ]=> st'st:Statest':Statehb':Bexp.eval st b' = falsehc₂':c₂.EvalR st st'⊢ c₂'.EvalR st st' All goals completed! 🐙 b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ if (~b') {~c₁'} else {~c₂'} ]=> st') → st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Stateh:st =[ if (~b') {~c₁'} else {~c₂'} ]=> st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' inversion h with | ifTrue hb' hc₁' => b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ Bexp.eval st b = trueb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ c₁.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ Bexp.eval st b = trueb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ c₁.EvalR st st' try b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ c₁.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ c₁.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₁' ]=> st'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = truehc₁':c₁'.EvalR st st'⊢ c₁.EvalR st st' All goals completed! 🐙 | ifFalse hb' hc₂' => b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ st =[ if (~b) {~c₁} else {~c₂} ]=> st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ Bexp.eval st b = falseb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ c₂.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ Bexp.eval st b = falseb:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ c₂.EvalR st st' try b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ c₂.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ c₂.EvalR st st' b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:∀ {st st' : State}, (st =[ ~c₂ ]=> st') ↔ st =[ ~c₂' ]=> st'st:Statest':Statehb':Bexp.eval st b = falsehc₂':c₂'.EvalR st st'⊢ c₂.EvalR st st' All goals completed! 🐙 )

For example, here are two programs and a proof of their equivalence using their congruence theorems.

example : imp {X := 0; if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {X := 0; if (X = 0) {Y := X - X} else {Y := 42}} := ⊢ imp {X := 0; if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {X := 0; if (X = 0) {Y := X - X} else {Y := 42}} ⊢ imp {X := 0} ≃ imp {X := 0}⊢ imp {if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {if (X = 0) {Y := X - X} else {Y := 42}} ⊢ imp {X := 0} ≃ imp {X := 0} All goals completed! 🐙 ⊢ imp {if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {if (X = 0) {Y := X - X} else {Y := 42}} ⊢ bexp {X = 0} ≃ bexp {X = 0}⊢ imp {Y := 0} ≃ imp {Y := X - X}⊢ imp {Y := 42} ≃ imp {Y := 42} ⊢ bexp {X = 0} ≃ bexp {X = 0} All goals completed! 🐙 ⊢ imp {Y := 0} ≃ imp {Y := X - X} ⊢ aexp {0} ≃ aexp {X - X} All goals completed! 🐙 ⊢ imp {Y := 42} ≃ imp {Y := 42} All goals completed! 🐙
Exercise★★★(not_congr) (Advanced, Manually graded)

We've shown that the Com.Equiv relation is both an equivalence and a congruence on commands. Can you think of a relation on commands that is an equivalence but not a congruence? Write down the relation (formally), together with an informal sketch of a proof that it is an equivalence and a counterexample showing it is not a congruence.

4.3. Program Transformation🔗

A program transformation is a function that takes a program as input and produces a modified program as output. Compiler optimizations such as constant folding are canonical examples, but there are many others.

def Aexp.TransSound (trans : Aexp → Aexp) : Prop := ∀ (a : Aexp), a ≃ (trans a) @[simp] theorem Aexp.transSound_def {trans : Aexp → Aexp} : TransSound trans ↔ ∀ (a : Aexp), a ≃ (trans a) := trans:Aexp → Aexp⊢ TransSound trans ↔ ∀ (a : Aexp), a ≃ trans a All goals completed! 🐙 def Bexp.TransSound (trans : Bexp → Bexp) : Prop := ∀ (b : Bexp), b ≃ (trans b) @[simp] theorem Bexp.transSound_def {trans : Bexp → Bexp} : TransSound trans ↔ ∀ (b : Bexp), b ≃ (trans b) := trans:Bexp → Bexp⊢ TransSound trans ↔ ∀ (b : Bexp), b ≃ trans b All goals completed! 🐙 def Com.TransSound (trans : Com → Com) : Prop := ∀ (c : Com), c ≃ (trans c) @[simp] theorem Com.transSound_def {trans : Com → Com} : TransSound trans ↔ ∀ (c : Com), c ≃ (trans c) := trans:Com → Com⊢ TransSound trans ↔ ∀ (c : Com), c ≃ trans c All goals completed! 🐙

4.3.1. The Constant-Folding Transformation🔗

An expression is constant if it contains no variable references.

Constant folding is an optimization that finds constant expressions and replaces them by their values.

def Aexp.foldConstants (a : Aexp) : Aexp := match a with | .num n => .num n | .id x => .id x | aexp { ~a₁ + ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => .num (n₁ + n₂) | a₁', a₂' => aexp { ~a₁' + ~a₂' } | aexp { ~a₁ - ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => .num (n₁ - n₂) | a₁', a₂' => aexp { ~a₁' - ~a₂' } | aexp { ~a₁ * ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => .num (n₁ * n₂) | a₁', a₂' => aexp { ~a₁' * ~a₂' } @[simp] theorem Aexp.foldConstants_num (n : Nat) : (Aexp.num n).foldConstants = .num n := rfl @[simp] theorem Aexp.foldConstants_id (x : Ident) : (Aexp.id x).foldConstants = .id x := rfl theorem Aexp.foldConstants_cases (a₁ a₂ : Aexp) : (∃ n₁ n₂, a₁.foldConstants = .num n₁ ∧ a₂.foldConstants = .num n₂) ∨ (aexp {a₁ + a₂}).foldConstants = (aexp {~a₁.foldConstants + ~a₂.foldConstants}) ∧ (aexp {a₁ - a₂}).foldConstants = (aexp {~a₁.foldConstants - ~a₂.foldConstants}) ∧ (aexp {a₁ * a₂}).foldConstants = (aexp {~a₁.foldConstants * ~a₂.foldConstants}) := a₁:Aexpa₂:Aexp⊢ (∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁.foldConstants + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁.foldConstants - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~a₁.foldConstants * ~a₂.foldConstants} cases ha₁ : a₁.foldConstants with a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * ~a₂.foldConstants} cases ha₂ : a₂.foldConstants with a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁n₂:Natha₂:a₂.foldConstants = num n₂⊢ (∃ n₁_1 n₂_1, num n₁ = num n₁_1 ∧ num n₂ = num n₂_1) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + ~(num n₂)} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - ~(num n₂)} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * ~(num n₂)} a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁n₂:Natha₂:a₂.foldConstants = num n₂⊢ ∃ n₁_1 n₂_1, num n₁ = num n₁_1 ∧ num n₂ = num n₂_1 All goals completed! 🐙 a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ * ~a₂✝}⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ aexp {~a₁✝ * ~a₂✝} = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + ~a₁✝ * ~a₂✝} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - ~a₁✝ * ~a₂✝} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * (~a₁✝ * ~a₂✝)}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ - ~a₂✝}⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ aexp {~a₁✝ - ~a₂✝} = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + (~a₁✝ - ~a₂✝)} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - (~a₁✝ - ~a₂✝)} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * (~a₁✝ - ~a₂✝)}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ + ~a₂✝}⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ aexp {~a₁✝ + ~a₂✝} = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + (~a₁✝ + ~a₂✝)} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - (~a₁✝ + ~a₂✝)} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * (~a₁✝ + ~a₂✝)}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁x✝:Identha₂:a₂.foldConstants = aexp {x✝}⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ aexp {x✝} = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~(num n₁) + x✝} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~(num n₁) - x✝} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~(num n₁) * x✝} All goals completed! 🐙 a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ * ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ * ~a₂✝} = num n₁ ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁✝ * ~a₂✝ + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁✝ * ~a₂✝ - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~a₁✝ * ~a₂✝ * ~a₂.foldConstants}a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ - ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ - ~a₂✝} = num n₁ ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁✝ - ~a₂✝ + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁✝ - ~a₂✝ - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {(~a₁✝ - ~a₂✝) * ~a₂.foldConstants}a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ + ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ + ~a₂✝} = num n₁ ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁✝ + ~a₂✝ + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁✝ + ~a₂✝ - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {(~a₁✝ + ~a₂✝) * ~a₂.foldConstants}a₁:Aexpa₂:Aexpx✝:Identha₁:a₁.foldConstants = aexp {x✝}⊢ (∃ n₁ n₂, aexp {x✝} = num n₁ ∧ a₂.foldConstants = num n₂) ∨ aexp {~a₁ + ~a₂}.foldConstants = aexp {x✝ + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {x✝ - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {x✝ * ~a₂.foldConstants} All goals completed! 🐙
Note to developers (Niklas Halonen @xhalo32)

Make sure we have explained what named cases hypotheses does (cases ha₁ : a₁.foldConstants in the above proof).

example : (aexp { (1 + 2) * X }).foldConstants = (aexp { 3 * X }) := ⊢ aexp {(1 + 2) * X}.foldConstants = aexp {3 * X} All goals completed! 🐙

Note that this version of constant folding doesn't do other "obvious" things like eliminating trivial additions (e.g., rewriting 0 + X to just X).: we are focusing on a single optimization for the sake of simplicity.

It is not hard to incorporate other ways of simplifying expressions -- the definitions and proofs just get longer. We'll consider some in the exercises.

example : (aexp { X - ((0 * 6) + Y) }).foldConstants = (aexp { X - (0 + Y) }) := ⊢ aexp {X - (0 * 6 + Y)}.foldConstants = aexp {X - (0 + Y)} All goals completed! 🐙

Not only can we lift Aexp.foldConstants to Bexp in the Bexp.eq, Bexp.neq, and Bexp.le cases, we can also look for constant boolean expressions and evaluate them in place as well.

def Bexp.foldConstants (b : Bexp) : Bexp := match b with | bexp { true } => bexp { true } | bexp { false } => bexp { false } | bexp { ~a₁ = ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => if n₁ = n₂ then bexp { true } else bexp {false} | a₁', a₂' => bexp { a₁' = a₂' } | bexp { ~a₁ ≠ ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => if n₁ ≠ n₂ then bexp { true } else bexp {false} | a₁', a₂' => bexp { a₁' ≠ a₂' } | bexp { ~a₁ ≤ ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => if n₁ ≤ n₂ then bexp { true } else bexp {false} | a₁', a₂' => bexp { a₁' ≤ a₂' } | bexp { ~a₁ > ~a₂ } => match a₁.foldConstants, a₂.foldConstants with | .num n₁, .num n₂ => if n₁ > n₂ then bexp { true } else bexp {false} | a₁', a₂' => bexp { a₁' > a₂' } | bexp { ¬ ~b₁ } => match b₁.foldConstants with | bexp { true } => bexp { false } | bexp { false } => bexp { true } | b₁' => bexp { ¬ b₁' } | bexp { ~b₁ ∧ ~b₂ } => match b₁.foldConstants, b₂.foldConstants with | bexp { true }, bexp { true } => bexp { true } | bexp { true }, bexp { false } => bexp { false } | bexp { false }, bexp { true } => bexp { false } | bexp { false }, bexp { false } => bexp { false } | b₁', b₂' => bexp { b₁' ∧ b₂' } @[simp] theorem Bexp.foldConstants_true : (bexp { true }).foldConstants = (bexp { true }) := rfl @[simp] theorem Bexp.foldConstants_false : (bexp { false }).foldConstants = (bexp { false }) := rfl theorem Bexp.foldConstants_comp (a₁ a₂ : Aexp) : (∃ n₁ n₂, a₁.foldConstants = .num n₁ ∧ a₂.foldConstants = .num n₂) ∨ (bexp {~a₁ = ~a₂}).foldConstants = (bexp {~a₁.foldConstants = ~a₂.foldConstants}) ∧ (bexp {~a₁ ≠ ~a₂}).foldConstants = (bexp {~a₁.foldConstants ≠ ~a₂.foldConstants}) ∧ (bexp {~a₁ ≤ ~a₂}).foldConstants = (bexp {~a₁.foldConstants ≤ ~a₂.foldConstants}) ∧ (bexp {~a₁ > ~a₂}).foldConstants = (bexp {~a₁.foldConstants > ~a₂.foldConstants}) := a₁:Aexpa₂:Aexp⊢ (∃ n₁ n₂, a₁.foldConstants = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~a₁.foldConstants = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~a₁.foldConstants ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~a₁.foldConstants ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~a₁.foldConstants > ~a₂.foldConstants} cases ha₁ : a₁.foldConstants with a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~a₂.foldConstants} cases ha₂ : a₂.foldConstants with a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁n₂:Natha₂:a₂.foldConstants = Aexp.num n₂⊢ (∃ n₁_1 n₂_1, Aexp.num n₁ = Aexp.num n₁_1 ∧ Aexp.num n₂ = Aexp.num n₂_1) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~(Aexp.num n₂)} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~(Aexp.num n₂)} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~(Aexp.num n₂)} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~(Aexp.num n₂)} a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁n₂:Natha₂:a₂.foldConstants = Aexp.num n₂⊢ ∃ n₁_1 n₂_1, Aexp.num n₁ = Aexp.num n₁_1 ∧ Aexp.num n₂ = Aexp.num n₂_1 All goals completed! 🐙 a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ * ~a₂✝}⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ aexp {~a₁✝ * ~a₂✝} = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~a₁✝ * ~a₂✝} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~a₁✝ * ~a₂✝} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~a₁✝ * ~a₂✝} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~a₁✝ * ~a₂✝}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ - ~a₂✝}⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ aexp {~a₁✝ - ~a₂✝} = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~a₁✝ - ~a₂✝} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~a₁✝ - ~a₂✝} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~a₁✝ - ~a₂✝} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~a₁✝ - ~a₂✝}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁a₁✝:Aexpa₂✝:Aexpha₂:a₂.foldConstants = aexp {~a₁✝ + ~a₂✝}⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ aexp {~a₁✝ + ~a₂✝} = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~a₁✝ + ~a₂✝} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~a₁✝ + ~a₂✝} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~a₁✝ + ~a₂✝} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~a₁✝ + ~a₂✝}a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁x✝:Identha₂:a₂.foldConstants = aexp {x✝}⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ aexp {x✝} = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~(Aexp.num n₁) = x✝} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ x✝} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ x✝} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~(Aexp.num n₁) > x✝} All goals completed! 🐙 a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ * ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ * ~a₂✝} = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~a₁✝ * ~a₂✝ = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~a₁✝ * ~a₂✝ ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~a₁✝ * ~a₂✝ ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~a₁✝ * ~a₂✝ > ~a₂.foldConstants}a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ - ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ - ~a₂✝} = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~a₁✝ - ~a₂✝ = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~a₁✝ - ~a₂✝ ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~a₁✝ - ~a₂✝ ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~a₁✝ - ~a₂✝ > ~a₂.foldConstants}a₁:Aexpa₂:Aexpa₁✝:Aexpa₂✝:Aexpha₁:a₁.foldConstants = aexp {~a₁✝ + ~a₂✝}⊢ (∃ n₁ n₂, aexp {~a₁✝ + ~a₂✝} = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {~a₁✝ + ~a₂✝ = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {~a₁✝ + ~a₂✝ ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {~a₁✝ + ~a₂✝ ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {~a₁✝ + ~a₂✝ > ~a₂.foldConstants}a₁:Aexpa₂:Aexpx✝:Identha₁:a₁.foldConstants = aexp {x✝}⊢ (∃ n₁ n₂, aexp {x✝} = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨ bexp {~a₁ = ~a₂}.foldConstants = bexp {x✝ = ~a₂.foldConstants} ∧ bexp {~a₁ ≠ ~a₂}.foldConstants = bexp {x✝ ≠ ~a₂.foldConstants} ∧ bexp {~a₁ ≤ ~a₂}.foldConstants = bexp {x✝ ≤ ~a₂.foldConstants} ∧ bexp {~a₁ > ~a₂}.foldConstants = bexp {x✝ > ~a₂.foldConstants} All goals completed! 🐙 theorem Bexp.foldConstants_unary (b : Bexp) : (b.foldConstants = (bexp { true }) ∨ b.foldConstants = (bexp { false })) ∨ (bexp { ¬b }).foldConstants = (bexp { ¬(b.foldConstants)}) := b:Bexp⊢ (b.foldConstants = bexp {true} ∨ b.foldConstants = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ ~b.foldConstants} cases hb : b.foldConstants with b:Bexpb':Boolhb:b.foldConstants = bool b'⊢ (bool b' = bexp {true} ∨ bool b' = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ ~(bool b')} All goals completed! 🐙 b:Bexpb₁✝:Bexpb₂✝:Bexphb:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ (bexp {~b₁✝ ∧ ~b₂✝} = bexp {true} ∨ bexp {~b₁✝ ∧ ~b₂✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ (~b₁✝ ∧ ~b₂✝)}b:Bexpb✝:Bexphb:b.foldConstants = bexp {¬ ~b✝}⊢ (bexp {¬ ~b✝} = bexp {true} ∨ bexp {¬ ~b✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ ¬ ~b✝}b:Bexpa₁✝:Aexpa₂✝:Aexphb:b.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ (bexp {~a₁✝ > ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ > ~a₂✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ (~a₁✝ > ~a₂✝)}b:Bexpa₁✝:Aexpa₂✝:Aexphb:b.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ (bexp {~a₁✝ ≤ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≤ ~a₂✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ (~a₁✝ ≤ ~a₂✝)}b:Bexpa₁✝:Aexpa₂✝:Aexphb:b.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ (bexp {~a₁✝ ≠ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≠ ~a₂✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ (~a₁✝ ≠ ~a₂✝)}b:Bexpa₁✝:Aexpa₂✝:Aexphb:b.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ (bexp {~a₁✝ = ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ = ~a₂✝} = bexp {false}) ∨ bexp {¬ ~b}.foldConstants = bexp {¬ (~a₁✝ = ~a₂✝)} All goals completed! 🐙 theorem Bexp.foldConstants_binary (b₁ : Bexp) (b₂ : Bexp) : ((b₁.foldConstants = (bexp { true }) ∨ b₁.foldConstants = (bexp { false })) ∧ (b₂.foldConstants = (bexp { true }) ∨ b₂.foldConstants = (bexp { false }))) ∨ (bexp {b₁ ∧ b₂}).foldConstants = (bexp {b₁.foldConstants ∧ b₂.foldConstants}) := b₁:Bexpb₂:Bexp⊢ (b₁.foldConstants = bexp {true} ∨ b₁.foldConstants = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~b₁.foldConstants ∧ ~b₂.foldConstants} cases hb₁ : b₁.foldConstants with b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~b₂.foldConstants} cases hb₂ : b₂.foldConstants with b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'b₂':Boolhb₂:b₂.foldConstants = bool b₂'⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bool b₂' = bexp {true} ∨ bool b₂' = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~(bool b₂')} All goals completed! 🐙 b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'b₁✝:Bexpb₂✝:Bexphb₂:b₂.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {~b₁✝ ∧ ~b₂✝} = bexp {true} ∨ bexp {~b₁✝ ∧ ~b₂✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~b₁✝ ∧ ~b₂✝}b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'b✝:Bexphb₂:b₂.foldConstants = bexp {¬ ~b✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {¬ ~b✝} = bexp {true} ∨ bexp {¬ ~b✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ¬ ~b✝}b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'a₁✝:Aexpa₂✝:Aexphb₂:b₂.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {~a₁✝ > ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ > ~a₂✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~a₁✝ > ~a₂✝}b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'a₁✝:Aexpa₂✝:Aexphb₂:b₂.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {~a₁✝ ≤ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≤ ~a₂✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~a₁✝ ≤ ~a₂✝}b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'a₁✝:Aexpa₂✝:Aexphb₂:b₂.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {~a₁✝ ≠ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≠ ~a₂✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~a₁✝ ≠ ~a₂✝}b₁:Bexpb₂:Bexpb₁':Boolhb₁:b₁.foldConstants = bool b₁'a₁✝:Aexpa₂✝:Aexphb₂:b₂.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ (bool b₁' = bexp {true} ∨ bool b₁' = bexp {false}) ∧ (bexp {~a₁✝ = ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ = ~a₂✝} = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~(bool b₁') ∧ ~a₁✝ = ~a₂✝} All goals completed! 🐙 b₁:Bexpb₂:Bexpb₁✝:Bexpb₂✝:Bexphb₁:b₁.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ (bexp {~b₁✝ ∧ ~b₂✝} = bexp {true} ∨ bexp {~b₁✝ ∧ ~b₂✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {(~b₁✝ ∧ ~b₂✝) ∧ ~b₂.foldConstants}b₁:Bexpb₂:Bexpb✝:Bexphb₁:b₁.foldConstants = bexp {¬ ~b✝}⊢ (bexp {¬ ~b✝} = bexp {true} ∨ bexp {¬ ~b✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {¬ ~b✝ ∧ ~b₂.foldConstants}b₁:Bexpb₂:Bexpa₁✝:Aexpa₂✝:Aexphb₁:b₁.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ (bexp {~a₁✝ > ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ > ~a₂✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~a₁✝ > ~a₂✝ ∧ ~b₂.foldConstants}b₁:Bexpb₂:Bexpa₁✝:Aexpa₂✝:Aexphb₁:b₁.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ (bexp {~a₁✝ ≤ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≤ ~a₂✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~a₁✝ ≤ ~a₂✝ ∧ ~b₂.foldConstants}b₁:Bexpb₂:Bexpa₁✝:Aexpa₂✝:Aexphb₁:b₁.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ (bexp {~a₁✝ ≠ ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ ≠ ~a₂✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~a₁✝ ≠ ~a₂✝ ∧ ~b₂.foldConstants}b₁:Bexpb₂:Bexpa₁✝:Aexpa₂✝:Aexphb₁:b₁.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ (bexp {~a₁✝ = ~a₂✝} = bexp {true} ∨ bexp {~a₁✝ = ~a₂✝} = bexp {false}) ∧ (b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false}) ∨ bexp {~b₁ ∧ ~b₂}.foldConstants = bexp {~a₁✝ = ~a₂✝ ∧ ~b₂.foldConstants} All goals completed! 🐙 example : (bexp { true ∧ ¬( false ∧ true) }).foldConstants = (bexp { true }) := ⊢ bexp {true ∧ ¬ (false ∧ true)}.foldConstants = bexp {true} All goals completed! 🐙 example : (bexp { (X = Y) ∧ ( 0 = (2 - (1 + 1))) }).foldConstants = (bexp { (X = Y) ∧ true }) := ⊢ bexp {X = Y ∧ 0 = 2 - (1 + 1)}.foldConstants = bexp {X = Y ∧ true} All goals completed! 🐙

To fold constants in a command, we simply apply the appropriate folding functions on all embedded expressions.

def Com.foldConstants (c : Com) : Com := match c with | imp { skip } => imp { skip } | imp { x := ~a } => imp { x := ~a.foldConstants } | imp { c₁ ; c₂ } => imp { c₁.foldConstants ; c₂.foldConstants } | imp { if (b) { c₁ } else { c₂ }} => match b.foldConstants with | bexp { true } => c₁.foldConstants | bexp { false } => c₂.foldConstants | b' => imp { if (b') {c₁.foldConstants} else { c₂.foldConstants}} | imp { while (b) {c}} => match b.foldConstants with | bexp { true } => imp { while (true) { skip }} | bexp { false } => imp { skip } | b' => imp { while (b') {c.foldConstants}} example : (imp { X := 4 + 5; Y := X - 3; if ((X - Y) = (2 + 4)) {skip} else {Y := 0}; if (0 ≤ (4 - (2 - 1))) {Y := 0} else {skip}; while (Y = 0) {X := X+1} }).foldConstants = (imp { X := 9; Y := X - 3; if ((X - Y) = 6) {skip} else {Y := 0}; Y := 0; while (Y = 0) {X := X+1} }) := ⊢ imp {X := 4 + 5; Y := X - 3; if (X - Y = 2 + 4) {skip} else {Y := 0}; if (0 ≤ 4 - (2 - 1)) {Y := 0} else {skip}; while (Y = 0) {X := X + 1}}.foldConstants = imp {X := 9; Y := X - 3; if (X - Y = 6) {skip} else {Y := 0}; Y := 0; while (Y = 0) {X := X + 1}} All goals completed! 🐙

4.3.2. Soundness of Constant Folding🔗

Now we need to show that what we've done is correct.

Here's the proof for arithmetic expressions.

theorem Aexp.foldConstants_sound : TransSound Aexp.foldConstants := ⊢ TransSound foldConstants a:Aexpst:State⊢ eval st a = eval st a.foldConstants induction a with st:Staten:Nat⊢ eval st (num n) = eval st (num n).foldConstants st:Statex:Ident⊢ eval st (aexp {x}) = eval st aexp {x}.foldConstants st:Statex:Ident⊢ eval st (aexp {x}) = eval st aexp {x}.foldConstantsst:Staten:Nat⊢ eval st (num n) = eval st (num n).foldConstants All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstants⊢ eval st (aexp {~a₁ * ~a₂}) = eval st aexp {~a₁ * ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstants⊢ eval st (aexp {~a₁ - ~a₂}) = eval st aexp {~a₁ - ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstants⊢ eval st (aexp {~a₁ + ~a₂}) = eval st aexp {~a₁ + ~a₂}.foldConstants cases Aexp.foldConstants_cases a₁ a₂ with st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st (aexp {~a₁ * ~a₂}) = eval st aexp {~a₁ * ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st (aexp {~a₁ - ~a₂}) = eval st aexp {~a₁ - ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st (aexp {~a₁ + ~a₂}) = eval st aexp {~a₁ + ~a₂}.foldConstants st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsn₁:Natn₂:Nath₁:a₁.foldConstants = num n₁h₂:a₂.foldConstants = num n₂⊢ eval st (aexp {~a₁ * ~a₂}) = eval st aexp {~a₁ * ~a₂}.foldConstants All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁.foldConstants + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁.foldConstants - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~a₁.foldConstants * ~a₂.foldConstants}⊢ eval st (aexp {~a₁ * ~a₂}) = eval st aexp {~a₁ * ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁.foldConstants + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁.foldConstants - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~a₁.foldConstants * ~a₂.foldConstants}⊢ eval st (aexp {~a₁ - ~a₂}) = eval st aexp {~a₁ - ~a₂}.foldConstantsst:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁ = eval st a₁.foldConstantsa₂_ih✝:eval st a₂ = eval st a₂.foldConstantsh:aexp {~a₁ + ~a₂}.foldConstants = aexp {~a₁.foldConstants + ~a₂.foldConstants} ∧ aexp {~a₁ - ~a₂}.foldConstants = aexp {~a₁.foldConstants - ~a₂.foldConstants} ∧ aexp {~a₁ * ~a₂}.foldConstants = aexp {~a₁.foldConstants * ~a₂.foldConstants}⊢ eval st (aexp {~a₁ + ~a₂}) = eval st aexp {~a₁ + ~a₂}.foldConstants All goals completed! 🐙

An equivalent version using the fun_induction tactic would look simpler:

theorem Aexp.foldConstants_sound' : TransSound Aexp.foldConstants := ⊢ TransSound foldConstants a:Aexpst:State⊢ eval st a = eval st a.foldConstants st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)st:Statex✝:Ident⊢ eval st (aexp {x✝}) = eval st (aexp {x✝})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ + ~a₂✝}) = eval st (num (n₁✝ + n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ + ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants + ~a₂✝.foldConstants})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ - ~a₂✝}) = eval st (num (n₁✝ - n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ - ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants - ~a₂✝.foldConstants})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ * ~a₂✝}) = eval st (num (n₁✝ * n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ * ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants * ~a₂✝.foldConstants}) st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)st:Statex✝:Ident⊢ eval st (aexp {x✝}) = eval st (aexp {x✝})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ + ~a₂✝}) = eval st (num (n₁✝ + n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ + ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants + ~a₂✝.foldConstants})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ - ~a₂✝}) = eval st (num (n₁✝ - n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ - ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants - ~a₂✝.foldConstants})st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ * ~a₂✝}) = eval st (num (n₁✝ * n₂✝))st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝ = eval st a₁✝.foldConstantsih1✝:eval st a₂✝ = eval st a₂✝.foldConstants⊢ eval st (aexp {~a₁✝ * ~a₂✝}) = eval st (aexp {~a₁✝.foldConstants * ~a₂✝.foldConstants}) All goals completed! 🐙
Exercise★★★(Bexp.fold_eq_informal) (Optional, Manually graded)

Here is an informal proof of the eq case of the soundness argument for boolean expression constant folding. Read it carefully and compare it to the formal proof that follows. Then fill in the le case of the formal proof (without looking at the eq case, if possible).

Theorem: The constant folding function for booleans, Bexp.fold_constants, is sound.

Proof: We must show that b is equivalent to Bexp.fold_constants b, for all boolean expressions b. Proceed by induction on b. We show just the case where b has the form a₁ = a₂.

In this case, we must show

  (bexp { a₁ = a₂ }).eval st = (bexp { a₁ = a₂ }).foldConstants.eval st

There are two cases to consider:

  • First, suppose a₁.foldConstants = aexp { n₁ } and a₂.foldConstants = aexp { n₂ } for some n₁ and n₂.

    In this case, we have

  Bexp.fold_constants (bexp { a₁ = a₂ }) = if (n₁ = n₂) then bexp { true } else bexp { false }

and

  (bexp {a₁ = a₂}).eval st = a₁.eval st = a₂.eval st.

By the soundness of constant folding for arithmetic expressions (Aexp.foldConstants_sound), we know

           a₁.eval st
         = (a₁.foldConstants).eval st
         = (aexp { n₁ }).eval st
         = n₁

and

           a₂.eval st
         = (a₂.foldConstants).eval st
         = (aexp { n₂ }).eval st
         = n₂

so

          bexp { a₁ = a₂ }.eval st
         = a₁.eval st = a₂.aeval st
         = n₁ = n₂

Also, it is easy to see (by considering the cases n₁ = n₂ and n₁ ≠ n₂ separately) that

          (if n₁ = n₂ then (bexp { true }) else (bexp { false }) ).eval st
         = if n₁ = n₂ then bexp { true }.eval st else bexp { false }.eval st
         = if n₁ = n₂ then true else false
         = n₁ = n₂

So

          (bexp { a₁ = a₂ }).eval st
         = n₁ = n₂.
         = (if n₁ = n₂ then (bexp { true }) else (bexp { false }) ).eval st,

as required.

  • Otherwise, one of a₁.foldConstants and a₂.foldConstants is not a constant. In this case, we must show

           bexp { a₁ = a₂ }.eval st
         = (bexp { (a₁.foldConstants = a₂.foldConstants) }).eval st,

which, by the definition of Bexp.eval, is the same as showing

           a₁.eval st = a₂.eval st
        = (a₁.foldConstants).eval st = (a₂.foldConstants).eval st

But the soundness of constant folding for arithmetic expressions (Aexp.foldConstants_sound) gives us

         a₁ = (a₁.foldConstants).eval st
         a₂ = (a₂.foldConstants).eval st

completing the case.

theorem Bexp.foldConstants_sound : Bexp.TransSound Bexp.foldConstants := ⊢ TransSound foldConstants b:Bexpst:State⊢ eval st b = eval st b.foldConstants induction b with st:Stateb:Bool⊢ eval st (bool b) = eval st (bool b).foldConstants st:State⊢ eval st (bexp {false}) = eval st bexp {false}.foldConstantsst:State⊢ eval st (bexp {true}) = eval st bexp {true}.foldConstants st:State⊢ eval st (bexp {false}) = eval st bexp {false}.foldConstantsst:State⊢ eval st (bexp {true}) = eval st bexp {true}.foldConstants All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexp⊢ eval st (bexp {~a₁ = ~a₂}) = eval st bexp {~a₁ = ~a₂}.foldConstants st:Statea₁:Aexpa₂:Aexp⊢ (Aexp.eval st a₁ == Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstants⊢ (Aexp.eval st a₁ == Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ (Aexp.eval st a₁ == Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ (Aexp.eval st a₁.foldConstants == Aexp.eval st a₂.foldConstants) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Natx✝:Ident⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st (aexp {x✝})) = eval st (match Aexp.num n✝, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identn✝:Nat⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {x✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝¹:Identx✝:Ident⊢ (Aexp.eval st (aexp {x✝¹}) == Aexp.eval st (aexp {x✝})) = eval st (match aexp {x✝¹}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) == Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ + ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) == Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ + ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) == Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ - ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) == Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ - ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) == Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) == Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) case num.num n₁ n₂ st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) -- The only interesting case is when both a₁ and a₂ become constants after folding st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) == Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ = n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' = ~a₂'}) All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexp⊢ eval st (bexp {~a₁ ≠ ~a₂}) = eval st bexp {~a₁ ≠ ~a₂}.foldConstants st:Statea₁:Aexpa₂:Aexp⊢ (Aexp.eval st a₁ != Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstants⊢ (Aexp.eval st a₁ != Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ (Aexp.eval st a₁ != Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ (Aexp.eval st a₁.foldConstants != Aexp.eval st a₂.foldConstants) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Natx✝:Ident⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st (aexp {x✝})) = eval st (match Aexp.num n✝, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (Aexp.num n✝) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identn✝:Nat⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {x✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝¹:Identx✝:Ident⊢ (Aexp.eval st (aexp {x✝¹}) != Aexp.eval st (aexp {x✝})) = eval st (match aexp {x✝¹}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {x✝}) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) != Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ + ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) != Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ + ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) != Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ - ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) != Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ - ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) != Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) != Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) case num.num n₁ n₂ st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nat⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ = n₂⊢ (Aexp.eval st (Aexp.num n✝¹) != Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≠ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≠ ~a₂'}) All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexp⊢ eval st (bexp {~a₁ ≤ ~a₂}) = eval st bexp {~a₁ ≤ ~a₂}.foldConstants solution! st:Statea₁:Aexpa₂:Aexp⊢ decide (Aexp.eval st a₁ ≤ Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstants⊢ decide (Aexp.eval st a₁ ≤ Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ decide (Aexp.eval st a₁ ≤ Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ decide (Aexp.eval st a₁.foldConstants ≤ Aexp.eval st a₂.foldConstants) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Natx✝:Ident⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st (aexp {x✝})) = eval st (match Aexp.num n✝, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identn✝:Nat⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {x✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝¹:Identx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝¹}) ≤ Aexp.eval st (aexp {x✝})) = eval st (match aexp {x✝¹}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ + ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) ≤ Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ + ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ - ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) ≤ Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ - ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) ≤ Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) ≤ Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) case num.num n₁ n₂ st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) ≤ Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ ≤ n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' ≤ ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nat⊢ decide (n₁ ≤ n₂) = eval st (if n₁ ≤ n₂ then bexp {true} else bexp {false}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ ≤ n₂⊢ decide (n₁ ≤ n₂) = eval st (if n₁ ≤ n₂ then bexp {true} else bexp {false})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ ≤ n₂⊢ decide (n₁ ≤ n₂) = eval st (if n₁ ≤ n₂ then bexp {true} else bexp {false}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₁ ≤ n₂⊢ decide (n₁ ≤ n₂) = eval st (if n₁ ≤ n₂ then bexp {true} else bexp {false})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₁ ≤ n₂⊢ decide (n₁ ≤ n₂) = eval st (if n₁ ≤ n₂ then bexp {true} else bexp {false}) All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexp⊢ eval st (bexp {~a₁ > ~a₂}) = eval st bexp {~a₁ > ~a₂}.foldConstants solution! st:Statea₁:Aexpa₂:Aexp⊢ decide (Aexp.eval st a₁ > Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstants⊢ decide (Aexp.eval st a₁ > Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ decide (Aexp.eval st a₁ > Aexp.eval st a₂) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstants⊢ decide (Aexp.eval st a₁.foldConstants > Aexp.eval st a₂.foldConstants) = eval st (match a₁.foldConstants, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st a₂.foldConstants) = eval st (match Aexp.num n✝, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {x✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ + ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ - ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st a₂.foldConstants) = eval st (match aexp {~a₁✝ * ~a₂✝}, a₂.foldConstants with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Natx✝:Ident⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st (aexp {x✝})) = eval st (match Aexp.num n✝, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝:Nata₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (Aexp.num n✝) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match Aexp.num n✝, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identn✝:Nat⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {x✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝¹:Identx✝:Ident⊢ decide (Aexp.eval st (aexp {x✝¹}) > Aexp.eval st (aexp {x✝})) = eval st (match aexp {x✝¹}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsx✝:Identa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {x✝}) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {x✝}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) > Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ + ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ + ~a₂✝}) > Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ + ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ + ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ + ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) > Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ - ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ - ~a₂✝}) > Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ - ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ - ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ - ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpn✝:Nat⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st (Aexp.num n✝)) = eval st (match aexp {~a₁✝ * ~a₂✝}, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝:Aexpa₂✝:Aexpx✝:Ident⊢ decide (Aexp.eval st (aexp {~a₁✝ * ~a₂✝}) > Aexp.eval st (aexp {x✝})) = eval st (match aexp {~a₁✝ * ~a₂✝}, aexp {x✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ + ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ + ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ - ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ - ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st (aexp {~a₁✝¹ * ~a₂✝¹}) > Aexp.eval st (aexp {~a₁✝ * ~a₂✝})) = eval st (match aexp {~a₁✝¹ * ~a₂✝¹}, aexp {~a₁✝ * ~a₂✝} with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn✝¹:Natn✝:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) case num.num n₁ n₂ st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nat⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₂ < n₁⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₂ < n₁⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:n₂ < n₁⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'})st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁ = Aexp.eval st a₁.foldConstantsh₂:Aexp.eval st a₂ = Aexp.eval st a₂.foldConstantsn₁:Natn₂:Nath✝:¬n₂ < n₁⊢ decide (Aexp.eval st (Aexp.num n✝¹) > Aexp.eval st (Aexp.num n✝)) = eval st (match Aexp.num n✝¹, Aexp.num n✝ with | Aexp.num n₁, Aexp.num n₂ => if n₁ > n₂ then bexp {true} else bexp {false} | a₁', a₂' => bexp {~a₁' > ~a₂'}) All goals completed! 🐙 st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ eval st (bexp {¬ ~b}) = eval st bexp {¬ ~b}.foldConstants st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st b) = eval st (match b.foldConstants with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st b.foldConstants) = eval st (match b.foldConstants with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb✝:Bool⊢ (!eval st (bool b✝)) = eval st (match bool b✝ with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb✝:Bexp⊢ (!eval st (bexp {¬ ~b✝})) = eval st (match bexp {¬ ~b✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb₁✝:Bexpb₂✝:Bexp⊢ (!eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb✝:Bool⊢ (!eval st (bool b✝)) = eval st (match bool b✝ with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (!eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb✝:Bexp⊢ (!eval st (bexp {¬ ~b✝})) = eval st (match bexp {¬ ~b✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb₁✝:Bexpb₂✝:Bexp⊢ (!eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Stateb:Bexpih:eval st b = eval st b.foldConstantsb✝:Bool⊢ (!eval st (bool b✝)) = eval st (match bool b✝ with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) case not.bool b st:Stateb✝:Bexpih:eval st b = eval st b.foldConstantsb:Bool⊢ (!eval st (bool b✝)) = eval st (match bool b✝ with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st (bexp {false})) = eval st (match bexp {false} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st (bexp {true})) = eval st (match bexp {true} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st (bexp {false})) = eval st (match bexp {false} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'})st:Stateb:Bexpih:eval st b = eval st b.foldConstants⊢ (!eval st (bexp {true})) = eval st (match bexp {true} with | bexp {true} => bexp {false} | bexp {false} => bexp {true} | b₁' => bexp {¬ ~b₁'}) All goals completed! 🐙 st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ eval st (bexp {~b₁ ∧ ~b₂}) = eval st bexp {~b₁ ∧ ~b₂}.foldConstants st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st b₁ && eval st b₂) = eval st (match b₁.foldConstants, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st b₁.foldConstants && eval st b₂.foldConstants) = eval st (match b₁.foldConstants, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bool⊢ (eval st (bool b✝) && eval st b₂.foldConstants) = eval st (match bool b✝, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ = ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ = ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ ≠ ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ ≠ ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ ≤ ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ ≤ ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ > ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ > ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexp⊢ (eval st (bexp {¬ ~b✝}) && eval st b₂.foldConstants) = eval st (match bexp {¬ ~b✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bool⊢ (eval st (bool b✝) && eval st b₂.foldConstants) = eval st (match bool b✝, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ = ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ = ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ ≠ ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ ≠ ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ ≤ ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ ≤ ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝ > ~a₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~a₁✝ > ~a₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexp⊢ (eval st (bexp {¬ ~b✝}) && eval st b₂.foldConstants) = eval st (match bexp {¬ ~b✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st b₂.foldConstants) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, b₂.foldConstants with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpb✝:Bool⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bool b✝)) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpb✝:Bexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝¹:Bexpb₂✝¹:Bexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~b₁✝¹ ∧ ~b₂✝¹}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~b₁✝¹ ∧ ~b₂✝¹}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝¹:Boolb✝:Bool⊢ (eval st (bool b✝¹) && eval st (bool b✝)) = eval st (match bool b✝¹, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Boola₁✝:Aexpa₂✝:Aexp⊢ (eval st (bool b✝) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bool b✝, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Boola₁✝:Aexpa₂✝:Aexp⊢ (eval st (bool b✝) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bool b✝, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Boola₁✝:Aexpa₂✝:Aexp⊢ (eval st (bool b✝) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bool b✝, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Boola₁✝:Aexpa₂✝:Aexp⊢ (eval st (bool b✝) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bool b✝, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝¹:Boolb✝:Bexp⊢ (eval st (bool b✝¹) && eval st (bexp {¬ ~b✝})) = eval st (match bool b✝¹, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Boolb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bool b✝) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bool b✝, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bool⊢ (eval st (bexp {~a₁✝ = ~a₂✝}) && eval st (bool b✝)) = eval st (match bexp {~a₁✝ = ~a₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ = ~a₂✝¹}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝¹ = ~a₂✝¹}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ = ~a₂✝¹}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝¹ = ~a₂✝¹}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ = ~a₂✝¹}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝¹ = ~a₂✝¹}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ = ~a₂✝¹}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝¹ = ~a₂✝¹}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bexp⊢ (eval st (bexp {~a₁✝ = ~a₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~a₁✝ = ~a₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~a₁✝ = ~a₂✝}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~a₁✝ = ~a₂✝}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bool⊢ (eval st (bexp {~a₁✝ ≠ ~a₂✝}) && eval st (bool b✝)) = eval st (match bexp {~a₁✝ ≠ ~a₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≠ ~a₂✝¹}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≠ ~a₂✝¹}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≠ ~a₂✝¹}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≠ ~a₂✝¹}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≠ ~a₂✝¹}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≠ ~a₂✝¹}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≠ ~a₂✝¹}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≠ ~a₂✝¹}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bexp⊢ (eval st (bexp {~a₁✝ ≠ ~a₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~a₁✝ ≠ ~a₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~a₁✝ ≠ ~a₂✝}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~a₁✝ ≠ ~a₂✝}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bool⊢ (eval st (bexp {~a₁✝ ≤ ~a₂✝}) && eval st (bool b✝)) = eval st (match bexp {~a₁✝ ≤ ~a₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≤ ~a₂✝¹}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≤ ~a₂✝¹}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≤ ~a₂✝¹}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≤ ~a₂✝¹}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≤ ~a₂✝¹}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≤ ~a₂✝¹}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ ≤ ~a₂✝¹}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝¹ ≤ ~a₂✝¹}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bexp⊢ (eval st (bexp {~a₁✝ ≤ ~a₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~a₁✝ ≤ ~a₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~a₁✝ ≤ ~a₂✝}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~a₁✝ ≤ ~a₂✝}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bool⊢ (eval st (bexp {~a₁✝ > ~a₂✝}) && eval st (bool b✝)) = eval st (match bexp {~a₁✝ > ~a₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ > ~a₂✝¹}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~a₁✝¹ > ~a₂✝¹}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ > ~a₂✝¹}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~a₁✝¹ > ~a₂✝¹}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ > ~a₂✝¹}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~a₁✝¹ > ~a₂✝¹}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝¹:Aexpa₂✝¹:Aexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~a₁✝¹ > ~a₂✝¹}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~a₁✝¹ > ~a₂✝¹}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb✝:Bexp⊢ (eval st (bexp {~a₁✝ > ~a₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~a₁✝ > ~a₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsa₁✝:Aexpa₂✝:Aexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~a₁✝ > ~a₂✝}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~a₁✝ > ~a₂✝}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝¹:Bexpb✝:Bool⊢ (eval st (bexp {¬ ~b✝¹}) && eval st (bool b✝)) = eval st (match bexp {¬ ~b✝¹}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {¬ ~b✝}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {¬ ~b✝}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {¬ ~b✝}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {¬ ~b✝}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {¬ ~b✝}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {¬ ~b✝}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {¬ ~b✝}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {¬ ~b✝}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝¹:Bexpb✝:Bexp⊢ (eval st (bexp {¬ ~b✝¹}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {¬ ~b✝¹}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝:Bexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {¬ ~b✝}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {¬ ~b✝}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpb✝:Bool⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bool b✝)) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ = ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ = ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ ≠ ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ ≤ ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpa₁✝:Aexpa₂✝:Aexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {~a₁✝ > ~a₂✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {~a₁✝ > ~a₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝:Bexpb₂✝:Bexpb✝:Bexp⊢ (eval st (bexp {~b₁✝ ∧ ~b₂✝}) && eval st (bexp {¬ ~b✝})) = eval st (match bexp {~b₁✝ ∧ ~b₂✝}, bexp {¬ ~b✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁✝¹:Bexpb₂✝¹:Bexpb₁✝:Bexpb₂✝:Bexp⊢ (eval st (bexp {~b₁✝¹ ∧ ~b₂✝¹}) && eval st (bexp {~b₁✝ ∧ ~b₂✝})) = eval st (match bexp {~b₁✝¹ ∧ ~b₂✝¹}, bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) (try All goals completed! 🐙; All goals completed! 🐙) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb✝¹:Boolb✝:Bool⊢ (eval st (bool b✝¹) && eval st (bool b✝)) = eval st (match bool b✝¹, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) case and.bool.bool b₁ b₂ st:Stateb₁✝:Bexpb₂✝:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₁:Boolb₂:Bool⊢ (eval st (bool b✝¹) && eval st (bool b✝)) = eval st (match bool b✝¹, bool b✝ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂✝:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₂:Bool⊢ (eval st (bexp {false}) && eval st (bool b₂)) = eval st (match bexp {false}, bool b₂ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂✝:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₂:Bool⊢ (eval st (bexp {true}) && eval st (bool b₂)) = eval st (match bexp {true}, bool b₂ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂✝:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₂:Bool⊢ (eval st (bexp {false}) && eval st (bool b₂)) = eval st (match bexp {false}, bool b₂ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂✝:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstantsb₂:Bool⊢ (eval st (bexp {true}) && eval st (bool b₂)) = eval st (match bexp {true}, bool b₂ with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {true}) && eval st (bexp {false})) = eval st (match bexp {true}, bexp {false} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {true}) && eval st (bexp {true})) = eval st (match bexp {true}, bexp {true} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {false}) && eval st (bexp {false})) = eval st (match bexp {false}, bexp {false} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {false}) && eval st (bexp {true})) = eval st (match bexp {false}, bexp {true} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {true}) && eval st (bexp {false})) = eval st (match bexp {true}, bexp {false} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'})st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.foldConstantsih₂:eval st b₂ = eval st b₂.foldConstants⊢ (eval st (bexp {true}) && eval st (bexp {true})) = eval st (match bexp {true}, bexp {true} with | bexp {true}, bexp {true} => bexp {true} | bexp {true}, bexp {false} => bexp {false} | bexp {false}, bexp {true} => bexp {false} | bexp {false}, bexp {false} => bexp {false} | b₁', b₂' => bexp {~b₁' ∧ ~b₂'}) All goals completed! 🐙
Exercise★★★(Com.foldConstants_sound) (Optional, Manually graded)

Complete the while case of the following proof.

theorem Com.foldConstants_sound : Com.TransSound Com.foldConstants := ⊢ TransSound foldConstants c:Com⊢ c ≃ c.foldConstants induction c with ⊢ imp {skip} ≃ imp {skip}.foldConstants All goals completed! 🐙 x:Identa:Aexp⊢ imp {x := ~a} ≃ imp {x := ~a}.foldConstants x:Identa:Aexp⊢ a ≃ a.foldConstants All goals completed! 🐙 c₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ imp {~c₁; ~c₂} ≃ imp {~c₁; ~c₂}.foldConstants c₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ c₁ ≃ c₁.foldConstantsc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ c₂ ≃ c₂.foldConstants c₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ c₁ ≃ c₁.foldConstantsc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ c₂ ≃ c₂.foldConstants All goals completed! 🐙 b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ imp {if (~b) {~c₁} else {~c₂}}.foldConstants b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstants⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match b.foldConstants with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstants⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match b.foldConstants with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} -- If the optimization doesn't eliminate the `if`, then the -- result is easy to prove from the `ih` and -- `Bexp.foldConstants_sound` b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match Bexp.bool b✝ with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb✝:Bexpheq:b.foldConstants = bexp {¬ ~b✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {¬ ~b✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match Bexp.bool b✝ with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb✝:Bexpheq:b.foldConstants = bexp {¬ ~b✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {¬ ~b✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} try (b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ b ≃ bexp {~b₁✝ ∧ ~b₂✝}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c₁ ≃ c₁.foldConstantsb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c₂ ≃ c₂.foldConstants b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ b ≃ bexp {~b₁✝ ∧ ~b₂✝}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c₁ ≃ c₁.foldConstantsb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c₂ ≃ c₂.foldConstants All goals completed! 🐙) b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match Bexp.bool b✝ with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} case cond.bool b b✝:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsb:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match Bexp.bool b✝ with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} cases b with b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {false} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ ?false.c₂b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ ?false.c₂ ≃ match bexp {false} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ Com b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ ?false.c₂b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ ?false.c₂ ≃ match bexp {false} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ Com try All goals completed! 🐙 b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ b ≃ bexp {false}; All goals completed! 🐙 b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ match bexp {true} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}} b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ ?true.c₂b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ ?true.c₂ ≃ match bexp {true} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ Com b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ ?true.c₂b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ ?true.c₂ ≃ match bexp {true} with | bexp {true} => c₁.foldConstants | bexp {false} => c₂.foldConstants | b' => imp {if (~b') {~c₁.foldConstants} else {~c₂.foldConstants}}b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ Com try All goals completed! 🐙 b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.foldConstantsih₂:c₂ ≃ c₂.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ b ≃ bexp {true}; All goals completed! 🐙 b:Bexpc:Comih:c ≃ c.foldConstants⊢ imp {while (~b) {~c}} ≃ imp {while (~b) {~c}}.foldConstants solution! b:Bexpc:Comih:c ≃ c.foldConstants⊢ imp {while (~b) {~c}} ≃ match b.foldConstants with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstants⊢ imp {while (~b) {~c}} ≃ match b.foldConstants with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {while (~b) {~c}} ≃ match Bexp.bool b✝ with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb✝:Bexpheq:b.foldConstants = bexp {¬ ~b✝}⊢ imp {while (~b) {~c}} ≃ match bexp {¬ ~b✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {while (~b) {~c}} ≃ match Bexp.bool b✝ with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ = ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ = ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≠ ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ ≠ ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ ≤ ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ ≤ ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsa₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {~a₁✝ > ~a₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~a₁✝ > ~a₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb✝:Bexpheq:b.foldConstants = bexp {¬ ~b✝}⊢ imp {while (~b) {~c}} ≃ match bexp {¬ ~b✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ imp {while (~b) {~c}} ≃ match bexp {~b₁✝ ∧ ~b₂✝} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} try (b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ b ≃ bexp {~b₁✝ ∧ ~b₂✝}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c ≃ c.foldConstants b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ b ≃ bexp {~b₁✝ ∧ ~b₂✝}b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {~b₁✝ ∧ ~b₂✝}⊢ c ≃ c.foldConstants All goals completed! 🐙) b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {while (~b) {~c}} ≃ match Bexp.bool b✝ with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} case whileDo.bool b b✝:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsb:Boolheq:b.foldConstants = Bexp.bool b✝⊢ imp {while (~b) {~c}} ≃ match Bexp.bool b✝ with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} cases b with b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ imp {while (~b) {~c}} ≃ match bexp {false} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {false}⊢ b ≃ bexp {false}; All goals completed! 🐙 b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ imp {while (~b) {~c}} ≃ match bexp {true} with | bexp {true} => imp {while (true) {skip}} | bexp {false} => imp {skip} | b' => imp {while (~b') {~c.foldConstants}} b:Bexpc:Comih:c ≃ c.foldConstantshb:b ≃ b.foldConstantsheq:b.foldConstants = bexp {true}⊢ b ≃ bexp {true}; All goals completed! 🐙

4.4. Soundness of (0 + n) Elimination, Redux🔗

Exercise★★★★(optimize0plus_var) (Optional)

Recall the definition optimize0plus from the Slang chapter:

def optimize0plus (a : Aexp) : Aexp :=
  match a with
  | num   n          => num n
  | plus  (num 0) e₂ => optimize0plus e₂
  | plus  e₁      e₂ => plus  (optimize0plus e₁) (optimize0plus e₂)
  | minus e₁      e₂ => minus (optimize0plus e₁) (optimize0plus e₂)
  | mult  e₁      e₂ => mult  (optimize0plus e₁) (optimize0plus e₂)

Note that this function is defined over the old version of Aexps, without states.

Write a new version of this function that deals with variables (by leaving them alone), plus analogous ones for Bexps and commands:

Aexp.optimize0plus
Bexp.optimize0plus
Com.optimize0plus
def Aexp.optimize0plus (a : Aexp) : Aexp := solution!( match a with | Aexp.num n => Aexp.num n | Aexp.id x => Aexp.id x | (aexp { 0 + ~a₂ }) => Aexp.optimize0plus a₂ | (aexp { ~a₁ + ~a₂ }) => (aexp { ~(Aexp.optimize0plus a₁) + ~(Aexp.optimize0plus a₂) }) | (aexp { ~a₁ - ~a₂ }) => (aexp { ~(Aexp.optimize0plus a₁) - ~(Aexp.optimize0plus a₂) }) | (aexp { ~a₁ * ~a₂ }) => (aexp { ~(Aexp.optimize0plus a₁) * ~(Aexp.optimize0plus a₂) }) ) def Bexp.optimize0plus (b : Bexp) : Bexp := solution!( match b with | (bexp { true }) => (bexp { true }) | (bexp { false }) => (bexp { false }) | (bexp { ~a₁ = ~a₂ }) => (bexp { ~(Aexp.optimize0plus a₁) = ~(Aexp.optimize0plus a₂) }) | (bexp { ~a₁ ≠ ~a₂ }) => (bexp { ~(Aexp.optimize0plus a₁) ≠ ~(Aexp.optimize0plus a₂) }) | (bexp { ~a₁ ≤ ~a₂ }) => (bexp { ~(Aexp.optimize0plus a₁) ≤ ~(Aexp.optimize0plus a₂) }) | (bexp { ~a₁ > ~a₂ }) => (bexp { ~(Aexp.optimize0plus a₁) > ~(Aexp.optimize0plus a₂) }) | (bexp { ¬ ~b₁ }) => (bexp { ¬ ~(Bexp.optimize0plus b₁) }) | (bexp { ~b₁ ∧ ~b₂ }) => (bexp { ~(Bexp.optimize0plus b₁) ∧ ~(Bexp.optimize0plus b₂) }) ) def Com.optimize0plus (c : Com) : Com := solution!( match c with | (imp { skip }) => (imp { skip }) | (imp { x := ~a }) => (imp { x := ~(Aexp.optimize0plus a) }) | (imp { c₁ ; c₂ }) => imp { ~(Com.optimize0plus c₁) ; ~(Com.optimize0plus c₂) } | (imp { if (b) {c₁} else {c₂} }) => imp { if (~(Bexp.optimize0plus b)) {~(Com.optimize0plus c₁)} else {~(Com.optimize0plus c₂)} } | (imp { while (b) {c₁} }) => imp { while (~(Bexp.optimize0plus b)) {~(Com.optimize0plus c₁)} } ) example : Com.optimize0plus (imp { while (X ≠ 0) { X := 0 + X - 1 } }) = (imp { while (X ≠ 0) { X := X - 1 } }) := ⊢ imp {while (X ≠ 0) {X := 0 + X - 1}}.optimize0plus = imp {while (X ≠ 0) {X := X - 1}} solution! All goals completed! 🐙

Prove that these three functions are sound, as we did for foldConstants. Make sure you use the congruence lemmas in the proof for Com.optimize0plus - otherwise it will be long!

theorem Aexp.optimize0plus_sound: Aexp.TransSound Aexp.optimize0plus := ⊢ TransSound optimize0plus solution! a:Aexpst:State⊢ eval st a = eval st a.optimize0plus induction a with (All goals completed! 🐙; try st:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ - eval st a₂ = eval st a₁.optimize0plus - eval st a₂.optimize0plus ) st:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ + eval st a₂ = eval st aexp {~a₁ + ~a₂}.optimize0plus cases a₁ with (st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusa₁✝:Aexpa₂✝:Aexpih₁:eval st a₁✝ * eval st a₂✝ = eval st a₁✝.optimize0plus * eval st a₂✝.optimize0plus⊢ eval st a₁✝ * eval st a₂✝ + eval st a₂ = eval st a₁✝.optimize0plus * eval st a₂✝.optimize0plus + eval st a₂.optimize0plus; try All goals completed! 🐙) st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusn:Natih₁:True⊢ n + eval st a₂ = eval st aexp {~(num n) + ~a₂}.optimize0plus st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:True⊢ 0 + eval st a₂ = eval st aexp {0 + ~a₂}.optimize0plusst:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:Truen✝:Nat⊢ n✝ + 1 + eval st a₂ = eval st aexp {~(num (n✝ + 1)) + ~a₂}.optimize0plus st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:True⊢ 0 + eval st a₂ = eval st aexp {0 + ~a₂}.optimize0plusst:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:Truen✝:Nat⊢ n✝ + 1 + eval st a₂ = eval st aexp {~(num (n✝ + 1)) + ~a₂}.optimize0plus st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:Truen✝:Nat⊢ n✝ + 1 + eval st a₂ = n✝ + 1 + eval st a₂.optimize0plus st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:True⊢ 0 + eval st a₂ = eval st a₂.optimize0plusst:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusih₁:Truen✝:Nat⊢ n✝ + 1 + eval st a₂ = n✝ + 1 + eval st a₂.optimize0plus All goals completed! 🐙 st:Statea₂:Aexpih₂:eval st a₂ = eval st a₂.optimize0plusx✝:Identih₁:True⊢ st[x✝] + eval st a₂ = st[x✝] + eval st a₂.optimize0plus All goals completed! 🐙 st:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ * eval st a₂ = eval st a₁.optimize0plus * eval st a₂.optimize0plus st:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ - eval st a₂ = eval st a₁.optimize0plus - eval st a₂.optimize0plus st:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ - eval st a₂ = eval st a₁.optimize0plus - eval st a₂.optimize0plusst:Statea₁:Aexpa₂:Aexpih₁:eval st a₁ = eval st a₁.optimize0plusih₂:eval st a₂ = eval st a₂.optimize0plus⊢ eval st a₁ * eval st a₂ = eval st a₁.optimize0plus * eval st a₂.optimize0plus All goals completed! 🐙 theorem Bexp.optimize0plus_sound: Bexp.TransSound Bexp.optimize0plus := ⊢ TransSound optimize0plus solution! b:Bexpst:State⊢ eval st b = eval st b.optimize0plus induction b with ( st:Statea₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st a₁✝ > Aexp.eval st a₂✝) = decide (Aexp.eval st a₁✝.optimize0plus > Aexp.eval st a₂✝.optimize0plus); try All goals completed! 🐙 ) st:Stateb:Bool⊢ b = eval st (bool b).optimize0plus st:State⊢ false = eval st bexp {false}.optimize0plusst:State⊢ true = eval st bexp {true}.optimize0plus st:State⊢ false = eval st bexp {false}.optimize0plusst:State⊢ true = eval st bexp {true}.optimize0plus All goals completed! 🐙 st:Stateb:Bexpih:eval st b = eval st b.optimize0plus⊢ (!eval st b) = !eval st b.optimize0plus All goals completed! 🐙 st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁ = eval st b₁.optimize0plusih₂:eval st b₂ = eval st b₂.optimize0plus⊢ (eval st b₁ && eval st b₂) = (eval st b₁.optimize0plus && eval st b₂.optimize0plus) All goals completed! 🐙 theorem Com.optimize0plus_sound: Com.TransSound Com.optimize0plus := ⊢ TransSound optimize0plus solution! c:Com⊢ c ≃ c.optimize0plus induction c with ⊢ imp {skip} ≃ imp {skip}.optimize0plus All goals completed! 🐙 x:Identa:Aexp⊢ imp {x := ~a} ≃ imp {x := ~a}.optimize0plus x:Identa:Aexp⊢ a ≃ a.optimize0plus; All goals completed! 🐙 c₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ imp {~c₁; ~c₂} ≃ imp {~c₁; ~c₂}.optimize0plus c₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₁ ≃ c₁.optimize0plusc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₂ ≃ c₂.optimize0plus c₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₁ ≃ c₁.optimize0plusc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₂ ≃ c₂.optimize0plus All goals completed! 🐙 b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ imp {if (~b) {~c₁} else {~c₂}} ≃ imp {if (~b) {~c₁} else {~c₂}}.optimize0plus b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ b ≃ b.optimize0plusb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₁ ≃ c₁.optimize0plusb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₂ ≃ c₂.optimize0plus b:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ b ≃ b.optimize0plusb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₁ ≃ c₁.optimize0plusb:Bexpc₁:Comc₂:Comih₁:c₁ ≃ c₁.optimize0plusih₂:c₂ ≃ c₂.optimize0plus⊢ c₂ ≃ c₂.optimize0plus try All goals completed! 🐙 All goals completed! 🐙 b:Bexpc:Comih:c ≃ c.optimize0plus⊢ imp {while (~b) {~c}} ≃ imp {while (~b) {~c}}.optimize0plus b:Bexpc:Comih:c ≃ c.optimize0plus⊢ b ≃ b.optimize0plusb:Bexpc:Comih:c ≃ c.optimize0plus⊢ c ≃ c.optimize0plus b:Bexpc:Comih:c ≃ c.optimize0plus⊢ b ≃ b.optimize0plusb:Bexpc:Comih:c ≃ c.optimize0plus⊢ c ≃ c.optimize0plus try All goals completed! 🐙 All goals completed! 🐙

Finally, let's define a compound optimizer on commands that first folds constants (using Com.foldConstants) and then eliminates 0 + n terms (usingCom.optimize0plus).

def optimizer (c : Com) := Com.optimize0plus (Com.foldConstants c)

Prove that this optimizer is sound.

theorem optimizer_sound : Com.TransSound optimizer := ⊢ Com.TransSound optimizer c:Com⊢ c ≃ optimizer c c:Com⊢ c ≃ ?c₂c:Com⊢ ?c₂ ≃ optimizer cc:Com⊢ Com c:Com⊢ c ≃ ?c₂ All goals completed! 🐙 c:Com⊢ c.foldConstants ≃ optimizer c All goals completed! 🐙

4.5. Proving Inequivalence🔗

Next, let's look at some programs that are not equivalent.

Suppose that c₁ is a command of the form

  X := a₁; Y := a₂

and c₂ is the command

       X := a₁; Y := a₂'

where a₂' is formed by substituting a₁ for all occurrences of X in a₂.

For example, c₁ and c₂ might be:

       c₁  =  (X := 42 + 53;
               Y := Y + X)
       c₂  =  (X := 42 + 53;
               Y := Y + (42 + 53))

Clearly, this particular c₁ and c₂ are equivalent. Is this true in general?

We will see in a moment that it is not, but it is worthwhile to pause, now, and see if you can find a counter-example on your own.

More formally, here is the function that substitutes an arithmetic expression u for each occurrence of a given variable x in another expression a:

def Aexp.subst (x : String) (u : Aexp) (a : Aexp) : Aexp := match a with | Aexp.num n => Aexp.num n | Aexp.id x' => if x = x' then u else Aexp.id x' | (aexp { ~a₁ + ~a₂ }) => (aexp { ~(Aexp.subst x u a₁) + ~(Aexp.subst x u a₂) }) | (aexp { ~a₁ - ~a₂ }) => (aexp { ~(Aexp.subst x u a₁) - ~(Aexp.subst x u a₂) }) | (aexp { ~a₁ * ~a₂ }) => (aexp { ~(Aexp.subst x u a₁) * ~(Aexp.subst x u a₂) }) example : Aexp.subst X (aexp { 42 + 53 }) (aexp { Y + X }) = (aexp { Y + (42 + 53) }) := ⊢ Aexp.subst X (aexp {42 + 53}) (aexp {Y + X}) = aexp {Y + (42 + 53)} All goals completed! 🐙

And here is the property we are interested in, expressing the claim that commands c₁ and c₂ as described above are always equivalent.

def SubstEquivProperty : Prop := ∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), (imp { x₁ := a₁; x₂ := a₂ }) ≃ (imp { x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) })

Sadly, the property does not always hold.

Here is a counterexample:

  X := X + 1; Y := X

If we perform the substitution, we get

  X := X + 1; Y := X + 1

which clearly isn't equivalent.

theorem subst_inequiv : ¬ SubstEquivProperty := ⊢ ¬SubstEquivProperty ⊢ ¬∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)} contra:∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}⊢ False /- Here is the counterexample: assuming that `SubstEquivProperty` holds allows us to prove that these two programs are equivalent... -/ contra:∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}c₁:Com := imp {X := X + 1; Y := X}⊢ False contra:∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}⊢ False contra:∀ (x₁ x₂ : String) (a₁ a₂ : Aexp), imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂⊢ False /- ... allows us to show that the command `c₂` can terminate in two different final states: st₁ = (Y →ₜ 1 ; X →ₜ 1) st₂ = (Y →ₜ 2 ; X →ₜ 1). -/ c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₁:∅ =[ ~c₁ ]=> st₁⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₁:∅ =[ ~c₁ ]=> st₁h₂:∅ =[ ~c₂ ]=> st₂⊢ False -- Finally, we use the fact that evaluation is deterministic to obtain a contradiction. c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₂:∅ =[ ~c₂ ]=> st₂h₁:∅ =[ ~c₂ ]=> st₁⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₁:∅ =[ ~c₂ ]=> st₁h₂:st₁ = st₂⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₁:∅ =[ ~c₂ ]=> st₁h₂:st₁ = st₂contra:st₁[Y] = st₂[Y]⊢ False c₁:Com := imp {X := X + 1; Y := X}c₂:Com := imp {X := X + 1; Y := X + 1}h:c₁ ≃ c₂st₁:TotalMap Ident Nat := Y →ₜ 1 ; X →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1h₁:∅ =[ ~c₂ ]=> st₁h₂:st₁ = st₂contra:1 = 2⊢ False All goals completed! 🐙
Exercise★★★★(better_subst_equiv) (Optional)

The equivalence we had in mind above was not complete nonsense -- in fact, it was actually almost right. To make it correct, we just need to exclude the case where the variable X occurs in the right-hand side of the first assignment statement.

inductive VarNotUsedInAexp (x : String) : Aexp → Prop where | num {n : Nat} : VarNotUsedInAexp x (Aexp.num n) | id {y : String} (h : x ≠ y) : VarNotUsedInAexp x (Aexp.id y) | plus {a₁ a₂ : Aexp} (h₁ : VarNotUsedInAexp x a₁) (h₂ : VarNotUsedInAexp x a₂) : VarNotUsedInAexp x ((aexp { a₁ + a₂ })) | minus {a₁ a₂ : Aexp} (h₁ : VarNotUsedInAexp x a₁) (h₂ : VarNotUsedInAexp x a₂) : VarNotUsedInAexp x ((aexp { a₁ - a₂ })) | mult {a₁ a₂ : Aexp} (h₁ : VarNotUsedInAexp x a₁) (h₂ : VarNotUsedInAexp x a₂) : VarNotUsedInAexp x ((aexp { a₁ * a₂ })) theorem Aexp.eval_weakening {x : String} {st : State} {a : Aexp} {ni : Nat} (h : VarNotUsedInAexp x a) : a.eval (x →ₜ ni ; st) = a.eval st := x:Stringst:Statea:Aexpni:Nath:VarNotUsedInAexp x a⊢ eval (x →ₜ ni ; st) a = eval st a induction a with x:Stringst:Stateni:Natn:Nath:VarNotUsedInAexp x (num n)⊢ eval (x →ₜ ni ; st) (num n) = eval st (num n) All goals completed! 🐙 x:Stringst:Stateni:Naty:Identh:VarNotUsedInAexp x (aexp {y})⊢ eval (x →ₜ ni ; st) (aexp {y}) = eval st (aexp {y}) x:Stringst:Stateni:Naty:Identh✝:x ≠ y⊢ eval (x →ₜ ni ; st) (aexp {y}) = eval st (aexp {y}); x:Stringst:Stateni:Naty:Identh✝:x ≠ y⊢ (x →ₜ ni ; st)[y] = st[y] x:Stringst:Stateni:Naty:Identh✝:x ≠ y⊢ x ≠ y; All goals completed! 🐙 x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ + ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ + ~a₂}) = eval st (aexp {~a₁ + ~a₂}) x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ - ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ - ~a₂}) = eval st (aexp {~a₁ - ~a₂}) x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ * ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ * ~a₂}) = eval st (aexp {~a₁ * ~a₂}) x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ * ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ * ~a₂}) = eval st (aexp {~a₁ * ~a₂})x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ - ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ - ~a₂}) = eval st (aexp {~a₁ - ~a₂})x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ + ~a₂})⊢ eval (x →ₜ ni ; st) (aexp {~a₁ + ~a₂}) = eval st (aexp {~a₁ + ~a₂}) x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h:VarNotUsedInAexp x (aexp {~a₁ * ~a₂})⊢ eval (x →ₜ ni ; st) a₁ * eval (x →ₜ ni ; st) a₂ = eval st a₁ * eval st a₂; x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h₁✝:VarNotUsedInAexp x a₁h₂✝:VarNotUsedInAexp x a₂⊢ eval (x →ₜ ni ; st) a₁ * eval (x →ₜ ni ; st) a₂ = eval st a₁ * eval st a₂; x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h₁✝:VarNotUsedInAexp x a₁h₂✝:VarNotUsedInAexp x a₂⊢ VarNotUsedInAexp x a₂x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h₁✝:VarNotUsedInAexp x a₁h₂✝:VarNotUsedInAexp x a₂⊢ VarNotUsedInAexp x a₁ x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h₁✝:VarNotUsedInAexp x a₁h₂✝:VarNotUsedInAexp x a₂⊢ VarNotUsedInAexp x a₂x:Stringst:Stateni:Nata₁:Aexpa₂:Aexpih₁:VarNotUsedInAexp x a₁ → eval (x →ₜ ni ; st) a₁ = eval st a₁ih₂:VarNotUsedInAexp x a₂ → eval (x →ₜ ni ; st) a₂ = eval st a₂h₁✝:VarNotUsedInAexp x a₁h₂✝:VarNotUsedInAexp x a₂⊢ VarNotUsedInAexp x a₁ All goals completed! 🐙

Using VarNotUsedInAexp, formalize and prove a correct version of SubstEquivProperty.

theorem aeval_subst {x : String} {st : State} {a₁ a₂ : Aexp} (h : VarNotUsedInAexp x a₁) : a₂.eval (x →ₜ a₁.eval st ; st) = (Aexp.subst x a₁ a₂).eval (x →ₜ a₁.eval st ; st) := x:Stringst:Statea₁:Aexpa₂:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂) induction a₂ generalizing a₁ st with x:Stringn:Natst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.num n) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (Aexp.num n)) All goals completed! 🐙 x:Stringy:Identst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {y})) x:Stringy:Identst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (if x = y then a₁ else aexp {y}) x:Stringy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:x = y⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (if x = y then a₁ else aexp {y})x:Stringy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:¬x = y⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (if x = y then a₁ else aexp {y}) x:Stringy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:x = y⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (if x = y then a₁ else aexp {y}) y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ Aexp.eval (y →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (y →ₜ Aexp.eval st a₁ ; st) (if y = y then a₁ else aexp {y}); y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ Aexp.eval (y →ₜ Aexp.eval st a₁ ; st) (if y = y then a₁ else aexp {y}) = Aexp.eval (y →ₜ Aexp.eval st a₁ ; st) (aexp {y}); y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ Aexp.eval (y →ₜ Aexp.eval st a₁ ; st) (if True then a₁ else aexp {y}) = Aexp.eval st a₁ y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ VarNotUsedInAexp y (if True then a₁ else aexp {y}); All goals completed! 🐙 x:Stringy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:¬x = y⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {y}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (if x = y then a₁ else aexp {y}) All goals completed! 🐙 x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ + ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ + ~a₂})) x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ - ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ - ~a₂})) x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ * ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ * ~a₂})) x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ * ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ * ~a₂}))x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ - ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ - ~a₂}))x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (aexp {~a₁✝ + ~a₂}) = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ (aexp {~a₁✝ + ~a₂})) x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ * Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝) * Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂) x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁x:Stringa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₁✝ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp}, VarNotUsedInAexp x a₁ → Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ All goals completed! 🐙 theorem subst_equiv {x₁ x₂ : String} {a₁ a₂ : Aexp} (h : VarNotUsedInAexp x₁ a₁) : imp { x₁ := a₁; x₂ := a₂ } ≃ imp { x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)} := x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁⊢ imp {x₁ := ~a₁; x₂ := ~a₂} ≃ imp {x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)} x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st') ↔ st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'; x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st') → st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st') → st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st' x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st') → st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st') → st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st' x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Stateheval:st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'⊢ st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st' x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Stateheval:st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st'⊢ st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st' inversion heval with | seq h₁ h₂ => x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~a₂}.EvalR st'✝ st'⊢ imp {x₁ := ~a₁}.EvalR st ?seq.st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~a₂}.EvalR st'✝ st'⊢ imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR ?seq.st' st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~a₂}.EvalR st'✝ st'⊢ State; x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~a₂}.EvalR st'✝ st'⊢ imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ st' x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝n✝:Nath✝:Aexp.eval st'✝ a₂ = n✝⊢ imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ (x₂ →ₜ n✝ ; st'✝) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝n✝:Nath✝:Aexp.eval st'✝ a₂ = n✝⊢ imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ (x₂ →ₜ n✝ ; st'✝) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝⊢ imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ (x₂ →ₜ Aexp.eval st'✝ a₂ ; st'✝); x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝⊢ Aexp.eval st'✝ (Aexp.subst x₁ a₁ a₂) = Aexp.eval st'✝ a₂ x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Staten✝:Nath✝:Aexp.eval st a₁ = n✝⊢ Aexp.eval (x₁ →ₜ n✝ ; st) (Aexp.subst x₁ a₁ a₂) = Aexp.eval (x₁ →ₜ n✝ ; st) a₂ x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Staten✝:Nath✝:Aexp.eval st a₁ = n✝⊢ Aexp.eval (x₁ →ₜ n✝ ; st) (Aexp.subst x₁ a₁ a₂) = Aexp.eval (x₁ →ₜ n✝ ; st) a₂ x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:State⊢ Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x₁ a₁ a₂) = Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) a₂; x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:State⊢ Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x₁ a₁ a₂) All goals completed! 🐙 x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Stateheval:st =[ x₁ := ~a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'⊢ st =[ x₁ := ~a₁; x₂ := ~a₂ ]=> st' inversion heval with | seq h₁ h₂ => x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ st'⊢ imp {x₁ := ~a₁}.EvalR st ?seq.st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ st'⊢ imp {x₂ := ~a₂}.EvalR ?seq.st' st'x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ st'⊢ State; x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝h₂:imp {x₂ := ~(Aexp.subst x₁ a₁ a₂)}.EvalR st'✝ st'⊢ imp {x₂ := ~a₂}.EvalR st'✝ st' x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝n✝:Nath✝:Aexp.eval st'✝ (Aexp.subst x₁ a₁ a₂) = n✝⊢ imp {x₂ := ~a₂}.EvalR st'✝ (x₂ →ₜ n✝ ; st'✝) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝n✝:Nath✝:Aexp.eval st'✝ (Aexp.subst x₁ a₁ a₂) = n✝⊢ imp {x₂ := ~a₂}.EvalR st'✝ (x₂ →ₜ n✝ ; st'✝) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝⊢ imp {x₂ := ~a₂}.EvalR st'✝ (x₂ →ₜ Aexp.eval st'✝ (Aexp.subst x₁ a₁ a₂) ; st'✝); x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest'✝:Stateh₁:imp {x₁ := ~a₁}.EvalR st st'✝⊢ Aexp.eval st'✝ a₂ = Aexp.eval st'✝ (Aexp.subst x₁ a₁ a₂) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Staten✝:Nath✝:Aexp.eval st a₁ = n✝⊢ Aexp.eval (x₁ →ₜ n✝ ; st) a₂ = Aexp.eval (x₁ →ₜ n✝ ; st) (Aexp.subst x₁ a₁ a₂) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Staten✝:Nath✝:Aexp.eval st a₁ = n✝⊢ Aexp.eval (x₁ →ₜ n✝ ; st) a₂ = Aexp.eval (x₁ →ₜ n✝ ; st) (Aexp.subst x₁ a₁ a₂) x₁:Stringx₂:Stringa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:State⊢ Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval (x₁ →ₜ Aexp.eval st a₁ ; st) (Aexp.subst x₁ a₁ a₂) All goals completed! 🐙
Exercise★★★(inequiv_exercise) (Optional)

Prove that an infinite loop is not equivalent to skip.

theorem inequiv_exercise: ¬ (imp { while (true) {skip} } ≃ imp { skip }) := ⊢ ¬imp {while (true) {skip}} ≃ imp {skip} solution! contra:imp {while (true) {skip}} ≃ imp {skip}⊢ False contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ False contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ ∅ =[ while (true) {skip} ]=> ∅ contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ ∅ =[ skip ]=> ∅ All goals completed! 🐙

4.6. Extended Exercise: Nondeterministic Imp🔗

As we have seen (in theorem ceval_deterministic in the Imp chapter), Imp's evaluation relation is deterministic. However, non-determinism is an important part of the definition of many real programming languages. For example, in many imperative languages (such as C and its relatives), the order in which function arguments are evaluated is unspecified: the program fragment

  x = 0;
  f(++x, x)

might call f with arguments (1, 0) or (1, 1), depending how the compiler chooses to order things. This can be a little confusing for programmers, but it gives compiler writers useful freedom.

In this exercise, we will extend Imp with a simple nondeterministic command and study how this change affects program equivalence. The new command has the syntax havoc X, where X is an identifier. The effect of executing havoc X is to assign an arbitrary number to the variable X, nondeterministically. For example, after executing the program:

  havoc Y;
  Z := Y * 2

the value of Y can be any number, while the value of Z is twice that of Y (so Z is always even). Note that we are not saying anything about the probabilities of the outcomes -- just that there are (infinitely) many different outcomes that can possibly happen after executing this nondeterministic code.

In a sense, a variable on which we do havoc roughly corresponds to an uninitialized variable in a low-level language like C. After the havoc, the variable holds a fixed but arbitrary number. Most sources of nondeterminism in language definitions are there precisely because programmers don't care which choice is made (and so it is good to leave it open to the compiler to choose whichever will run faster).

We call this new language Himp ("Imp extended with havoc").

namespace Himp

To formalize Himp, we first add a clause to the definition of commands.

inductive Com : Type where | skip : Com | asgn : String → Aexp → Com | seq : Com → Com → Com | cond : Bexp → Com → Com → Com | whileDo : Bexp → Com → Com | havoc : String → Com -- <--- NEW
Notation encoding: commands, macro rulesnamespace Com /-- Assignment -/ syntax:max "havoc" ppHardSpace ident : imp_com open Lean scoped macro_rules | `(imp { $s }) => do let stx ← match s with | `(imp_com| skip) => ``(Com.skip) | `(imp_com| havoc $x:ident) => ``(Com.havoc $x) | `(imp_com| $x:ident) => ``(($x : Com)) | `(imp_com| $c₁ ; $c₂) => ``(Com.seq (imp {$c₁}) (imp {$c₂})) | `(imp_com| $x:ident := $a) => ``(Com.asgn $x (aexp {$a})) | `(imp_com| if ($b) {$c₁} else {$c₂}) => ``(Com.cond (bexp {$b}) (imp {$c₁}) (imp {$c₂})) | `(imp_com| while ($b) {$c}) => ``(Com.whileDo (bexp {$b}) (imp {$c})) | `(imp_com| ~$c) => `(($c : Com)) | _ => Macro.throwUnsupported return Imp.Elab.withSourceInfoOf s stx end Com open scoped Ambiguous namespace `Com`: it is interpreted as `_root_.Himp.Com` because this `open` occurs inside `namespace Himp`, while `_root_.Com` is silently not opened. Specify the namespace unambiguously, e.g. `_root_.Himp.Com`. The warning can sometimes also be addressed by moving the `open` outside of the surrounding `namespace`. Note: This linter can be disabled with `set_option linter.ambiguousOpen false`Com namespace Delab open Lean PrettyPrinter Imp.Delab @[app_unexpander Com.havoc] def unexpandComHavoc : Unexpander | `($_ $x:ident) => `(imp { havoc $x:ident }) | _ => throw () attribute [app_unexpander Com.skip] unexpandComSkip attribute [app_unexpander Com.asgn] unexpandComAsgn attribute [app_unexpander Com.seq] unexpandComSeq attribute [app_unexpander Com.cond] unexpandComCond attribute [app_unexpander Com.whileDo] unexpandComWhileDo end Delab /-- info: imp {havoc X} : Com -/ #guard_msgs in #check imp { havoc X }
Exercise★★(himp_eval) (Optional, Manually graded)

Now, we must extend the operational semantics. We have provided a template for the Com.EvalR relation below, specifying the big-step semantics. What rule(s) must be added to the definition of Com.EvalR to formalize the behavior of the havoc command?

inductive Com.EvalR : Com → State → State → Prop where | skip {st : State} : EvalR (imp {skip}) st st | asgn {st : State} {a : Aexp} {n : Nat} {x : Ident} (h : a.eval st = n) : EvalR (imp {x := a}) st (x →ₜ n ; st) | seq {c₁ c₂ : Com} {st st' st'' : State} (h₁ : EvalR c₁ st st') (h₂ : EvalR c₂ st' st'') : EvalR (imp {c₁; c₂}) st st'' | ifTrue {st st' : State} {b : Bexp} {c₁ c₂ : Com} (hb : b.eval st = true) (hc : EvalR c₁ st st') : EvalR (imp {if (b) {c₁} else {c₂}}) st st' | ifFalse {st st' : State} {b : Bexp} {c₁ c₂ : Com} (hb : b.eval st = false) (hc : EvalR c₂ st st') : EvalR (imp {if (b) {c₁} else {c₂}}) st st' | whileFalse {b : Bexp} {st : State} {c : Com} (hb : b.eval st = false) : EvalR (imp {while (b) {c}}) st st | whileTrue {st st' st'' : State} {b : Bexp} {c : Com} (hb : b.eval st = true) (hc : EvalR c st st') (hloop : Com.EvalR (imp {while (b) {c}}) st' st'') : EvalR (imp {while (b) {c}}) st st'' | havoc {st : State} {x : String} (n : Nat) : EvalR (imp {havoc x}) st (x →ₜ n ; st)
Notation encoding: commandsopen scoped HasEval instance : HasEval Com State State where Eval := Com.EvalR @[simp] theorem Com.evalR_eq {c : Com} {st st' : State} : EvalR c st st' ↔ st =[ c ]=> st' := c:Comst:Statest':State⊢ c.EvalR st st' ↔ st =[ ~c ]=> st' All goals completed! 🐙

As a sanity check, the following claims should be provable for your definition:

example : ∅ =[ havoc X ]=> (X →ₜ 0) := ⊢ ∅ =[ havoc X ]=> X →ₜ 0 solution! All goals completed! 🐙 example : ∅ =[ skip; havoc Z ]=> (Z →ₜ 42) := ⊢ ∅ =[ skip; havoc Z ]=> Z →ₜ 42 solution! ⊢ imp {skip}.EvalR ∅ ?st'⊢ imp {havoc Z}.EvalR ?st' (Z →ₜ 42)⊢ State; ⊢ imp {havoc Z}.EvalR ∅ (Z →ₜ 42); All goals completed! 🐙

Finally, we repeat the definition of command equivalence from above:

def Com.Equiv (c₁ c₂ : Com) : Prop := ∀ (st st' : State), (st =[ c₁ ]=> st') ↔ (st =[ c₂ ]=> st') instance : Equiv Com where equiv := Com.Equiv @[simp] theorem Com.equiv_notation {c₁ c₂ : Com} : c₁.Equiv c₂ ↔ c₁ ≃ c₂ := c₁:Comc₂:Com⊢ c₁.Equiv c₂ ↔ c₁ ≃ c₂ All goals completed! 🐙 @[simp] theorem Com.equiv_def {c₁ c₂ : Com} : c₁ ≃ c₂ ↔ ∀ {st st' : State}, (st =[ c₁ ]=> st') ↔ (st =[ c₂ ]=> st') := c₁:Comc₂:Com⊢ c₁ ≃ c₂ ↔ ∀ {st st' : State}, (st =[ ~c₁ ]=> st') ↔ st =[ ~c₂ ]=> st' All goals completed! 🐙

Let's apply this definition to prove some nondeterministic programs equivalent / inequivalent.

Exercise★★★(havoc_swap) (Optional, Manually graded)

Are the following two programs equivalent?

def pXY := imp { havoc X ; havoc Y } def pYX := imp { havoc Y; havoc X }

If you think they are equivalent, prove it. If you think they are not, prove that.

Note that this is proving something general, considering arbitrary x and y, not just the (distinct) string constants X and Y; this is why the case distinction is needed.

theorem pXY_approx_pYX {x y : String} {st st' : State} (h : st =[ havoc x; havoc y ]=> st') : st =[ havoc y; havoc x ]=> st' := x:Stringy:Stringst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'⊢ st =[ havoc y; havoc x ]=> st' x:Stringy:Stringst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:x = y⊢ st =[ havoc y; havoc x ]=> st'x:Stringy:Stringst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:¬x = y⊢ st =[ havoc y; havoc x ]=> st' x:Stringy:Stringst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:x = y⊢ st =[ havoc y; havoc x ]=> st' y:Stringst:Statest':Stateh:st =[ havoc y; havoc y ]=> st'⊢ st =[ havoc y; havoc y ]=> st'; All goals completed! 🐙 x:Stringy:Stringst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:¬x = y⊢ st =[ havoc y; havoc x ]=> st' inversion h with | seq h₁ h₂ => x:Stringy:Stringst:Statest':Statehid:¬x = yst'✝:Stateh₁:imp {havoc x}.EvalR st st'✝h₂:imp {havoc y}.EvalR st'✝ st'⊢ st =[ havoc y; havoc x ]=> st' x:Stringy:Stringst:Statest':Statehid:¬x = yn✝:Nath₂:imp {havoc y}.EvalR (x →ₜ n✝ ; st) st'⊢ st =[ havoc y; havoc x ]=> st'; x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ st =[ havoc y; havoc x ]=> y →ₜ n✝ ; x →ₜ n✝¹ ; st x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc y}.EvalR st ?«havoc».st'x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR ?«havoc».st' (y →ₜ n✝ ; x →ₜ n✝¹ ; st)x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ State; x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ Natx:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ ?«havoc».h₁ ; st) (y →ₜ n✝ ; x →ₜ n✝¹ ; st); x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ n✝ ; st) (y →ₜ n✝ ; x →ₜ n✝¹ ; st) x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ n✝ ; st) (x →ₜ n✝¹ ; y →ₜ n✝ ; st)x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ y ≠ x; x:Stringy:Stringst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ y ≠ x; All goals completed! 🐙 theorem pXY_cequiv_pYX : (pXY ≃ pYX) ∨ ¬ (pXY ≃ pYX) := ⊢ pXY ≃ pYX ∨ ¬pXY ≃ pYX /- Hint: You may want to use `update_permute` at some point, in which case you'll probably be left with `X ≠ Y` as a hypothesis. You can use `contradiction to discharge this. -/ solution! ⊢ pXY ≃ pYX; st:Statest':State⊢ (st =[ ~pXY ]=> st') ↔ st =[ ~pYX ]=> st' st:Statest':State⊢ (st =[ ~pXY ]=> st') → st =[ ~pYX ]=> st'st:Statest':State⊢ (st =[ ~pYX ]=> st') → st =[ ~pXY ]=> st' st:Statest':State⊢ (st =[ ~pXY ]=> st') → st =[ ~pYX ]=> st'st:Statest':State⊢ (st =[ ~pYX ]=> st') → st =[ ~pXY ]=> st' All goals completed! 🐙
Exercise★★★★(havoc_copy) (Optional)

Are the following two programs equivalent?

def ptwice := (imp { havoc X; havoc Y }) def pcopy := (imp { havoc X; Y := X })

If you think they are equivalent, then prove it. If you think they are not, then prove that. (Hint: You may find the have tactic useful.)

theorem ptwice_equiv_pcopy : (ptwice ≃ pcopy) ∨ ¬(ptwice ≃ pcopy) := ⊢ ptwice ≃ pcopy ∨ ¬ptwice ≃ pcopy solution! ⊢ ¬ptwice ≃ pcopy; contra:ptwice ≃ pcopy⊢ False contra:ptwice ≃ pcopyh:∅ =[ ~ptwice ]=> Y →ₜ 1 ; X →ₜ 0⊢ False contra:ptwice ≃ pcopyh:∅ =[ ~pcopy ]=> Y →ₜ 1 ; X →ₜ 0⊢ False inversion h with | seq h₁ h₂ => contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)⊢ False; inversion h₂ with | asgn n x h => contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => bif Y == a' then 1 else (X →ₜ 0)[a']) = fun a' => bif Y == a' then Aexp.eval (X →ₜ n✝) (aexp {X}) else (X →ₜ n✝)[a']⊢ False; contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']⊢ False contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']hy:(if Y = Y then 1 else (X →ₜ 0)[Y]) = if Y = Y then n✝ else (X →ₜ n✝)[Y]⊢ False contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']hy:(if Y = Y then 1 else (X →ₜ 0)[Y]) = if Y = Y then n✝ else (X →ₜ n✝)[Y]hx:(if Y = X then 1 else (X →ₜ 0)[X]) = if Y = X then n✝ else (X →ₜ n✝)[X]⊢ False contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']hx:(if Y = X then 1 else (X →ₜ 0)[X]) = if Y = X then n✝ else (X →ₜ n✝)[X]hy:1 = n✝⊢ False contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']hy:1 = n✝hx:(if Y = X then 1 else 0) = n✝⊢ False; contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)x:h₂ ≍ ⋯h:(fun a' => if Y = a' then 1 else (X →ₜ 0)[a']) = fun a' => if Y = a' then n✝ else (X →ₜ n✝)[a']hy:1 = n✝hx:(if Y = X then 1 else 0) = 1⊢ False All goals completed! 🐙

The definition of program equivalence we are using here has some subtle consequences on programs that may loop forever. What Equiv says is that the set of possible terminating outcomes of two equivalent programs is the same. However, in a language with nondeterminism, like Himp, some programs always terminate, some programs always diverge, and some programs can nondeterministically terminate in some runs and diverge in others. The final part of the following exercise illustrates this phenomenon.

Exercise★★★★(p₁_p₂_term) (Advanced)

Consider the following commands:

def p₁ : Com := imp { while (¬ (X = 0)) { havoc Y; X := X + 1 } } def p₂ : Com := imp{ while (¬ (X = 0)) { skip } }

Intuitively, p₁ and p₂ have the same termination behavior: either they loop forever, or they terminate in the same state they started in. We can capture the termination behavior of p₁ and p₂ individually with these lemmas:

theorem p₁_may_diverge (st st' : State) (h : st[X] ≠ 0) : ¬ (st =[ p₁ ]=> st') := st:Statest':Stateh:st[X] ≠ 0⊢ ¬st =[ ~p₁ ]=> st' solution! st:Statest':Stateh:st[X] ≠ 0contra:st =[ ~p₁ ]=> st'⊢ False st:Statest':Stateh✝:st[X] ≠ 0p₁':Comh:p₁ = p₁'contra:st =[ ~p₁' ]=> st'⊢ False induction contra with All goals completed! 🐙 st:Statest':Statep₁':Comst✝:Stateh:st✝[X] ≠ 0h':Bexp.eval st✝ (bexp {¬ (X = 0)}) = false⊢ False All goals completed! 🐙 st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ False st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ st'✝[X] ≠ 0st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ st'✝[X] ≠ 0st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} try All goals completed! 🐙 inversion hc with | seq h₁ h₂ => st:Statest':Statep₁':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → Falsen✝:Nath₂:imp {X := X + 1}.EvalR (Y →ₜ n✝ ; st✝) st'✝⊢ st'✝[X] ≠ 0; st:Statest':Statep₁':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsen✝¹:Natn✝:Nath✝:Aexp.eval (Y →ₜ n✝¹ ; st✝) (aexp {X + 1}) = n✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR (X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝) st''✝ihloop:(X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝)[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ (X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝)[X] ≠ 0 st:Statest':Statep₁':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₁ = imp {havoc Y; X := X + 1} → Falsen✝¹:Natn✝:Nath✝:Aexp.eval (Y →ₜ n✝¹ ; st✝) (aexp {X + 1}) = n✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR (X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝) st''✝ihloop:(X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝)[X] ≠ 0 → p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} → False⊢ n✝ ≠ 0; st:Statest':Statep₁':Comst✝:Statest''✝:Staten✝¹:Natn✝:Nath:¬st✝[X] = 0ihc:¬p₁ = imp {havoc Y; X := X + 1}h✝:(Y →ₜ n✝¹ ; st✝)[X] + 1 = n✝hloop:(X →ₜ n✝ ; Y →ₜ n✝¹ ; st✝) =[ while (¬ (X = 0)) {havoc Y; X := X + 1} ]=> st''✝ihloop:¬n✝ = 0 → ¬p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}⊢ ¬n✝ = 0; All goals completed! 🐙 theorem p₂_may_diverge (st st' : State) (h : st[X] ≠ 0) : ¬ (st =[ p₂ ]=> st') := st:Statest':Stateh:st[X] ≠ 0⊢ ¬st =[ ~p₂ ]=> st' solution! st:Statest':Stateh:st[X] ≠ 0contra:st =[ ~p₂ ]=> st'⊢ False st:Statest':Stateh✝:st[X] ≠ 0p₂':Comh:p₂ = p₂'contra:st =[ ~p₂' ]=> st'⊢ False induction contra with All goals completed! 🐙 st:Statest':Statep₂':Comst✝:Stateh:st✝[X] ≠ 0h':Bexp.eval st✝ (bexp {¬ (X = 0)}) = false⊢ False All goals completed! 🐙 st:Statest':Statep₂':Comst✝:Statest'✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st✝ st'✝ihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st'✝ st''✝ihloop:st'✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ False st:Statest':Statep₂':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st✝ st''✝ihloop:st✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ False; st:Statest':Statep₂':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st✝ st''✝ihloop:st✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ st✝[X] ≠ 0st:Statest':Statep₂':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st✝ st''✝ihloop:st✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ p₂ = imp {while (¬ (X = 0)) {skip}} st:Statest':Statep₂':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st✝ st''✝ihloop:st✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ st✝[X] ≠ 0st:Statest':Statep₂':Comst✝:Statest''✝:Stateh:st✝[X] ≠ 0hb:Bexp.eval st✝ (bexp {¬ (X = 0)}) = trueihc:st✝[X] ≠ 0 → p₂ = imp {skip} → Falsehloop:imp {while (¬ (X = 0)) {skip}}.EvalR st✝ st''✝ihloop:st✝[X] ≠ 0 → p₂ = imp {while (¬ (X = 0)) {skip}} → False⊢ p₂ = imp {while (¬ (X = 0)) {skip}} All goals completed! 🐙
Exercise★★★★(p₁_p₂_equiv) (Advanced)

Use these two lemmas to prove that p₁ and p₂ are actually equivalent.

theorem p₁_p₂_equiv : p₁ ≃ p₂ := ⊢ p₁ ≃ p₂ solution! st:Statest':State⊢ (st =[ ~p₁ ]=> st') ↔ st =[ ~p₂ ]=> st'; st:Statest':State⊢ (st =[ ~p₁ ]=> st') → st =[ ~p₂ ]=> st'st:Statest':State⊢ (st =[ ~p₂ ]=> st') → st =[ ~p₁ ]=> st' st:Statest':State⊢ (st =[ ~p₁ ]=> st') → st =[ ~p₂ ]=> st'st:Statest':State⊢ (st =[ ~p₂ ]=> st') → st =[ ~p₁ ]=> st' st:Statest':Stateh:st =[ ~p₂ ]=> st'⊢ st =[ ~p₁ ]=> st' st:Statest':Stateh:st =[ ~p₁ ]=> st'⊢ st =[ ~p₂ ]=> st' cases h with st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ st =[ ~p₂ ]=> st st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ Bexp.eval st (bexp {¬ (X = 0)}) = false; All goals completed! 🐙 st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st'⊢ st =[ ~p₂ ]=> st' st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st st'✝hloop:False⊢ st =[ ~p₂ ]=> st'st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st'⊢ st'✝[X] ≠ 0; st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {havoc Y; X := X + 1}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st'⊢ st'✝[X] ≠ 0 st:Statest':Statest'✝:Statehc:imp {havoc Y; X := X + 1}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st'hb:¬st[X] = 0⊢ st'✝[X] ≠ 0 inversion hc with | seq h₁ h₂ => st:Statest':Statest'✝:Statehloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR st'✝ st'hb:¬st[X] = 0n✝:Nath₂:imp {X := X + 1}.EvalR (Y →ₜ n✝ ; st) st'✝⊢ st'✝[X] ≠ 0; st:Statest':Statehb:¬st[X] = 0n✝¹:Natn✝:Nath✝:Aexp.eval (Y →ₜ n✝¹ ; st) (aexp {X + 1}) = n✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR (X →ₜ n✝ ; Y →ₜ n✝¹ ; st) st'⊢ (X →ₜ n✝ ; Y →ₜ n✝¹ ; st)[X] ≠ 0 st:Statest':Statehb:¬st[X] = 0n✝¹:Natn✝:Nath✝:Aexp.eval (Y →ₜ n✝¹ ; st) (aexp {X + 1}) = n✝hloop:imp {while (¬ (X = 0)) {havoc Y; X := X + 1}}.EvalR (X →ₜ n✝ ; Y →ₜ n✝¹ ; st) st'⊢ n✝ ≠ 0; st:Statest':Statehb:¬st[X] = 0n✝¹:Natn✝:Nath✝:(Y →ₜ n✝¹ ; st)[X] + 1 = n✝hloop:(X →ₜ n✝ ; Y →ₜ n✝¹ ; st) =[ while (¬ (X = 0)) {havoc Y; X := X + 1} ]=> st'⊢ ¬n✝ = 0; All goals completed! 🐙 st:Statest':Stateh:st =[ ~p₂ ]=> st'⊢ st =[ ~p₁ ]=> st' cases h with st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ st =[ ~p₁ ]=> st st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ Bexp.eval st (bexp {¬ (X = 0)}) = false; All goals completed! 🐙 st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st'✝ st'⊢ st =[ ~p₁ ]=> st' st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st st'✝hloop:False⊢ st =[ ~p₁ ]=> st'st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st'✝ st'⊢ st'✝[X] ≠ 0; st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st'✝ st'⊢ st'✝[X] ≠ 0 st:Statest':Statest'✝:Statehc:imp {skip}.EvalR st st'✝hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st'✝ st'hb:¬st[X] = 0⊢ st'✝[X] ≠ 0 st:Statest':Statehb:¬st[X] = 0hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st st'⊢ st[X] ≠ 0; All goals completed! 🐙
Exercise★★★★(p₃_p₄_inequiv) (Advanced)

Prove that the following programs are not equivalent. (Hint: What should the value of Z be when p₃ terminates? What about p₄?)

def p₃ : Com := imp { Z := 1; while (X ≠ 0) { havoc X; havoc Z } } def p₄ : Com := imp { X := 0; Z := 1 }

First, note that the programs p₃ and p₄ are not equivalent: when p₃ terminates, even though X definitely has value 0, Z might have any natural number as the value.

theorem p₃_p₄_inequiv : ¬ (p₃ ≃ p₄) := ⊢ ¬p₃ ≃ p₄ solution! contra:p₃ ≃ p₄⊢ False contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ False contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1h:st =[ ~p₃ ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ False st:TotalMap Ident Nat := X →ₜ 1h:st =[ ~p₃ ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; stcontra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'⊢ False st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h:st =[ ~p₄ ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ False inversion h with | seq h₁ h₂ => st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'n✝:Nath✝:Aexp.eval st (aexp {0}) = n✝h₂:imp {Z := 1}.EvalR (X →ₜ n✝ ; st) (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st)⊢ False; st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'n✝:Nath✝:0 = n✝h₂:(X →ₜ n✝ ; st) =[ Z := 1 ]=> Z →ₜ n✝ ; X →ₜ n✝ ; Z →ₜ 1 ; st⊢ False; st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0 ; st) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ False st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; st⊢ Falsest:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> X →ₜ 0 ; Z →ₜ 0 ; st⊢ X ≠ Zst:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ Z ≠ X st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; st⊢ Falsest:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> X →ₜ 0 ; Z →ₜ 0 ; st⊢ X ≠ Zst:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ Z ≠ X try All goals completed! 🐙 inversion h₂ with | asgn _ h _ h' => st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; stn✝:Nath:Aexp.eval (X →ₜ 0) (aexp {1}) = n✝h✝:h₂ ≍ ⋯h':(fun a' => bif Z == a' then 0 else (X →ₜ 0 ; st)[a']) = fun a' => bif Z == a' then n✝ else (X →ₜ 0)[a']hz:(bif Z == Z then 0 else (X →ₜ 0 ; st)[Z]) = bif Z == Z then n✝ else (X →ₜ 0)[Z]⊢ False st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; stn✝:Nath:1 = n✝h✝:h₂ ≍ ⋯h':(fun a' => bif Z == a' then 0 else (X →ₜ 0 ; st)[a']) = fun a' => bif Z == a' then n✝ else (X →ₜ 0)[a']hz:(bif Z == Z then 0 else (X →ₜ 0 ; st)[Z]) = bif Z == Z then n✝ else (X →ₜ 0)[Z]⊢ False; st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ ~p₃ ]=> st') ↔ st =[ ~p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> Z →ₜ 0 ; X →ₜ 0 ; sth✝:h₂ ≍ ⋯h':(fun a' => bif Z == a' then 0 else (X →ₜ 0 ; st)[a']) = fun a' => bif Z == a' then 1 else (X →ₜ 0)[a']hz:(bif Z == Z then 0 else (X →ₜ 0 ; st)[Z]) = bif Z == Z then 1 else (X →ₜ 0)[Z]⊢ False All goals completed! 🐙
Exercise★★★★★(p₅_p₆_equiv) (Advanced, Optional)

Prove that the following commands are equivalent. (Hint: As mentioned above, our definition of Equiv for Himp only takes into account the sets of possible terminating configurations: two programs are equivalent if and only if the set of possible terminating states is the same for both programs when given a same starting state st. If p₅ terminates, what should the final state be? Conversely, is it always possible to make p₅ terminate?)

def p₅ : Com := imp { while (X ≠ 1) { havoc X } } def p₆ : Com := imp { X := 1 }

Programs p₅ and p₆ are equivalent although p₅ may diverge, while p₆ always terminates. The definition we took for Equiv cannot distinguish between these two scenarios. It accepts the two programs as equivalent on the basis that: if p₅ terminates it produces the same final state as p₆, and there exists an execution in which p₅ terminates and does exactly as p₆.

There are two directions to the proof:

→: Observe that whenever p₅ terminates, it does so with X set to 1, and no other variable changed. But this is exactly the behavior of p₆. Thus given a pair of states st and st' and that st =[ p₅ ]=> st', the answer to the question "Does st =[ p₆ ]=> st'?" is "Yes".

← (and more controversially): Given that st =[ p₆ ]=> st' for some st and st', can we show that st =[ p₅ ]=> st'? Observe that we can use the hypothesis to conclude that st' = (X →ₜ 1 ; st). Is there some execution of p₅ starting from st which also ends up in st'? Yes!

Hence their equivalence.

theorem p₅_summary (st st' : State) (h : st =[ p₅ ]=> st') : st' = (X →ₜ 1 ; st) := st:Statest':Stateh:st =[ ~p₅ ]=> st'⊢ st' = X →ₜ 1 ; st st:Statest':Statep₅':Comhp:p₅ = p₅'h:st =[ ~p₅' ]=> st'⊢ st' = X →ₜ 1 ; st induction h with All goals completed! 🐙 st:Statest':Statep₅':Comst✝:Stateh':Bexp.eval st✝ (bexp {X ≠ 1}) = false⊢ st✝ = X →ₜ 1 ; st✝ st:Statest':Statep₅':Comst✝:Stateh':st✝[X] = 1⊢ st✝ = X →ₜ 1 ; st✝ All goals completed! 🐙 st:Statest':Statep₅':Comst✝:Statest'✝:Statest''✝:Statehb:Bexp.eval st✝ (bexp {X ≠ 1}) = truehc:imp {havoc X}.EvalR st✝ st'✝ihc:p₅ = imp {havoc X} → st'✝ = X →ₜ 1 ; st✝hloop:imp {while (X ≠ 1) {havoc X}}.EvalR st'✝ st''✝ihloop:p₅ = imp {while (X ≠ 1) {havoc X}} → st''✝ = X →ₜ 1 ; st'✝⊢ st''✝ = X →ₜ 1 ; st✝ st:Statest':Statep₅':Comst✝:Statest'✝:Statest''✝:Statehb:Bexp.eval st✝ (bexp {X ≠ 1}) = truehc:imp {havoc X}.EvalR st✝ st'✝ihc:p₅ = imp {havoc X} → st'✝ = X →ₜ 1 ; st✝hloop:imp {while (X ≠ 1) {havoc X}}.EvalR st'✝ st''✝ihloop:st''✝ = X →ₜ 1 ; st'✝⊢ st''✝ = X →ₜ 1 ; st✝; st:Statest':Statep₅':Comst✝:Statest'✝:Statehb:Bexp.eval st✝ (bexp {X ≠ 1}) = truehc:imp {havoc X}.EvalR st✝ st'✝ihc:p₅ = imp {havoc X} → st'✝ = X →ₜ 1 ; st✝hloop:imp {while (X ≠ 1) {havoc X}}.EvalR st'✝ (X →ₜ 1 ; st'✝)⊢ X →ₜ 1 ; st'✝ = X →ₜ 1 ; st✝ st:Statest':Statep₅':Comst✝:Statehb:Bexp.eval st✝ (bexp {X ≠ 1}) = truen✝:Natihc:p₅ = imp {havoc X} → X →ₜ n✝ ; st✝ = X →ₜ 1 ; st✝hloop:imp {while (X ≠ 1) {havoc X}}.EvalR (X →ₜ n✝ ; st✝) (X →ₜ 1 ; X →ₜ n✝ ; st✝)⊢ X →ₜ 1 ; X →ₜ n✝ ; st✝ = X →ₜ 1 ; st✝ All goals completed! 🐙 theorem p₅_p₆_equiv : p₅ ≃ p₆ := ⊢ p₅ ≃ p₆ solution! st:Statest':State⊢ (st =[ ~p₅ ]=> st') ↔ st =[ ~p₆ ]=> st'; st:Statest':State⊢ (st =[ ~p₅ ]=> st') → st =[ ~p₆ ]=> st'st:Statest':State⊢ (st =[ ~p₆ ]=> st') → st =[ ~p₅ ]=> st' st:Statest':State⊢ (st =[ ~p₅ ]=> st') → st =[ ~p₆ ]=> st'st:Statest':State⊢ (st =[ ~p₆ ]=> st') → st =[ ~p₅ ]=> st' st:Statest':Stateh:st =[ ~p₆ ]=> st'⊢ st =[ ~p₅ ]=> st' st:Statest':Stateh:st =[ ~p₅ ]=> st'⊢ st =[ ~p₆ ]=> st' st:Statest':Stateh:st' = X →ₜ 1 ; st⊢ st =[ ~p₆ ]=> st'; st:State⊢ st =[ ~p₆ ]=> X →ₜ 1 ; st st:State⊢ Aexp.eval st (aexp {1}) = 1; All goals completed! 🐙 st:Statest':Stateh:st =[ ~p₆ ]=> st'⊢ st =[ ~p₅ ]=> st' inversion h with | asgn n h => st:Staten:Nath:1 = n⊢ st =[ ~p₅ ]=> X →ₜ n ; st; st:Staten:Nath:1 = n⊢ st =[ ~p₅ ]=> X →ₜ 1 ; st st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ ~p₅ ]=> X →ₜ 1 ; stst:Staten:Nath:1 = nhx:¬st[X] = 1⊢ st =[ ~p₅ ]=> X →ₜ 1 ; st st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ ~p₅ ]=> X →ₜ 1 ; st st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ ~p₅ ]=> st st:Staten:Nath:1 = nhx:st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = false; All goals completed! 🐙 st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ st =[ ~p₅ ]=> X →ₜ 1 ; st st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = truest:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {havoc X}.EvalR st (X →ₜ 1 ; st)st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {while (X ≠ 1) {havoc X}}.EvalR (X →ₜ 1 ; st) (X →ₜ 1 ; st) st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = true All goals completed! 🐙 st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {havoc X}.EvalR st (X →ₜ 1 ; st) All goals completed! 🐙 st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {while (X ≠ 1) {havoc X}}.EvalR (X →ₜ 1 ; st) (X →ₜ 1 ; st) st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval (X →ₜ 1 ; st) (bexp {X ≠ 1}) = false; All goals completed! 🐙
end Himp

4.7. Additional Exercises🔗

Exercise★★★(swap_noninterfering_assignments) (Optional)

(Hint: You may or may not - depending how you approach it - need to use ext explicitly for this one.)

theorem swap_noninterfering_assignments (l₁ l₂ : String) (a₁ a₂ : Aexp) (hl : l₁ ≠ l₂) (h₁ : VarNotUsedInAexp l₁ a₂) (h₂ : VarNotUsedInAexp l₂ a₁) : imp { l₁ := a₁; l₂ := a₂ } ≃ imp { l₂ := a₂; l₁ := a₁ } := l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁⊢ imp {l₁ := ~a₁; l₂ := ~a₂} ≃ imp {l₂ := ~a₂; l₁ := ~a₁} solution! l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'⊢ imp {l₁ := ~a₁; l₂ := ~a₂} ≃ imp {l₂ := ~a₂; l₁ := ~a₁} l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':State⊢ (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') ↔ st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'; l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':State⊢ (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':State⊢ (st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st') → st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st' l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':State⊢ (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':State⊢ (st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st') → st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st' l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':Stateh:st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'⊢ st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st' l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':Stateh:st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st'⊢ st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st' All goals completed! 🐙 l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':Stateh:st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'⊢ st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st' apply hs (l₁:Stringl₂:Stringa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : String} {a₁ a₂ : Aexp}, l₁ ≠ l₂ → VarNotUsedInAexp l₁ a₂ → VarNotUsedInAexp l₂ a₁ → ∀ {st st' : State}, (st =[ l₁ := ~a₁; l₂ := ~a₂ ]=> st') → st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'st:Statest':Stateh:st =[ l₂ := ~a₂; l₁ := ~a₁ ]=> st'⊢ l₂ ≠ l₁ All goals completed! 🐙) h₂ h₁ h
Exercise★★★★(for_while_equiv) (Optional)

This exercise extends the optional add_for_loop exercise from the Imp chapter, where you were asked to extend the language of commands with C-style for loops. Prove that the command:

for (c₁; b; c₂) {
  c₃
}

is equivalent to:

c₁;
while (b) {
  c₃;
  c₂
}
Exercise★★★★(cApprox) (Advanced, Optional)

In this exercise we define an asymmetric variant of program equivalence we call program approximation. We say that a program c₁ approximates a program c₂ when, for each of the initial states for which c₁ terminates, c₂ also terminates and produces the same final state. Formally, program approximation is defined as follows:

def Approx (c₁ c₂ : Com) : Prop := forall (st st' : State), (st =[ c₁ ]=> st') → (st =[ c₂ ]=> st')

For example, the program c₁

while (X ≠ 1) {
  X := X - 1
}

approximates c₂: X := 1, but c₂ does not approximate c₁ since c₁ does not terminate when X = 0 but c₂ does. If two programs approximate each other in both directions, then they are equivalent.

Find two programs c₃ and c₄ such that neither approximates the other.

def c₃ : Com := solution!(imp { X := 1 }) def c₄ : Com := solution!(imp { X := 2 }) theorem c₃_c₄_different : ¬ (Approx c₃ c₄) ∧ ¬ (Approx c₄ c₃) := ⊢ ¬Approx c₃ c₄ ∧ ¬Approx c₄ c₃ solution! ⊢ ¬Approx c₃ c₄⊢ ¬Approx c₄ c₃ ⊢ ¬Approx c₃ c₄⊢ ¬Approx c₄ c₃ contra:Approx c₄ c₃⊢ False contra:Approx c₃ c₄⊢ False contra:Approx c₃ c₄h:∅ =[ ~c₃ ]=> X →ₜ 1⊢ False contra:Approx c₃ c₄h:∅ =[ ~c₄ ]=> X →ₜ 1⊢ False inversion h with | asgn n h _ h' => contra:Approx c₃ c₄h✝¹:∅ =[ ~c₄ ]=> X →ₜ 1n:Nath:Aexp.eval ∅ (aexp {2}) = nh✝:h✝¹ ≍ ⋯h':(fun a' => bif X == a' then 1 else ∅[a']) = fun a' => bif X == a' then n else ∅[a']hx:(bif X == X then 1 else ∅[X]) = bif X == X then n else ∅[X]⊢ False contra:Approx c₃ c₄h✝¹:∅ =[ ~c₄ ]=> X →ₜ 1n:Nath:Aexp.eval ∅ (aexp {2}) = nh✝:h✝¹ ≍ ⋯h':(fun a' => bif X == a' then 1 else ∅[a']) = fun a' => bif X == a' then n else ∅[a']hx:(bif X == X then 1 else ∅[X]) = bif X == X then Aexp.eval ∅ (aexp {2}) else ∅[X]⊢ False All goals completed! 🐙 contra:Approx c₄ c₃⊢ False contra:Approx c₄ c₃h:∅ =[ ~c₄ ]=> X →ₜ 2⊢ False contra:Approx c₄ c₃h:∅ =[ ~c₃ ]=> X →ₜ 2⊢ False inversion h with | asgn n h _ h' => contra:Approx c₄ c₃h✝¹:∅ =[ ~c₃ ]=> X →ₜ 2n:Nath:Aexp.eval ∅ (aexp {1}) = nh✝:h✝¹ ≍ ⋯h':(fun a' => bif X == a' then 2 else ∅[a']) = fun a' => bif X == a' then n else ∅[a']hx:(bif X == X then 2 else ∅[X]) = bif X == X then n else ∅[X]⊢ False contra:Approx c₄ c₃h✝¹:∅ =[ ~c₃ ]=> X →ₜ 2n:Nath:Aexp.eval ∅ (aexp {1}) = nh✝:h✝¹ ≍ ⋯h':(fun a' => bif X == a' then 2 else ∅[a']) = fun a' => bif X == a' then n else ∅[a']hx:(bif X == X then 2 else ∅[X]) = bif X == X then Aexp.eval ∅ (aexp {1}) else ∅[X]⊢ False All goals completed! 🐙

Find a program cMin that approximates every other program.

def cMin : Com := solution!(imp { while (true) { skip } }) theorem cMin_minimal (c : Com) : Approx cMin c := c:Com⊢ Approx cMin c solution! c:Comst:Statest':Stateh:st =[ ~cMin ]=> st'⊢ st =[ ~c ]=> st' c:Comst:Statest':Stateh:False⊢ st =[ ~c ]=> st' All goals completed! 🐙

Finally, find a non-trivial property which is preserved by program approximation (when going from left to right).

def zprop (c : Com) : Prop := solution!(forall st, exists st', (st =[ c ]=> st'))

Intuitively, zprop holds of programs that terminate on all inputs.

theorem zprop_preserving (c c' : Com) (hc : zprop c) (ha : Approx c c') : zprop c' := c:Comc':Comhc:zprop cha:Approx c c'⊢ zprop c' solution! c:Comc':Comhc:∀ (st : State), ∃ st', st =[ ~c ]=> st'ha:Approx c c'⊢ ∀ (st : State), ∃ st', st =[ ~c' ]=> st' c:Comc':Comhc:∀ (st : State), ∃ st', st =[ ~c ]=> st'ha:Approx c c'st:State⊢ ∃ st', st =[ ~c' ]=> st' c:Comc':Comha:Approx c c'st:Statehc:∃ st', st =[ ~c ]=> st'⊢ ∃ st', st =[ ~c' ]=> st' c:Comc':Comha:Approx c c'st:Statest':Stateh:st =[ ~c ]=> st'⊢ ∃ st', st =[ ~c' ]=> st' c:Comc':Comha:Approx c c'st:Statest':Stateh:st =[ ~c' ]=> st'⊢ ∃ st', st =[ ~c' ]=> st'; All goals completed! 🐙
Source revision: 9cc9a7b, committed 2026-09-28 19:09 UTC