4. Equiv: Program Equivalence
open scoped HasEval MyGetElem Com
Advice for Working on Exercises:
-
Most of the Lean proofs we ask you to do in this chapter are similar to proofs that we've provided. Before starting to work on exercises, take the time to work through our proofs (both informally and in Lean) and make sure you understand them in detail. This will save you a lot of effort.
-
The Lean proofs we're doing now are sufficiently complicated that it is more or less impossible to complete them by random experimentation or following your nose. You need to start with an idea about why the property is true and how the proof is going to go. The best way to do this is to write out at least a sketch of an informal proof on paper - one that intuitively convinces you of the truth of the theorem - before starting to work on the formal one. Alternately, grab a friend and try to convince them that the theorem is true; then try to formalize your explanation.
-
Use automation to save work! The proofs in this chapter can get pretty long if you try to write out all the cases explicitly.
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 + 2is behaviorally equivalent to1 + X + 1 -
X - Xis behaviorally equivalent to0 -
(X - 1) + 1is not behaviorally equivalent toX
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 type class Equiv, so that these relations (and
the one for commands, below) can all be written with the notation
≃.
class Equiv (α : Type) where
equiv : α → α → Prop
infix:70 " ≃ " => Equiv.equiv -- you can type `≃` as \simeq
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! 🐙
Are these two programs equivalent?
X := 1;
Y := 2
and
Y := 2;
X := 1
(A) Yes (B) No (C) Not sure
What about these?
X := 1;
Y := 2
and
X := 2;
Y := 1
(A) Yes (B) No (C) Not sure
What about these?
while (1 ≤ X) {
X := X + 1
}
and
while (2 ≤ X) {
X := X + 1
}
(A) Yes (B) No (C) Not sure
These?
while (true) {
while (false) { X := X + 1 }
}
and
while (false) {
while (true) { X := X + 1 }
}
(A) Yes (B) No (C) Not sure
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'
intro st st'' c:Comst:Statest'':State⊢ (st =[ skip; c ]=> st'') ↔ st =[ c ]=> st''
constructor mp c:Comst:Statest'':State⊢ (st =[ skip; c ]=> st'') → st =[ c ]=> st''mpr c:Comst:Statest'':State⊢ (st =[ c ]=> st'') → st =[ skip; c ]=> st''
· mp c:Comst:Statest'':State⊢ (st =[ skip; c ]=> st'') → st =[ c ]=> st'' intro h mp c:Comst:Statest'':Stateh:st =[ skip; c ]=> st''⊢ st =[ c ]=> st''
inversion h with
| seq st' h1 h2 =>
inversion h1 skip c:Comst:Statest'':Stateh2:c.EvalR st st''⊢ st =[ c ]=> st''
exact h2 All goals completed! 🐙
· mpr c:Comst:Statest'':State⊢ (st =[ c ]=> st'') → st =[ skip; c ]=> st'' intro h mpr c:Comst:Statest'':Stateh:st =[ c ]=> st''⊢ st =[ skip; c ]=> st''
exact EvalR.seq EvalR.skip h All goals completed! 🐙
Prove that adding a skip after a command also results in an
equivalent program.
theorem skip_right {c : Com} : imp { c; skip } ≃ c := by c:Com⊢ imp {c; skip} ≃ c
solution!
rw [equiv_def c:Com⊢ ∀ {st st' : State}, (st =[ c; skip ]=> st') ↔ st =[ c ]=> st'] c:Com⊢ ∀ {st st' : State}, (st =[ c; skip ]=> st') ↔ st =[ c ]=> st'
intro st st'' c:Comst:Statest'':State⊢ (st =[ c; skip ]=> st'') ↔ st =[ c ]=> st''
constructor mp c:Comst:Statest'':State⊢ (st =[ c; skip ]=> st'') → st =[ c ]=> st''mpr c:Comst:Statest'':State⊢ (st =[ c ]=> st'') → st =[ c; skip ]=> st''
· mp c:Comst:Statest'':State⊢ (st =[ c; skip ]=> st'') → st =[ c ]=> st'' intro h mp c:Comst:Statest'':Stateh:st =[ c; skip ]=> st''⊢ st =[ c ]=> st''
inversion h with
| seq st' h1 h2 =>
inversion h2 skip c:Comst:Statest'':Stateh1:c.EvalR st st''⊢ st =[ c ]=> st''
exact h1 All goals completed! 🐙
· mpr c:Comst:Statest'':State⊢ (st =[ c ]=> st'') → st =[ c; skip ]=> st'' intro h mpr c:Comst:Statest'':Stateh:st =[ c ]=> st''⊢ st =[ c; skip ]=> st''
exact EvalR.seq h EvalR.skip All goals completed! 🐙
Similarly, here is a simple equivalence that optimizes if
commands.
theorem if_true_simple {c₁ c₂ : Com} : imp {if (true) {c₁} else {c₂}} ≃ c₁ := by c₁:Comc₂:Com⊢ imp {if (true) {c₁} else {c₂}} ≃ c₁
rw [equiv_def c₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (true) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'] c₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (true) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'
intro st st' c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'
constructor mp c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {c₁} else {c₂} ]=> st') → st =[ c₁ ]=> st'mpr c₁:Comc₂:Comst:Statest':State⊢ (st =[ c₁ ]=> st') → st =[ if (true) {c₁} else {c₂} ]=> st'
· mp c₁:Comc₂:Comst:Statest':State⊢ (st =[ if (true) {c₁} else {c₂} ]=> st') → st =[ c₁ ]=> st' intro h mp c₁:Comc₂:Comst:Statest':Stateh:st =[ if (true) {c₁} else {c₂} ]=> st'⊢ st =[ c₁ ]=> st'
inversion h with
| ifTrue hb hc => exact hc All goals completed! 🐙
| ifFalse hb hc => simp at hb All goals completed! 🐙
· mpr c₁:Comc₂:Comst:Statest':State⊢ (st =[ c₁ ]=> st') → st =[ if (true) {c₁} else {c₂} ]=> st' intro h mpr c₁:Comc₂:Comst:Statest':Stateh:st =[ c₁ ]=> st'⊢ st =[ if (true) {c₁} else {c₂} ]=> st'
apply EvalR.ifTrue _ h c₁:Comc₂:Comst:Statest':Stateh:st =[ c₁ ]=> st'⊢ Bexp.eval st (bexp {true}) = true
simp 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 allstandst', that ifst =[ imp {if (b) {c₁} else {c₂}} ]=> st'thenst =[ c₁ ]=> st'.Proceed by cases on the rules that could possibly have been used to show
st =[ imp {if (b) {c₁} else {c₂}} ]=> st', namelyCom.EvalR.ifTrueandCom.EvalR.ifFalse.-
Suppose the final rule in the derivation of
st =[ imp {if (b) {c₁} else {c₂}} ]=> st'wasCom.EvalR.ifTrue. We then have, by the premises ofCom.EvalR.ifTrue, thatst =[ 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'wasCom.EvalR.ifFalse. We then know thatb.eval st = falseandst =[ c₂ ]=> st'.Recall that
bis equivalent totrue, i.e., for allst,b.eval st = (bexp {true}).eval st. In particular, this means thatb.eval st = true, since(bexp {true}).eval st = true. But this is a contradiction, sinceCom.EvalR.ifFalserequires thatb.eval st = false. Thus, the final rule could not have beenCom.EvalR.ifFalse.
-
-
(
←) We must show, for allstandst', that ifst =[ c₁ ]=> st'thenst =[ imp {if (b) {c₁} else {c₂}} ]=> st'.Since
bis equivalent totrue, we know thatb.eval st = (bexp {true}).eval st = true. Together with the assumption thatst =[ c₁ ]=> st', we can applyCom.EvalR.ifTrueto derivest =[ imp {if (b) {c₁} else {c₂}} ]=> st'.
Here is the formal version of this proof:
theorem if_true {b : Bexp} {c₁ c₂ : Com} (hb : bexp {true} ≃ b ) :
imp {if (b) {c₁} else {c₂}} ≃ c₁ := by b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ b⊢ imp {if (b) {c₁} else {c₂}} ≃ c₁
rw [equiv_def b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ b⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'] b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ b⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'
intro st st' b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₁ ]=> st'
constructor mp b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ c₁ ]=> st'mpr b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':State⊢ (st =[ c₁ ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st'
· mp b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ c₁ ]=> st' intro h mp b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Stateh:st =[ if (b) {c₁} else {c₂} ]=> st'⊢ st =[ c₁ ]=> st'
inversion h ifTrue b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ c₁ ]=> st'ifFalse b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ c₁ ]=> st' <;> ifTrue b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ c₁ ]=> st'ifFalse b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ c₁ ]=> st' simp_all All goals completed! 🐙
· mpr b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':State⊢ (st =[ c₁ ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st' intro h mpr b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Stateh:st =[ c₁ ]=> st'⊢ st =[ if (b) {c₁} else {c₂} ]=> st'
apply EvalR.ifTrue _ h b:Bexpc₁:Comc₂:Comhb:bexp {true} ≃ bst:Statest':Stateh:st =[ c₁ ]=> st'⊢ Bexp.eval st b = true
simp_all All goals completed! 🐙
theorem if_false {b : Bexp} {c₁ c₂ : Com} (hb : bexp {false} ≃ b) :
imp {if (b) {c₁} else {c₂}} ≃ c₂ := by b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ b⊢ imp {if (b) {c₁} else {c₂}} ≃ c₂
solution!
rw [equiv_def b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ b⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₂ ]=> st'] b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ b⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₂ ]=> st'
intro st st' b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ c₂ ]=> st'
constructor mp b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ c₂ ]=> st'mpr b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':State⊢ (st =[ c₂ ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st'
· mp b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ c₂ ]=> st' intro h mp b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Stateh:st =[ if (b) {c₁} else {c₂} ]=> st'⊢ st =[ c₂ ]=> st'
inversion h ifTrue b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ c₂ ]=> st'ifFalse b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ c₂ ]=> st' <;> ifTrue b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Statehb✝:Bexp.eval st b = truehc✝:c₁.EvalR st st'⊢ st =[ c₂ ]=> st'ifFalse b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Statehb✝:Bexp.eval st b = falsehc✝:c₂.EvalR st st'⊢ st =[ c₂ ]=> st' simp_all All goals completed! 🐙
· mpr b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':State⊢ (st =[ c₂ ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st' intro h mpr b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Stateh:st =[ c₂ ]=> st'⊢ st =[ if (b) {c₁} else {c₂} ]=> st'
apply EvalR.ifFalse _ h b:Bexpc₁:Comc₂:Comhb:bexp {false} ≃ bst:Statest':Stateh:st =[ c₂ ]=> st'⊢ Bexp.eval st b = false
simp_all All goals completed! 🐙
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₁}} := by b:Bexpc₁:Comc₂:Com⊢ imp {if (b) {c₁} else {c₂}} ≃ imp {if (¬ b) {c₂} else {c₁}}
solution!
rw [equiv_def b:Bexpc₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ if (¬ b) {c₂} else {c₁} ]=> st'] b:Bexpc₁:Comc₂:Com⊢ ∀ {st st' : State}, (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ if (¬ b) {c₂} else {c₁} ]=> st'
intro st st' b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') ↔ st =[ if (¬ b) {c₂} else {c₁} ]=> st'
constructor mp b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ if (¬ b) {c₂} else {c₁} ]=> st'mpr b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (¬ b) {c₂} else {c₁} ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st'
· mp b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (b) {c₁} else {c₂} ]=> st') → st =[ if (¬ b) {c₂} else {c₁} ]=> st' intro h mp 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 =>
apply EvalR.ifFalse _ hc b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st b = truehc:c₁.EvalR st st'⊢ Bexp.eval st (bexp {¬ b}) = false
simp [hb] All goals completed! 🐙
| ifFalse hb hc =>
apply EvalR.ifTrue _ hc b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st b = falsehc:c₂.EvalR st st'⊢ Bexp.eval st (bexp {¬ b}) = true
simp [hb] All goals completed! 🐙
· mpr b:Bexpc₁:Comc₂:Comst:Statest':State⊢ (st =[ if (¬ b) {c₂} else {c₁} ]=> st') → st =[ if (b) {c₁} else {c₂} ]=> st' intro h mpr 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 =>
apply EvalR.ifFalse _ hc b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st (bexp {¬ b}) = truehc:c₂.EvalR st st'⊢ Bexp.eval st b = false
simp_all All goals completed! 🐙
| ifFalse hb hc =>
apply EvalR.ifTrue _ hc b:Bexpc₁:Comc₂:Comst:Statest':Statehb:Bexp.eval st (bexp {¬ b}) = falsehc:c₁.EvalR st st'⊢ Bexp.eval st b = true
simp_all 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} (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} := by b:Bexpc:Comhb:b ≃ bexp {false}⊢ imp {while (b) {c}} ≃ imp {skip}
rw [equiv_def b:Bexpc:Comhb:b ≃ bexp {false}⊢ ∀ {st st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ skip ]=> st'] b:Bexpc:Comhb:b ≃ bexp {false}⊢ ∀ {st st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ skip ]=> st'
intro st st'' b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (b) {c} ]=> st'') ↔ st =[ skip ]=> st''
constructor mp b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (b) {c} ]=> st'') → st =[ skip ]=> st''mpr b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ skip ]=> st'') → st =[ while (b) {c} ]=> st''
· mp b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ while (b) {c} ]=> st'') → st =[ skip ]=> st'' intro h mp b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':Stateh:st =[ while (b) {c} ]=> st''⊢ st =[ skip ]=> st''
inversion h with
| whileFalse => exact EvalR.skip All goals completed! 🐙
| whileTrue st' hb' hc hloop =>
simp_all All goals completed! 🐙
· mpr b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':State⊢ (st =[ skip ]=> st'') → st =[ while (b) {c} ]=> st'' intro h mpr b:Bexpc:Comhb:b ≃ bexp {false}st:Statest'':Stateh:st =[ skip ]=> st''⊢ st =[ while (b) {c} ]=> st''
inversion h skip b:Bexpc:Comhb:b ≃ bexp {false}st:State⊢ st =[ while (b) {c} ]=> st
apply EvalR.whileFalse skip b:Bexpc:Comhb:b ≃ bexp {false}st:State⊢ Bexp.eval st b = false
simp_all All goals completed! 🐙
Write an informal proof of while_false.
Theorem: For all b and c, if b is equivalent to false,
then while (b) {c} is equivalent to skip.
Proof:
-
(
→) We know thatbis equivalent tofalse. We must show, for allstandst', that ifst =[ while (b) {c} ]=> st'thenst =[ skip ]=> st'.There are only two ways we can have
st =[ while (b) {c} ]=> st': usingCom.EvalR.whileFalseandCom.EvalR.whileTrue.-
Suppose the final rule used to show
st =[ while (b) {c} ]=> st'wasCom.EvalR.whileFalse. We then know thatst = st'; byCom.EvalR.skip, we know thatst =[ skip ]=> st. -
Suppose the final rule used to show
st =[ while (b) {c} ]=> st'wasCom.EvalR.whileTrue. But this rule only applies whenb.eval st = true. However, we are assuming thatbis equivalent tofalse, i.e., for allst,b.eval st = (bexp {false}).eval st = false. So we have a contradiction, and the final rule could not have beenCom.EvalR.whileTrueafter all.
-
-
(
←) We know thatbis equivalent tofalse. We must show, for allstandst', that ifst =[ skip ]=> st'thenst =[ while (b) {c} ]=> st'.Com.EvalR.skipis the only rule that could have provenst =[ skip ]=> st', so we know thatst' = st. We must show thatst =[ while (b) {c} ]=> st.Since
bis equivalent tofalse, we know thatb.eval st = false. ByCom.EvalR.whileFalse, then, we can derive thatst =[ while (b) {c} ]=> st, and we are done.
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 ruleCom.EvalR.whileFalse. Then by assumptionb.eval st = false. But this contradicts the assumption thatbis equivalent totrue. -
Suppose
st =[ while (b) {c} ]=> st'is proved using ruleCom.EvalR.whileTrue. We must have:-
b.eval st = true, and -
there is some
st₀such thatst =[ c ]=> st₀andst₀ =[ while (b) {c} ]=> st'. -
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' := by b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}⊢ ¬st =[ while (b) {c} ]=> st'
workinclass!
intro contra b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}contra:st =[ while (b) {c} ]=> st'⊢ False
generalize heq : (imp {while (b) {c}}) = com at contra b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comheq:imp {while (b) {c}} = comcontra:st =[ com ]=> st'⊢ False
induction contra with
| whileFalse hb' => whileFalse 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
injection heq with hbeq hceq whileFalse b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comb✝:Bexpst✝:Statec✝:Comhb':Bexp.eval st✝ b✝ = falsehbeq:b = b✝hceq:c = c✝⊢ False
subst hbeq whileFalse b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Statec✝:Comhceq:c = c✝hb':Bexp.eval st✝ b = false⊢ False
simp_all All goals completed! 🐙 -- hb and hb' are contradictory
| whileTrue hb' hc' hwhile ih1 ih2 => whileTrue 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
exact ih2 heq All goals completed! 🐙
| skip skip b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Stateheq:imp {while (b) {c}} = imp {skip}⊢ False | asgn asgn 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 | seq seq 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 | ifTrue ifTrue 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 | ifFalse ifFalse 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 => ifFalse 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₂✝}}⊢ FalseifTrue 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₂✝}}⊢ Falseseq 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₂✝}⊢ Falseasgn 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✝}⊢ Falseskip b:Bexpc:Comst:Statest':Statehb:b ≃ bexp {true}com:Comst✝:Stateheq:imp {while (b) {c}} = imp {skip}⊢ False
contradiction All goals completed! 🐙 -- `heq` says that different commands are equal
Explain what the lemma while_true_nonterm means in English.
The lemma while_true_nonterm claims that if a Bexp b is
equivalent to true (i.e., if ∀ st, b.eval st = true),
then it is not possible to construct a derivation
st =[ while (b) {c} ]=> st' for any st, st', or c.
We can understand this lack of a derivation as nontermination: the
reason a derivation can't be constructed is because the
Com.EvalR.whileTrue rule would need to be applied infinitely many times,
but derivations are finite.
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}} := by b:Bexpc:Comhb:b ≃ bexp {true}⊢ imp {while (b) {c}} ≃ imp {while (true) {skip}}
solution!
rw [equiv_def 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 st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ while (true) {skip} ]=> st'
intro st st' b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (b) {c} ]=> st') ↔ st =[ while (true) {skip} ]=> st'
constructor mp b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ while (true) {skip} ]=> st'mpr b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (true) {skip} ]=> st') → st =[ while (b) {c} ]=> st'
· mp b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ while (true) {skip} ]=> st' intro h mp b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (b) {c} ]=> st'⊢ st =[ while (true) {skip} ]=> st'
exfalso mp b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (b) {c} ]=> st'⊢ False
exact while_true_nonterm hb h All goals completed! 🐙
· mpr b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':State⊢ (st =[ while (true) {skip} ]=> st') → st =[ while (b) {c} ]=> st' intro h mpr b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ st =[ while (b) {c} ]=> st'
exfalso mpr b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ False
apply while_true_nonterm _ h b:Bexpc:Comhb:b ≃ bexp {true}st:Statest':Stateh:st =[ while (true) {skip} ]=> st'⊢ bexp {true} ≃ bexp {true}
rw [Bexp.equiv_def 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})
intro 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})
rfl 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}
} := by b:Bexpc:Com⊢ imp {while (b) {c}} ≃ imp {if (b) {c} else {skip}; while (b) {c}}
workinclass!
rw [equiv_def b:Bexpc:Com⊢ ∀ {st st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ if (b) {c} else {skip}; while (b) {c} ]=> st'] b:Bexpc:Com⊢ ∀ {st st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ if (b) {c} else {skip}; while (b) {c} ]=> st'
intro st st' b:Bexpc:Comst:Statest':State⊢ (st =[ while (b) {c} ]=> st') ↔ st =[ if (b) {c} else {skip}; while (b) {c} ]=> st'
constructor mp b:Bexpc:Comst:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ if (b) {c} else {skip}; while (b) {c} ]=> st'mpr b:Bexpc:Comst:Statest':State⊢ (st =[ if (b) {c} else {skip}; while (b) {c} ]=> st') → st =[ while (b) {c} ]=> st'
· mp b:Bexpc:Comst:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ if (b) {c} else {skip}; while (b) {c} ]=> st' intro h mp 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 =>
apply EvalR.seq (st' := st) whileFalse.h₁ b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {if (b) {c} else {skip}}.EvalR st stwhileFalse.h₂ b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {while (b) {c}}.EvalR st st
· whileFalse.h₁ b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {if (b) {c} else {skip}}.EvalR st st exact EvalR.ifFalse hb EvalR.skip All goals completed! 🐙
· whileFalse.h₂ b:Bexpc:Comst:Statehb:Bexp.eval st b = false⊢ imp {while (b) {c}}.EvalR st st exact EvalR.whileFalse hb All goals completed! 🐙
| whileTrue stmid hb hc hloop =>
apply EvalR.seq _ 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
exact EvalR.ifTrue hb hc All goals completed! 🐙
· mpr b:Bexpc:Comst:Statest':State⊢ (st =[ if (b) {c} else {skip}; while (b) {c} ]=> st') → st =[ while (b) {c} ]=> st' intro h mpr 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 =>
exact EvalR.whileTrue hb hc h2 All goals completed! 🐙
| ifFalse hb hc =>
inversion hc skip b:Bexpc:Comst:Statest':Statehb:Bexp.eval st b = falseh2:imp {while (b) {c}}.EvalR st st'⊢ st =[ while (b) {c} ]=> st'
exact h2 All goals completed! 🐙
theorem seq_assoc {c₁ c₂ c₃ : Com} :
imp {~(imp {c₁; c₂}); c₃} ≃ imp {c₁; c₂; c₃} := by c₁:Comc₂:Comc₃:Com⊢ imp {~(imp {c₁; c₂}); c₃} ≃ imp {c₁; c₂; c₃}
solution!
intro st₁ st₂ c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂) ↔ st₁ =[ c₁; c₂; c₃ ]=> st₂
constructor mp c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂) → st₁ =[ c₁; c₂; c₃ ]=> st₂mpr c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ c₁; c₂; c₃ ]=> st₂) → st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂ <;> mp c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂) → st₁ =[ c₁; c₂; c₃ ]=> st₂mpr c₁:Comc₂:Comc₃:Comst₁:Statest₂:State⊢ (st₁ =[ c₁; c₂; c₃ ]=> st₂) → st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂ intro h mpr c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ c₁; c₂; c₃ ]=> st₂⊢ st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂
· mp c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂⊢ st₁ =[ c₁; c₂; c₃ ]=> st₂ inversion h with
| seq h₁ h₂ =>
inversion h₁ seq 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₂
apply EvalR.seq seq.h₁ 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'seq.h₂ 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₂seq.st' c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₂:c₃.EvalR st'✝¹ st₂st'✝:Stateh₁✝:c₁.EvalR st₁ st'✝h₂✝:c₂.EvalR st'✝ st'✝¹⊢ State; assumption seq.h₂ 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₂
apply EvalR.seq 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'✝¹⊢ c₂.EvalR st'✝ ?seq.h₂.st'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'✝¹⊢ c₃.EvalR ?seq.h₂.st' 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'✝¹⊢ State <;> 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'✝¹⊢ c₂.EvalR st'✝ ?seq.h₂.st'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'✝¹⊢ c₃.EvalR ?seq.h₂.st' 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'✝¹⊢ State assumption All goals completed! 🐙
· mpr c₁:Comc₂:Comc₃:Comst₁:Statest₂:Stateh:st₁ =[ c₁; c₂; c₃ ]=> st₂⊢ st₁ =[ ~(imp {c₁; c₂}); c₃ ]=> st₂ inversion h with
| seq h₁ h₂ =>
inversion h₂ seq 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₂
apply EvalR.seq seq.h₁ 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'seq.h₂ 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₂seq.st' c₁:Comc₂:Comc₃:Comst₁:Statest₂:Statest'✝¹:Stateh₁:c₁.EvalR st₁ st'✝¹st'✝:Stateh₁✝:c₂.EvalR st'✝¹ st'✝h₂✝:c₃.EvalR st'✝ st₂⊢ State <;> seq.h₁ 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'seq.h₂ 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₂seq.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 assumption All goals completed! 🐙
apply EvalR.seq 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₂⊢ c₁.EvalR st₁ ?seq.h₁.st'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₂⊢ c₂.EvalR ?seq.h₁.st' 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₂⊢ State <;> 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₂⊢ c₁.EvalR st₁ ?seq.h₁.st'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₂⊢ c₂.EvalR ?seq.h₁.st' 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₂⊢ State assumption 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 := ~(.id x) } ≃ imp { skip } := by x:Ident⊢ imp {x := ~(Aexp.id x)} ≃ imp {skip}
rw [equiv_def x:Ident⊢ ∀ {st st' : State}, (st =[ x := ~(Aexp.id x) ]=> st') ↔ st =[ skip ]=> st'] x:Ident⊢ ∀ {st st' : State}, (st =[ x := ~(Aexp.id x) ]=> st') ↔ st =[ skip ]=> st'
intro st st' x:Identst:Statest':State⊢ (st =[ x := ~(Aexp.id x) ]=> st') ↔ st =[ skip ]=> st'
constructor mp x:Identst:Statest':State⊢ (st =[ x := ~(Aexp.id x) ]=> st') → st =[ skip ]=> st'mpr x:Identst:Statest':State⊢ (st =[ skip ]=> st') → st =[ x := ~(Aexp.id x) ]=> st'
· mp x:Identst:Statest':State⊢ (st =[ x := ~(Aexp.id x) ]=> st') → st =[ skip ]=> st' intro h mp x:Identst:Statest':Stateh:st =[ x := ~(Aexp.id x) ]=> st'⊢ st =[ skip ]=> st'
inversion h with
| asgn n h =>
subst h asgn x:Identst:State⊢ st =[ skip ]=> x →ₜ Aexp.eval st (Aexp.id x) ; st
simp only [Aexp.eval_id, TotalMap.update_same] asgn x:Identst:State⊢ st =[ skip ]=> st
exact Com.EvalR.skip All goals completed! 🐙
· mpr x:Identst:Statest':State⊢ (st =[ skip ]=> st') → st =[ x := ~(Aexp.id x) ]=> st' intro h mpr x:Identst:Statest':Stateh:st =[ skip ]=> st'⊢ st =[ x := ~(Aexp.id x) ]=> st'
inversion h skip x:Identst:State⊢ st =[ x := ~(Aexp.id x) ]=> st
have h' : st =[ x := ~(.id x) ]=> x →ₜ st[x] ; st := by x:Ident⊢ imp {x := ~(Aexp.id x)} ≃ imp {skip}
apply Com.EvalR.asgn x:Identst:State⊢ Aexp.eval st (Aexp.id x) = st[x]
simp skip x:Identst:Stateh':st =[ x := ~(Aexp.id x) ]=> x →ₜ st[x] ; st⊢ st =[ x := ~(Aexp.id x) ]=> st
simp_all [TotalMap.update_same] All goals completed! 🐙
theorem assign_equiv {x : Ident} {a : Aexp} (ha : .id x ≃ a) :
imp { skip } ≃ imp { x := a } := by x:Identa:Aexpha:Aexp.id x ≃ a⊢ imp {skip} ≃ imp {x := a}
solution!
rw [equiv_def x:Identa:Aexpha:Aexp.id x ≃ a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ x := a ]=> st'] x:Identa:Aexpha:Aexp.id x ≃ a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ x := a ]=> st'
rw [Aexp.equiv_def x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ x := a ]=> st'] at ha x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st a⊢ ∀ {st st' : State}, (st =[ skip ]=> st') ↔ st =[ x := a ]=> st'
intro st st' x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') ↔ st =[ x := a ]=> st'
constructor mp x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') → st =[ x := a ]=> st'mpr x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':State⊢ (st =[ x := a ]=> st') → st =[ skip ]=> st'
· mp x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':State⊢ (st =[ skip ]=> st') → st =[ x := a ]=> st' intro h mp x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':Stateh:st =[ skip ]=> st'⊢ st =[ x := a ]=> st'
inversion h skip x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:State⊢ st =[ x := a ]=> st
have h' : st =[ x := a ]=> x →ₜ st[x]; st := by x:Identa:Aexpha:Aexp.id x ≃ a⊢ imp {skip} ≃ imp {x := a}
apply Com.EvalR.asgn x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:State⊢ Aexp.eval st a = st[x]
simp [← ha] skip x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Stateh':st =[ x := a ]=> x →ₜ st[x] ; st⊢ st =[ x := a ]=> st
simp_all [← ha] All goals completed! 🐙
· mpr x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':State⊢ (st =[ x := a ]=> st') → st =[ skip ]=> st' intro h mpr x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:Statest':Stateh:st =[ x := a ]=> st'⊢ st =[ skip ]=> st'
inversion h with
| asgn n h =>
subst h asgn x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:State⊢ st =[ skip ]=> x →ₜ Aexp.eval st a ; st
simp only [← ha, Aexp.eval_id, TotalMap.update_same] asgn x:Identa:Aexpha:∀ (st : State), Aexp.eval st (Aexp.id x) = Aexp.eval st ast:State⊢ st =[ skip ]=> st
exact Com.EvalR.skip All goals completed! 🐙
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 Behavioral 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 - i.e., that they are reflexive,
symmetric, and transitive. These proofs are all easy. We also register
the reflexivity lemmas with the @[refl] tag to the rfl to
prove goals about them and the symmetry lemmas with the @[symm] tag
to swap sides of goals about equivalence using the symm.
end Com
@[refl]
theorem Aexp.equiv_refl (a : Aexp) : a ≃ a := by a:Aexp⊢ a ≃ a simp_all All goals completed! 🐙
@[symm]
theorem Aexp.equiv_symm {a₁ a₂ : Aexp} (h : a₁ ≃ a₂) : a₂ ≃ a₁ := by a₁:Aexpa₂:Aexph:a₁ ≃ a₂⊢ a₂ ≃ a₁ simp_all All goals completed! 🐙
theorem Aexp.equiv_trans {a₁ a₂ a₃ : Aexp} (h₁ : a₁ ≃ a₂) (h₂ : a₂ ≃ a₃) : a₁ ≃ a₃ := by a₁:Aexpa₂:Aexpa₃:Aexph₁:a₁ ≃ a₂h₂:a₂ ≃ a₃⊢ a₁ ≃ a₃ simp_all All goals completed! 🐙
@[refl]
theorem Bexp.equiv_refl {b : Bexp} : b ≃ b := by b:Bexp⊢ b ≃ b simp_all All goals completed! 🐙
@[symm]
theorem Bexp.equiv_symm {b₁ b₂ : Bexp} (h : b₁ ≃ b₂) : b₂ ≃ b₁ := by b₁:Bexpb₂:Bexph:b₁ ≃ b₂⊢ b₂ ≃ b₁ simp_all All goals completed! 🐙
theorem Bexp.equiv_trans {b₁ b₂ b₃ : Bexp} (h₁ : b₁ ≃ b₂) (h₂ : b₂ ≃ b₃) : b₁ ≃ b₃ := by b₁:Bexpb₂:Bexpb₃:Bexph₁:b₁ ≃ b₂h₂:b₂ ≃ b₃⊢ b₁ ≃ b₃ simp_all All goals completed! 🐙
@[refl]
theorem Com.equiv_refl {c : Com} : c ≃ c := by c:Com⊢ c ≃ c simp_all All goals completed! 🐙
@[symm]
theorem Com.equiv_symm {c₁ c₂ : Com} (h : c₁ ≃ c₂) : c₂ ≃ c₁ := by c₁:Comc₂:Comh:c₁ ≃ c₂⊢ c₂ ≃ c₁ simp_all All goals completed! 🐙
theorem Com.equiv_trans {c₁ c₂ c₃ : Com} (h₁ : c₁ ≃ c₂) (h₂ : c₂ ≃ c₃) : c₁ ≃ c₃ := by c₁:Comc₂:Comc₃:Comh₁:c₁ ≃ c₂h₂:c₂ ≃ c₃⊢ c₁ ≃ c₃ simp_all 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 need 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
Com.foldConstants_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'} := by x:Identa:Aexpa':Aexpha:a ≃ a'⊢ imp {x := a} ≃ imp {x := a'}
rw [equiv_def x:Identa:Aexpa':Aexpha:a ≃ a'⊢ ∀ {st st' : State}, (st =[ x := a ]=> st') ↔ st =[ x := a' ]=> st'] x:Identa:Aexpa':Aexpha:a ≃ a'⊢ ∀ {st st' : State}, (st =[ x := a ]=> st') ↔ st =[ x := a' ]=> st'
intro st st' x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a ]=> st') ↔ st =[ x := a' ]=> st'
constructor mp x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a ]=> st') → st =[ x := a' ]=> st'mpr x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a' ]=> st') → st =[ x := a ]=> st' <;> mp x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a ]=> st') → st =[ x := a' ]=> st'mpr x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a' ]=> st') → st =[ x := a ]=> st'
· mpr x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':State⊢ (st =[ x := a' ]=> st') → st =[ x := a ]=> st' intro h mpr x:Identa:Aexpa':Aexpha:a ≃ a'st:Statest':Stateh:st =[ x := a' ]=> st'⊢ st =[ x := a ]=> st'
inversion h with
| asgn n h =>
subst h asgn x:Identa:Aexpa':Aexpha:a ≃ a'st:State⊢ st =[ x := a ]=> x →ₜ Aexp.eval st a' ; st
apply Com.EvalR.asgn asgn x:Identa:Aexpa':Aexpha:a ≃ a'st:State⊢ Aexp.eval st a = Aexp.eval st a'
simp_all 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 thatst =[ while (b) {c} ]=> st'impliesst =[ while (b') {c'} ]=> st', by induction on a derivation ofst =[ while (b) {c} ]=> st'. The only nontrivial cases are when the final rule in the derivation isCom.EvalR.whileFalseorCom.EvalR.whileTrue.-
Com.EvalR.whileFalse: In this case, the form of the rule gives usb.eval st = falseandst = st'. But then, sincebandb'are equivalent, we haveb'.eval st = false, andCom.EvalR.whileFalseapplies, giving usst =[ while (b') {c'} ]=> st', as required. -
Com.EvalR.whileTrue: The form of the rule now gives usb.eval st = true, withst =[ c ]=> st'₀andst'₀ =[ while (b) {c} ]=> st'for some statest'₀, with the induction hypothesisst'₀ =[ while (b') {c'} ]=> st'.Since
candc'are equivalent, we know thatst =[ c' ]=> st'₀. And sincebandb'are equivalent, we haveb'.eval st = true. NowCom.EvalR.whileTrueapplies, giving usst =[ 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'}} := by b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'⊢ imp {while (b) {c}} ≃ imp {while (b') {c'}}
workinclass!
rw [equiv_def 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 st' : State}, (st =[ while (b) {c} ]=> st') ↔ st =[ while (b') {c'} ]=> st'
intro st st' b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (b) {c} ]=> st') ↔ st =[ while (b') {c'} ]=> st'
constructor mp b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ while (b') {c'} ]=> st'mpr b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (b') {c'} ]=> st') → st =[ while (b) {c} ]=> st'
· mp b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (b) {c} ]=> st') → st =[ while (b') {c'} ]=> st' intro h mp b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Stateh:st =[ while (b) {c} ]=> st'⊢ st =[ while (b') {c'} ]=> st'
generalize heq : (imp {while (b) {c}}) = com at h mp 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
| whileFalse hb' => mp.whileFalse 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✝
injection heq with hbeq hceq mp.whileFalse 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✝
subst hbeq mp.whileFalse 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✝
apply Com.EvalR.whileFalse mp.whileFalse 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
rw [← hb mp.whileFalse 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] mp.whileFalse 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
exact hb' All goals completed! 🐙
| whileTrue hb' hc' hwhile _ ih2 => mp.whileTrue 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''✝
injection heq with beq ceq mp.whileTrue 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''✝
subst beq ceq mp.whileTrue 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''✝
rw [hb mp.whileTrue 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''✝] at hb' mp.whileTrue 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''✝
specialize ih2 rfl mp.whileTrue 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''✝
apply Com.EvalR.whileTrue hb' _ ih2 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'✝ rw [equiv_def 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'✝] at hc 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'✝
exact hc.mp hc' All goals completed! 🐙
| skip mp.skip 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✝ | asgn mp.asgn 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✝ | seq mp.seq 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''✝ | ifTrue mp.ifTrue 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'✝ | ifFalse mp.ifFalse 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'✝ => mp.ifFalse 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'✝mp.ifTrue 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'✝mp.seq 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''✝mp.asgn 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✝mp.skip 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✝
contradiction All goals completed! 🐙
· mpr b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':State⊢ (st =[ while (b') {c'} ]=> st') → st =[ while (b) {c} ]=> st' intro h mpr b:Bexpb':Bexpc:Comc':Comhb:b ≃ b'hc:c ≃ c'st:Statest':Stateh:st =[ while (b') {c'} ]=> st'⊢ st =[ while (b) {c} ]=> st'
generalize heq : (imp {while (b') {c'}}) = com at h mpr 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
| whileFalse hb' => mpr.whileFalse 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✝
injection heq with hbeq hceq mpr.whileFalse 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✝
subst hbeq mpr.whileFalse 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✝
apply Com.EvalR.whileFalse mpr.whileFalse 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
rw [hb mpr.whileFalse 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] mpr.whileFalse 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
exact hb' All goals completed! 🐙
| whileTrue hb' hc' hwhile _ ih2 => mpr.whileTrue 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''✝
injection heq with beq ceq mpr.whileTrue 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''✝
subst beq ceq mpr.whileTrue 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''✝
rw [← hb mpr.whileTrue 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''✝] at hb' mpr.whileTrue 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''✝
specialize ih2 rfl mpr.whileTrue 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''✝
apply Com.EvalR.whileTrue hb' _ ih2 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'✝ rw [equiv_def 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'✝] at hc 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'✝
exact hc.mpr hc' All goals completed! 🐙
| skip mpr.skip 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✝ | asgn mpr.asgn 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✝ | seq mpr.seq 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''✝ | ifTrue mpr.ifTrue 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'✝ | ifFalse mpr.ifFalse 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'✝ => mpr.ifFalse 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'✝mpr.ifTrue 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'✝mpr.seq 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''✝mpr.asgn 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✝mpr.skip 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✝
contradiction All goals completed! 🐙
theorem Com.congruence_seq {c₁ c₁' c₂ c₂' : Com} (hc₁ : c₁ ≃ c₁') (hc₂ : c₂ ≃ c₂') :
imp {c₁ ; c₂} ≃ imp {c₁' ; c₂'} := by c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'⊢ imp {c₁; c₂} ≃ imp {c₁'; c₂'}
solution!(
intro st st' c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ c₁; c₂ ]=> st') ↔ st =[ c₁'; c₂' ]=> st'
constructor mp c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ c₁; c₂ ]=> st') → st =[ c₁'; c₂' ]=> st'mpr c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ c₁'; c₂' ]=> st') → st =[ c₁; c₂ ]=> st'
· mp c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ c₁; c₂ ]=> st') → st =[ c₁'; c₂' ]=> st' intro h mp 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₂' =>
rw [equiv_def seq 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'] at hc₁ seq 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'
rw [equiv_def seq 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'] at hc₂ seq 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'
exact Com.EvalR.seq (hc₁.mp hc₁') (hc₂.mp hc₂') All goals completed! 🐙
· mpr c₁:Comc₁':Comc₂:Comc₂':Comhc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'st:Statest':State⊢ (st =[ c₁'; c₂' ]=> st') → st =[ c₁; c₂ ]=> st' intro h mpr 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₂' =>
rw [equiv_def seq 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'] at hc₁ seq 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'
rw [equiv_def seq 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'] at hc₂ seq 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'
exact Com.EvalR.seq (hc₁.mpr hc₁') (hc₂.mpr hc₂') All goals completed! 🐙
)
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₂}} ≃
imp {if (b') {c₁'} else {c₂'}} := by b:Bexpb':Bexpc₁:Comc₁':Comc₂:Comc₂':Comhb:b ≃ b'hc₁:c₁ ≃ c₁'hc₂:c₂ ≃ c₂'⊢ imp {if (b) {c₁} else {c₂}} ≃ imp {if (b') {c₁'} else {c₂'}}
solution!(
intro st 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'
constructor mp 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'mpr 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'
· mp 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' intro h mp 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₁' =>
rw [hb ifTrue 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'] at hb' ifTrue 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'
apply Com.EvalR.ifTrue ifTrue.hb 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' = trueifTrue.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'⊢ c₁'.EvalR st st' <;> ifTrue.hb 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' = trueifTrue.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'⊢ c₁'.EvalR st st' try assumption ifTrue.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'⊢ c₁'.EvalR st st'
· ifTrue.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'⊢ c₁'.EvalR st st' rw [equiv_def ifTrue.hc 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'] at hc₁ ifTrue.hc 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'
exact (hc₁.mp hc₁') All goals completed! 🐙
| ifFalse hb' hc₂' =>
rw [hb ifFalse 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'] at hb' ifFalse 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'
apply Com.EvalR.ifFalse ifFalse.hb 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' = falseifFalse.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'⊢ c₂'.EvalR st st' <;> ifFalse.hb 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' = falseifFalse.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'⊢ c₂'.EvalR st st' try assumption ifFalse.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'⊢ c₂'.EvalR st st'
· ifFalse.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'⊢ c₂'.EvalR st st' rw [equiv_def ifFalse.hc 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'] at hc₂ ifFalse.hc 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'
exact (hc₂.mp hc₂') All goals completed! 🐙
· mpr 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' intro h mpr 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₁' =>
rw [← hb ifTrue 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'] at hb' ifTrue 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'
apply Com.EvalR.ifTrue ifTrue.hb 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 = trueifTrue.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'⊢ c₁.EvalR st st' <;> ifTrue.hb 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 = trueifTrue.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'⊢ c₁.EvalR st st' try assumption ifTrue.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'⊢ c₁.EvalR st st'
· ifTrue.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'⊢ c₁.EvalR st st' rw [equiv_def ifTrue.hc 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'] at hc₁ ifTrue.hc 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'
exact (hc₁.mpr hc₁') All goals completed! 🐙
| ifFalse hb' hc₂' =>
rw [← hb ifFalse 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'] at hb' ifFalse 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'
apply Com.EvalR.ifFalse ifFalse.hb 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 = falseifFalse.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'⊢ c₂.EvalR st st' <;> ifFalse.hb 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 = falseifFalse.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'⊢ c₂.EvalR st st' try assumption ifFalse.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'⊢ c₂.EvalR st st'
· ifFalse.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'⊢ c₂.EvalR st st' rw [equiv_def ifFalse.hc 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'] at hc₂ ifFalse.hc 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'
exact (hc₂.mpr hc₂') All goals completed! 🐙
)
For example, here are two programs and a proof of their equivalence using the 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}} := by ⊢ imp {X := 0; if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {X := 0; if (X = 0) {Y := X - X} else {Y := 42}}
apply Com.congruence_seq hc₁ ⊢ imp {X := 0} ≃ imp {X := 0}hc₂ ⊢ imp {if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {if (X = 0) {Y := X - X} else {Y := 42}}
· hc₁ ⊢ imp {X := 0} ≃ imp {X := 0} apply Com.equiv_refl All goals completed! 🐙
· hc₂ ⊢ imp {if (X = 0) {Y := 0} else {Y := 42}} ≃ imp {if (X = 0) {Y := X - X} else {Y := 42}} apply Com.congruence_if hc₂.hb ⊢ bexp {X = 0} ≃ bexp {X = 0}hc₂.hc₁ ⊢ imp {Y := 0} ≃ imp {Y := X - X}hc₂.hc₂ ⊢ imp {Y := 42} ≃ imp {Y := 42}
· hc₂.hb ⊢ bexp {X = 0} ≃ bexp {X = 0} apply Bexp.equiv_refl All goals completed! 🐙
· hc₂.hc₁ ⊢ imp {Y := 0} ≃ imp {Y := X - X} apply Com.congruence_asgn hc₂.hc₁ ⊢ aexp {0} ≃ aexp {X - X}
simp All goals completed! 🐙
· hc₂.hc₂ ⊢ imp {Y := 42} ≃ imp {Y := 42} apply Com.equiv_refl All goals completed! 🐙
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.
Here's a simple one:
inductive WeirdRel : Com → Com → Prop where
| refl {c : Com} : WeirdRel c c
| symm {c₁ c₂ : Com} : WeirdRel c₁ c₂ → WeirdRel c₂ c₁
| trans {c₁ c₂ c₃ : Com} : WeirdRel c₁ c₂ → WeirdRel c₂ c₃ → WeirdRel c₁ c₃
| weird : WeirdRel (imp { skip }) (imp { X := X })
Some less contrived examples:
-
c₁andc₂are related if either both terminate from an arbitrary starting state or both do not terminate from some starting state. -
c₁andc₂are related ifc₂can be obtained fromc₁by permuting the names of variables (e.g., renamingXtoYandYtoX).
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.
A program transformation is sound if it preserves the behavior of the original program.
def Aexp.TransSound (trans : Aexp → Aexp) : Prop :=
∀ (a : Aexp), (trans a) ≃ a
@[simp]
theorem Aexp.transSound_def {trans : Aexp → Aexp} :
TransSound trans ↔ ∀ (a : Aexp), (trans a) ≃ a := by trans:Aexp → Aexp⊢ TransSound trans ↔ ∀ (a : Aexp), trans a ≃ a rfl All goals completed! 🐙
def Bexp.TransSound (trans : Bexp → Bexp) : Prop :=
∀ (b : Bexp), (trans b) ≃ b
@[simp]
theorem Bexp.transSound_def {trans : Bexp → Bexp} :
TransSound trans ↔ ∀ (b : Bexp), (trans b) ≃ b := by trans:Bexp → Bexp⊢ TransSound trans ↔ ∀ (b : Bexp), trans b ≃ b rfl All goals completed! 🐙
def Com.TransSound (trans : Com → Com) : Prop :=
∀ (c : Com), (trans c) ≃ c
@[simp]
theorem Com.transSound_def {trans : Com → Com} :
TransSound trans ↔ ∀ (c : Com), (trans c) ≃ c := by trans:Com → Com⊢ TransSound trans ↔ ∀ (c : Com), trans c ≃ c rfl 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}) := by 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
| num n₁ => num 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
| num n₂ => num.num 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₂)}
left num.num 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
exists n₁, n₂ All goals completed! 🐙
| _ => num.mult 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₂✝)}num.minus 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₂✝)}num.plus 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₂✝)}num.id a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = num n₁x✝:Identha₂:a₂.foldConstants = id x✝⊢ (∃ n₁_1 n₂, num n₁ = num n₁_1 ∧ id x✝ = num n₂) ∨
aexp {a₁ + a₂}.foldConstants = aexp {~(num n₁) + ~(id x✝)} ∧
aexp {a₁ - a₂}.foldConstants = aexp {~(num n₁) - ~(id x✝)} ∧
aexp {a₁ * a₂}.foldConstants = aexp {~(num n₁) * ~(id x✝)}
simp [foldConstants, ha₁, ha₂] All goals completed! 🐙
| _ => mult 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}minus 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}plus 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}id a₁:Aexpa₂:Aexpx✝:Identha₁:a₁.foldConstants = id x✝⊢ (∃ n₁ n₂, id x✝ = num n₁ ∧ a₂.foldConstants = num n₂) ∨
aexp {a₁ + a₂}.foldConstants = aexp {~(id x✝) + ~a₂.foldConstants} ∧
aexp {a₁ - a₂}.foldConstants = aexp {~(id x✝) - ~a₂.foldConstants} ∧
aexp {a₁ * a₂}.foldConstants = aexp {~(id x✝) * ~a₂.foldConstants}
simp [foldConstants, ha₁] All goals completed! 🐙
Make sure we have explained what named cases hypotheses do (cases ha₁ : a₁.foldConstants in the above proof).
example : (aexp { (1 + 2) * X }).foldConstants = (aexp { 3 * X }) := by ⊢ aexp {(1 + 2) * X}.foldConstants = aexp {3 * X} rfl 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) }) := by ⊢ aexp {X - (0 * 6 + Y)}.foldConstants = aexp {X - (0 + Y)} rfl All goals completed! 🐙
Not only can we lift Aexp.foldConstants to Bexp in the Bexp.eq,
Bexp.neq, Bexp.le, and Bexp.gt 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}) := by 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
| num n₁ => num 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
| num n₂ => num.num 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₂)}
left num.num 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
exists n₁, n₂ All goals completed! 🐙
| _ => num.mult 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₂✝}num.minus 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₂✝}num.plus 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₂✝}num.id a₁:Aexpa₂:Aexpn₁:Natha₁:a₁.foldConstants = Aexp.num n₁x✝:Identha₂:a₂.foldConstants = Aexp.id x✝⊢ (∃ n₁_1 n₂, Aexp.num n₁ = Aexp.num n₁_1 ∧ Aexp.id x✝ = Aexp.num n₂) ∨
bexp {a₁ = a₂}.foldConstants = bexp {~(Aexp.num n₁) = ~(Aexp.id x✝)} ∧
bexp {a₁ ≠ a₂}.foldConstants = bexp {~(Aexp.num n₁) ≠ ~(Aexp.id x✝)} ∧
bexp {a₁ ≤ a₂}.foldConstants = bexp {~(Aexp.num n₁) ≤ ~(Aexp.id x✝)} ∧
bexp {a₁ > a₂}.foldConstants = bexp {~(Aexp.num n₁) > ~(Aexp.id x✝)}
simp [foldConstants, ha₁, ha₂] All goals completed! 🐙
| _ => mult 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}minus 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}plus 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}id a₁:Aexpa₂:Aexpx✝:Identha₁:a₁.foldConstants = Aexp.id x✝⊢ (∃ n₁ n₂, Aexp.id x✝ = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂) ∨
bexp {a₁ = a₂}.foldConstants = bexp {~(Aexp.id x✝) = ~a₂.foldConstants} ∧
bexp {a₁ ≠ a₂}.foldConstants = bexp {~(Aexp.id x✝) ≠ ~a₂.foldConstants} ∧
bexp {a₁ ≤ a₂}.foldConstants = bexp {~(Aexp.id x✝) ≤ ~a₂.foldConstants} ∧
bexp {a₁ > a₂}.foldConstants = bexp {~(Aexp.id x✝) > ~a₂.foldConstants} simp [foldConstants, ha₁] All goals completed! 🐙
theorem Bexp.foldConstants_unary (b : Bexp) :
(b.foldConstants = (bexp { true }) ∨ b.foldConstants = (bexp { false })) ∨
(bexp { ¬b }).foldConstants = (bexp { ¬~(b.foldConstants)}) := by b:Bexp⊢ (b.foldConstants = bexp {true} ∨ b.foldConstants = bexp {false}) ∨ bexp {¬ b}.foldConstants = bexp {¬ ~b.foldConstants}
cases hb : b.foldConstants with
| bool b' => bool b:Bexpb':Boolhb:b.foldConstants = bool b'⊢ (bool b' = bexp {true} ∨ bool b' = bexp {false}) ∨ bexp {¬ b}.foldConstants = bexp {¬ ~(bool b')}
simp_all All goals completed! 🐙
| _ => and 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₂✝)}not b:Bexpb✝:Bexphb:b.foldConstants = bexp {¬ b✝}⊢ (bexp {¬ b✝} = bexp {true} ∨ bexp {¬ b✝} = bexp {false}) ∨ bexp {¬ b}.foldConstants = bexp {¬ ¬ b✝}gt 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₂✝)}le 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₂✝)}neq 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₂✝)}eq 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₂✝)}
simp [foldConstants, hb] 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}) := by 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
| bool b₁' => bool 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
| bool b₂' => bool.bool 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₂')} simp_all All goals completed! 🐙
| _ => bool.and 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₂✝}bool.not 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✝}bool.gt 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₂✝}bool.le 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₂✝}bool.neq 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₂✝}bool.eq 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₂✝} simp [foldConstants, hb₁, hb₂] All goals completed! 🐙
| _ => and 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}not 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}gt 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}le 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}neq 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}eq 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} simp [foldConstants, hb₁] All goals completed! 🐙
example : (bexp { true ∧ ¬( false ∧ true) }).foldConstants = (bexp { true }) := by ⊢ bexp {true ∧ ¬ (false ∧ true)}.foldConstants = bexp {true}
rfl All goals completed! 🐙
example : (bexp { (X = Y) ∧ ( 0 = (2 - (1 + 1))) }).foldConstants = (bexp { (X = Y) ∧ true }) := by ⊢ bexp {X = Y ∧ 0 = 2 - (1 + 1)}.foldConstants = bexp {X = Y ∧ true}
rfl 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}
}) := by ⊢ 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}} rfl 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 := by ⊢ TransSound foldConstants
intro a st a:Aexpst:State⊢ eval st a.foldConstants = eval st a
induction a with
| num n num st:Staten:Nat⊢ eval st (num n).foldConstants = eval st (num n) | id x id st:Statex:Ident⊢ eval st (id x).foldConstants = eval st (id x) => id st:Statex:Ident⊢ eval st (id x).foldConstants = eval st (id x)num st:Staten:Nat⊢ eval st (num n).foldConstants = eval st (num n) rfl All goals completed! 🐙
| _ a₁ a₂ _ _ => mult st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂⊢ eval st aexp {a₁ * a₂}.foldConstants = eval st (aexp {a₁ * a₂})minus st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂⊢ eval st aexp {a₁ - a₂}.foldConstants = eval st (aexp {a₁ - a₂})plus st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂⊢ eval st aexp {a₁ + a₂}.foldConstants = eval st (aexp {a₁ + a₂})
-- `plus`, `minus`, and `mult` follow from the IH and the observation that
-- `(aexp {a₁ + a₂}).eval st = a₁.eval st + a₂.eval st
-- = (Aexp.num (a₁.eval st + a₂.eval st)).eval st`
-- (and similarly for `minus`/`-` and `mult`/`*`).
cases Aexp.foldConstants_cases a₁ a₂ with
| inl h => mult.inl st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st aexp {a₁ * a₂}.foldConstants = eval st (aexp {a₁ * a₂})minus.inl st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st aexp {a₁ - a₂}.foldConstants = eval st (aexp {a₁ - a₂})plus.inl st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h:∃ n₁ n₂, a₁.foldConstants = num n₁ ∧ a₂.foldConstants = num n₂⊢ eval st aexp {a₁ + a₂}.foldConstants = eval st (aexp {a₁ + a₂})
obtain ⟨n₁, n₂, h₁, h₂⟩ := h mult.inl st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂n₁:Natn₂:Nath₁:a₁.foldConstants = num n₁h₂:a₂.foldConstants = num n₂⊢ eval st aexp {a₁ * a₂}.foldConstants = eval st (aexp {a₁ * a₂})
simp_all [foldConstants] All goals completed! 🐙
| inr h => mult.inr st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h: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₂}.foldConstants = eval st (aexp {a₁ * a₂})minus.inr st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h: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₂}.foldConstants = eval st (aexp {a₁ - a₂})plus.inr st:Statea₁:Aexpa₂:Aexpa₁_ih✝:eval st a₁.foldConstants = eval st a₁a₂_ih✝:eval st a₂.foldConstants = eval st a₂h: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₂}.foldConstants = eval st (aexp {a₁ + a₂})
simp_all All goals completed! 🐙
An equivalent version using the fun_induction tactic would look simpler:
theorem Aexp.foldConstants_sound' : TransSound Aexp.foldConstants := by ⊢ TransSound foldConstants
intro a st a:Aexpst:State⊢ eval st a.foldConstants = eval st a
fun_induction Aexp.foldConstants case1 st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)case2 st:Statex✝:Ident⊢ eval st (id x✝) = eval st (id x✝)case3 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ + n₂✝)) = eval st (aexp {a₁✝ + a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants + ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ + a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ - n₂✝)) = eval st (aexp {a₁✝ - a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants - ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ - a₂✝})case7 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ * n₂✝)) = eval st (aexp {a₁✝ * a₂✝})case8 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants * ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ * a₂✝}) <;> case1 st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)case2 st:Statex✝:Ident⊢ eval st (id x✝) = eval st (id x✝)case3 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ + n₂✝)) = eval st (aexp {a₁✝ + a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants + ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ + a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ - n₂✝)) = eval st (aexp {a₁✝ - a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants - ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ - a₂✝})case7 st:Statea₁✝:Aexpa₂✝:Aexpn₁✝:Natn₂✝:Natx✝¹:a₂✝.foldConstants = num n₂✝x✝:a₁✝.foldConstants = num n₁✝ih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (num (n₁✝ * n₂✝)) = eval st (aexp {a₁✝ * a₂✝})case8 st:Statea₁✝:Aexpa₂✝:Aexpx✝:∀ (n₁ n₂ : Nat), a₁✝.foldConstants = num n₁ → a₂✝.foldConstants = num n₂ → Falseih2✝:eval st a₁✝.foldConstants = eval st a₁✝ih1✝:eval st a₂✝.foldConstants = eval st a₂✝⊢ eval st (aexp {~a₁✝.foldConstants * ~a₂✝.foldConstants}) = eval st (aexp {a₁✝ * a₂✝}) simp_all All goals completed! 🐙
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 and gt cases of the formal proof (without looking
at the eq case, if possible).
Theorem: The constant folding function for booleans,
Bexp.foldConstants, is sound.
Proof: We must show that b is equivalent to b.foldConstants,
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 { a₁ = a₂ }).foldConstants = 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₂.eval 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₁.eval st = (a₁.foldConstants).eval st
a₂.eval st = (a₂.foldConstants).eval st
completing the case.
theorem Bexp.foldConstants_sound : Bexp.TransSound Bexp.foldConstants := by ⊢ TransSound foldConstants
intro b st b:Bexpst:State⊢ eval st b.foldConstants = eval st b
induction b with
| bool b => bool st:Stateb:Bool⊢ eval st (bool b).foldConstants = eval st (bool b) cases b bool.false st:State⊢ eval st bexp {false}.foldConstants = eval st (bexp {false})bool.true st:State⊢ eval st bexp {true}.foldConstants = eval st (bexp {true}) <;> bool.false st:State⊢ eval st bexp {false}.foldConstants = eval st (bexp {false})bool.true st:State⊢ eval st bexp {true}.foldConstants = eval st (bexp {true}) rfl All goals completed! 🐙
| eq a₁ a₂ => eq st:Statea₁:Aexpa₂:Aexp⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
have h₁ : Aexp.eval st a₁.foldConstants = Aexp.eval st a₁ :=
Aexp.foldConstants_sound a₁ st eq st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
have h₂ : Aexp.eval st a₂.foldConstants = Aexp.eval st a₂ :=
Aexp.foldConstants_sound a₂ st eq st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
cases Bexp.foldConstants_comp a₁ a₂ with
| inl h => eq.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h:∃ n₁ n₂, a₁.foldConstants = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
-- The only interesting case: both `a₁` and `a₂` fold to constants
obtain ⟨n₁, n₂, hn₁, hn₂⟩ := h eq.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
by_cases n₁ = n₂ pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ = n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ = n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂}) <;> pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ = n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ = n₂⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂}) simp_all [foldConstants] All goals completed! 🐙
| inr h => eq.inr st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h: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}⊢ eval st bexp {a₁ = a₂}.foldConstants = eval st (bexp {a₁ = a₂})
simp_all All goals completed! 🐙
| neq a₁ a₂ => neq st:Statea₁:Aexpa₂:Aexp⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
have h₁ : Aexp.eval st a₁.foldConstants = Aexp.eval st a₁ :=
Aexp.foldConstants_sound a₁ st neq st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
have h₂ : Aexp.eval st a₂.foldConstants = Aexp.eval st a₂ :=
Aexp.foldConstants_sound a₂ st neq st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
cases Bexp.foldConstants_comp a₁ a₂ with
| inl h => neq.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h:∃ n₁ n₂, a₁.foldConstants = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
obtain ⟨n₁, n₂, hn₁, hn₂⟩ := h neq.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
by_cases n₁ = n₂ pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ = n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ = n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂}) <;> pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ = n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ = n₂⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂}) simp_all [foldConstants] All goals completed! 🐙
| inr h => neq.inr st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h: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}⊢ eval st bexp {a₁ ≠ a₂}.foldConstants = eval st (bexp {a₁ ≠ a₂})
simp_all All goals completed! 🐙
| le a₁ a₂ => le st:Statea₁:Aexpa₂:Aexp⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
solution!
have h₁ : Aexp.eval st a₁.foldConstants = Aexp.eval st a₁ :=
Aexp.foldConstants_sound a₁ st le st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
have h₂ : Aexp.eval st a₂.foldConstants = Aexp.eval st a₂ :=
Aexp.foldConstants_sound a₂ st le st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
cases Bexp.foldConstants_comp a₁ a₂ with
| inl h => le.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h:∃ n₁ n₂, a₁.foldConstants = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
obtain ⟨n₁, n₂, hn₁, hn₂⟩ := h le.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
by_cases n₁ ≤ n₂ pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ ≤ n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ ≤ n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂}) <;> pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ ≤ n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ ≤ n₂⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂}) simp_all [foldConstants, Nat.not_le_of_gt] All goals completed! 🐙
| inr h => le.inr st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h: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}⊢ eval st bexp {a₁ ≤ a₂}.foldConstants = eval st (bexp {a₁ ≤ a₂})
simp_all All goals completed! 🐙
| gt a₁ a₂ => gt st:Statea₁:Aexpa₂:Aexp⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
solution!
have h₁ : Aexp.eval st a₁.foldConstants = Aexp.eval st a₁ :=
Aexp.foldConstants_sound a₁ st gt st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
have h₂ : Aexp.eval st a₂.foldConstants = Aexp.eval st a₂ :=
Aexp.foldConstants_sound a₂ st gt st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
cases Bexp.foldConstants_comp a₁ a₂ with
| inl h => gt.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h:∃ n₁ n₂, a₁.foldConstants = Aexp.num n₁ ∧ a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
obtain ⟨n₁, n₂, hn₁, hn₂⟩ := h gt.inl st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
by_cases n₁ > n₂ pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ > n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ > n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂}) <;> pos st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:n₁ > n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})neg st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂n₁:Natn₂:Nathn₁:a₁.foldConstants = Aexp.num n₁hn₂:a₂.foldConstants = Aexp.num n₂h✝:¬n₁ > n₂⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂}) simp_all [foldConstants, Nat.not_lt_of_le] All goals completed! 🐙
| inr h => gt.inr st:Statea₁:Aexpa₂:Aexph₁:Aexp.eval st a₁.foldConstants = Aexp.eval st a₁h₂:Aexp.eval st a₂.foldConstants = Aexp.eval st a₂h: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}⊢ eval st bexp {a₁ > a₂}.foldConstants = eval st (bexp {a₁ > a₂})
simp_all All goals completed! 🐙
| not b ih => not st:Stateb:Bexpih:eval st b.foldConstants = eval st b⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b})
cases Bexp.foldConstants_unary b with
| inl h => not.inl st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:b.foldConstants = bexp {true} ∨ b.foldConstants = bexp {false}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b})
rcases h with h | h not.inl.inl st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:b.foldConstants = bexp {true}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b})not.inl.inr st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:b.foldConstants = bexp {false}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b}) <;> not.inl.inl st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:b.foldConstants = bexp {true}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b})not.inl.inr st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:b.foldConstants = bexp {false}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b}) simp_all [foldConstants] All goals completed! 🐙
| inr h => not.inr st:Stateb:Bexpih:eval st b.foldConstants = eval st bh:bexp {¬ b}.foldConstants = bexp {¬ ~b.foldConstants}⊢ eval st bexp {¬ b}.foldConstants = eval st (bexp {¬ b})
simp_all All goals completed! 🐙
| and b₁ b₂ ih₁ ih₂ => and st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})
cases Bexp.foldConstants_binary b₁ b₂ with
| inl h => and.inl st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h:(b₁.foldConstants = bexp {true} ∨ b₁.foldConstants = bexp {false}) ∧
(b₂.foldConstants = bexp {true} ∨ b₂.foldConstants = bexp {false})⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})
obtain ⟨h₁ | h₁, h₂ | h₂⟩ := h and.inl.inl.inl st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {true}h₂:b₂.foldConstants = bexp {true}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inl.inr st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {true}h₂:b₂.foldConstants = bexp {false}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inr.inl st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {false}h₂:b₂.foldConstants = bexp {true}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inr.inr st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {false}h₂:b₂.foldConstants = bexp {false}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂}) <;> and.inl.inl.inl st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {true}h₂:b₂.foldConstants = bexp {true}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inl.inr st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {true}h₂:b₂.foldConstants = bexp {false}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inr.inl st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {false}h₂:b₂.foldConstants = bexp {true}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})and.inl.inr.inr st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h₁:b₁.foldConstants = bexp {false}h₂:b₂.foldConstants = bexp {false}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂}) simp_all [foldConstants] All goals completed! 🐙
| inr h => and.inr st:Stateb₁:Bexpb₂:Bexpih₁:eval st b₁.foldConstants = eval st b₁ih₂:eval st b₂.foldConstants = eval st b₂h:bexp {b₁ ∧ b₂}.foldConstants = bexp {~b₁.foldConstants ∧ ~b₂.foldConstants}⊢ eval st bexp {b₁ ∧ b₂}.foldConstants = eval st (bexp {b₁ ∧ b₂})
simp_all All goals completed! 🐙
Complete the while case of the following proof.
theorem Com.foldConstants_sound : Com.TransSound Com.foldConstants := by ⊢ TransSound foldConstants
intro c c:Com⊢ c.foldConstants ≃ c
induction c with
| skip => skip ⊢ imp {skip}.foldConstants ≃ imp {skip}
simp [Com.foldConstants] All goals completed! 🐙
| asgn x a => asgn x:Identa:Aexp⊢ imp {x := a}.foldConstants ≃ imp {x := a}
apply Com.congruence_asgn asgn x:Identa:Aexp⊢ a.foldConstants ≃ a
apply Aexp.foldConstants_sound All goals completed! 🐙
| seq c₁ c₂ ih₁ ih₂ => seq c₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ imp {c₁; c₂}.foldConstants ≃ imp {c₁; c₂}
apply Com.congruence_seq seq.hc₁ c₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ c₁.foldConstants ≃ c₁seq.hc₂ c₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ c₂.foldConstants ≃ c₂ <;> seq.hc₁ c₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ c₁.foldConstants ≃ c₁seq.hc₂ c₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ c₂.foldConstants ≃ c₂ assumption All goals completed! 🐙
| cond b c₁ c₂ ih₁ ih₂ => cond b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ imp {if (b) {c₁} else {c₂}}.foldConstants ≃ imp {if (b) {c₁} else {c₂}}
simp only [Com.foldConstants] cond b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂⊢ (match b.foldConstants with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}
have hb : b.foldConstants ≃ b := by ⊢ TransSound foldConstants
apply Bexp.foldConstants_sound cond b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ b⊢ (match b.foldConstants with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}
-- If the optimization doesn't eliminate the `if`, then the
-- result is easy to prove from the `ih` and
-- `Bexp.foldConstants_sound`
cases heq : b.foldConstants cond.bool b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.eq b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ = a₂✝}⊢ (match bexp {a₁✝ = a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.neq b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≠ a₂✝}⊢ (match bexp {a₁✝ ≠ a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.le b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≤ a₂✝}⊢ (match bexp {a₁✝ ≤ a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.gt b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ > a₂✝}⊢ (match bexp {a₁✝ > a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.not b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb✝:Bexpheq:b.foldConstants = bexp {¬ b✝}⊢ (match bexp {¬ b✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.and b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ (match bexp {b₁✝ ∧ b₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}} <;> cond.bool b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.eq b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ = a₂✝}⊢ (match bexp {a₁✝ = a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.neq b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≠ a₂✝}⊢ (match bexp {a₁✝ ≠ a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.le b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≤ a₂✝}⊢ (match bexp {a₁✝ ≤ a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.gt b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ > a₂✝}⊢ (match bexp {a₁✝ > a₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.not b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb✝:Bexpheq:b.foldConstants = bexp {¬ b✝}⊢ (match bexp {¬ b✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}cond.and b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ (match bexp {b₁✝ ∧ b₂✝} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}} try (apply Com.congruence_if cond.and.hb b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ bexp {b₁✝ ∧ b₂✝} ≃ bcond.and.hc₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c₁.foldConstants ≃ c₁cond.and.hc₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c₂.foldConstants ≃ c₂ <;> cond.and.hb b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ bexp {b₁✝ ∧ b₂✝} ≃ bcond.and.hc₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c₁.foldConstants ≃ c₁cond.and.hc₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c₂.foldConstants ≃ c₂ simp_all All goals completed! 🐙)
· cond.bool b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}} case cond.bool b => b✝:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bb:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}
cases b with
| false => false b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ (match bexp {false} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}
apply Com.equiv_trans false.h₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ (match bexp {false} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
?false.c₂false.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ ?false.c₂ ≃ imp {if (b) {c₁} else {c₂}}false.c₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ Com <;> false.h₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ (match bexp {false} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
?false.c₂false.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ ?false.c₂ ≃ imp {if (b) {c₁} else {c₂}}false.c₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ Com try assumption false.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ c₂ ≃ imp {if (b) {c₁} else {c₂}}
symm false.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ imp {if (b) {c₁} else {c₂}} ≃ c₂; apply Com.if_false false.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ bexp {false} ≃ b; simp_all All goals completed! 🐙
| true => true b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ (match bexp {true} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
imp {if (b) {c₁} else {c₂}}
apply Com.equiv_trans true.h₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ (match bexp {true} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
?true.c₂true.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ ?true.c₂ ≃ imp {if (b) {c₁} else {c₂}}true.c₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ Com <;> true.h₁ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ (match bexp {true} with
| bexp {true} => c₁.foldConstants
| bexp {false} => c₂.foldConstants
| b' => imp {if (b') {~c₁.foldConstants} else {~c₂.foldConstants}}) ≃
?true.c₂true.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ ?true.c₂ ≃ imp {if (b) {c₁} else {c₂}}true.c₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ Com try assumption true.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ c₁ ≃ imp {if (b) {c₁} else {c₂}}
symm true.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ imp {if (b) {c₁} else {c₂}} ≃ c₁; apply Com.if_true true.h₂ b:Bexpc₁:Comc₂:Comih₁:c₁.foldConstants ≃ c₁ih₂:c₂.foldConstants ≃ c₂hb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ bexp {true} ≃ b; simp_all All goals completed! 🐙
| whileDo b c ih => whileDo b:Bexpc:Comih:c.foldConstants ≃ c⊢ imp {while (b) {c}}.foldConstants ≃ imp {while (b) {c}}
solution!
simp only [Com.foldConstants] whileDo b:Bexpc:Comih:c.foldConstants ≃ c⊢ (match b.foldConstants with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}
have hb : b.foldConstants ≃ b := by ⊢ TransSound foldConstants
apply Bexp.foldConstants_sound whileDo b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ b⊢ (match b.foldConstants with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}
-- Again, the cases where `Com.foldConstants` doesn't change the test
-- or don't change the loop body follow from the `ih` and
-- `Bexp.foldConstants_sound`
cases heq : b.foldConstants whileDo.bool b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.eq b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ = a₂✝}⊢ (match bexp {a₁✝ = a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.neq b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≠ a₂✝}⊢ (match bexp {a₁✝ ≠ a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.le b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≤ a₂✝}⊢ (match bexp {a₁✝ ≤ a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.gt b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ > a₂✝}⊢ (match bexp {a₁✝ > a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.not b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb✝:Bexpheq:b.foldConstants = bexp {¬ b✝}⊢ (match bexp {¬ b✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.and b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ (match bexp {b₁✝ ∧ b₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}} <;> whileDo.bool b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.eq b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ = a₂✝}⊢ (match bexp {a₁✝ = a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.neq b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≠ a₂✝}⊢ (match bexp {a₁✝ ≠ a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.le b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ ≤ a₂✝}⊢ (match bexp {a₁✝ ≤ a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.gt b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ ba₁✝:Aexpa₂✝:Aexpheq:b.foldConstants = bexp {a₁✝ > a₂✝}⊢ (match bexp {a₁✝ > a₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.not b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb✝:Bexpheq:b.foldConstants = bexp {¬ b✝}⊢ (match bexp {¬ b✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}whileDo.and b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ (match bexp {b₁✝ ∧ b₂✝} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}} try (apply Com.congruence_while whileDo.and.hb b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ bexp {b₁✝ ∧ b₂✝} ≃ bwhileDo.and.hc b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c.foldConstants ≃ c <;> whileDo.and.hb b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ bexp {b₁✝ ∧ b₂✝} ≃ bwhileDo.and.hc b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb₁✝:Bexpb₂✝:Bexpheq:b.foldConstants = bexp {b₁✝ ∧ b₂✝}⊢ c.foldConstants ≃ c simp_all All goals completed! 🐙)
· whileDo.bool b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb✝:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}} case whileDo.bool b => b✝:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bb:Boolheq:b.foldConstants = Bexp.bool b✝⊢ (match Bexp.bool b✝ with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}
cases b with
| false => false b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ (match bexp {false} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}
symm false b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq: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}}; apply Com.while_false false b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq:b.foldConstants = bexp {false}⊢ b ≃ bexp {false}; simp_all All goals completed! 🐙
| true => true b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ (match bexp {true} with
| bexp {true} => imp {while (true) {skip}}
| bexp {false} => imp {skip}
| b' => imp {while (b') {~c.foldConstants}}) ≃
imp {while (b) {c}}
symm true b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq: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}}; apply Com.while_true true b:Bexpc:Comih:c.foldConstants ≃ chb:b.foldConstants ≃ bheq:b.foldConstants = bexp {true}⊢ b ≃ bexp {true}; simp_all All goals completed! 🐙
4.3.3. Soundness of (0 + n) Elimination, Redux
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₁)} }
)
theorem test_optimize0plus :
Com.optimize0plus
(imp { while (X ≠ 0) { X := 0 + X - 1 } }) =
(imp { while (X ≠ 0) { X := X - 1 } }) := by ⊢ imp {while (X ≠ 0) {X := 0 + X - 1}}.optimize0plus = imp {while (X ≠ 0) {X := X - 1}}
solution!
rfl 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!
As with the proofs for foldConstants,
you may find the fun_induction tactic helpful here.
theorem Aexp.optimize0plus_sound : Aexp.TransSound Aexp.optimize0plus := by ⊢ TransSound optimize0plus
solution!
intro a st a:Aexpst:State⊢ eval st a.optimize0plus = eval st a
fun_induction Aexp.optimize0plus a case1 st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)case2 st:Statex✝:Ident⊢ eval st (id x✝) = eval st (id x✝)case3 st:Statea₂✝:Aexpih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st a₂✝.optimize0plus = eval st (aexp {0 + a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexpx✝:a₁✝ = aexp {0} → Falseih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus + ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ + a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexpih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus - ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ - a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexpih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus * ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ * a₂✝}) <;> case1 st:Staten✝:Nat⊢ eval st (num n✝) = eval st (num n✝)case2 st:Statex✝:Ident⊢ eval st (id x✝) = eval st (id x✝)case3 st:Statea₂✝:Aexpih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st a₂✝.optimize0plus = eval st (aexp {0 + a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexpx✝:a₁✝ = aexp {0} → Falseih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus + ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ + a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexpih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus - ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ - a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexpih2✝:eval st a₁✝.optimize0plus = eval st a₁✝ih1✝:eval st a₂✝.optimize0plus = eval st a₂✝⊢ eval st (aexp {~a₁✝.optimize0plus * ~a₂✝.optimize0plus}) = eval st (aexp {a₁✝ * a₂✝}) simp_all All goals completed! 🐙
theorem Bexp.optimize0plus_sound : Bexp.TransSound Bexp.optimize0plus := by ⊢ TransSound optimize0plus
solution!
intro b st b:Bexpst:State⊢ eval st b.optimize0plus = eval st b
fun_induction Bexp.optimize0plus b case1 st:State⊢ eval st (bexp {true}) = eval st (bexp {true})case2 st:State⊢ eval st (bexp {false}) = eval st (bexp {false})case3 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus = ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ = a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus ≠ ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ ≠ a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus ≤ ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ ≤ a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus > ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ > a₂✝})case7 st:Stateb₁✝:Bexpih1✝:eval st b₁✝.optimize0plus = eval st b₁✝⊢ eval st (bexp {¬ ~b₁✝.optimize0plus}) = eval st (bexp {¬ b₁✝})case8 st:Stateb₁✝:Bexpb₂✝:Bexpih2✝:eval st b₁✝.optimize0plus = eval st b₁✝ih1✝:eval st b₂✝.optimize0plus = eval st b₂✝⊢ eval st (bexp {~b₁✝.optimize0plus ∧ ~b₂✝.optimize0plus}) = eval st (bexp {b₁✝ ∧ b₂✝}) <;> case1 st:State⊢ eval st (bexp {true}) = eval st (bexp {true})case2 st:State⊢ eval st (bexp {false}) = eval st (bexp {false})case3 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus = ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ = a₂✝})case4 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus ≠ ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ ≠ a₂✝})case5 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus ≤ ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ ≤ a₂✝})case6 st:Statea₁✝:Aexpa₂✝:Aexp⊢ eval st (bexp {~a₁✝.optimize0plus > ~a₂✝.optimize0plus}) = eval st (bexp {a₁✝ > a₂✝})case7 st:Stateb₁✝:Bexpih1✝:eval st b₁✝.optimize0plus = eval st b₁✝⊢ eval st (bexp {¬ ~b₁✝.optimize0plus}) = eval st (bexp {¬ b₁✝})case8 st:Stateb₁✝:Bexpb₂✝:Bexpih2✝:eval st b₁✝.optimize0plus = eval st b₁✝ih1✝:eval st b₂✝.optimize0plus = eval st b₂✝⊢ eval st (bexp {~b₁✝.optimize0plus ∧ ~b₂✝.optimize0plus}) = eval st (bexp {b₁✝ ∧ b₂✝})
simp_all only [Bexp.eval] All goals completed! 🐙 <;> case3 st:Statea₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st a₁✝.optimize0plus == Aexp.eval st a₂✝.optimize0plus) = (Aexp.eval st a₁✝ == Aexp.eval st a₂✝)case4 st:Statea₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st a₁✝.optimize0plus != Aexp.eval st a₂✝.optimize0plus) = (Aexp.eval st a₁✝ != Aexp.eval st a₂✝)case5 st:Statea₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st a₁✝.optimize0plus ≤ Aexp.eval st a₂✝.optimize0plus) = decide (Aexp.eval st a₁✝ ≤ Aexp.eval st a₂✝)case6 st:Statea₁✝:Aexpa₂✝:Aexp⊢ decide (Aexp.eval st a₁✝.optimize0plus > Aexp.eval st a₂✝.optimize0plus) = decide (Aexp.eval st a₁✝ > Aexp.eval st a₂✝)
rw [Aexp.optimize0plus_sound, case3 st:Statea₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st a₁✝ == Aexp.eval st a₂✝.optimize0plus) = (Aexp.eval st a₁✝ == Aexp.eval st a₂✝) Aexp.optimize0plus_sound case3 st:Statea₁✝:Aexpa₂✝:Aexp⊢ (Aexp.eval st a₁✝ == Aexp.eval st a₂✝) = (Aexp.eval st a₁✝ == Aexp.eval st a₂✝)] All goals completed! 🐙
theorem Com.optimize0plus_sound : Com.TransSound Com.optimize0plus := by ⊢ TransSound optimize0plus
solution!
intro c c:Com⊢ c.optimize0plus ≃ c
induction c with
| skip => skip ⊢ imp {skip}.optimize0plus ≃ imp {skip} rfl All goals completed! 🐙
| asgn x a => asgn x:Identa:Aexp⊢ imp {x := a}.optimize0plus ≃ imp {x := a} apply Com.congruence_asgn asgn x:Identa:Aexp⊢ a.optimize0plus ≃ a; apply Aexp.optimize0plus_sound All goals completed! 🐙
| seq c₁ c₂ ih₁ ih₂ => seq c₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ imp {c₁; c₂}.optimize0plus ≃ imp {c₁; c₂} apply Com.congruence_seq seq.hc₁ c₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₁.optimize0plus ≃ c₁seq.hc₂ c₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₂.optimize0plus ≃ c₂ <;> seq.hc₁ c₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₁.optimize0plus ≃ c₁seq.hc₂ c₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₂.optimize0plus ≃ c₂ assumption All goals completed! 🐙
| cond b c₁ c₂ ih₁ ih₂ => cond b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ imp {if (b) {c₁} else {c₂}}.optimize0plus ≃ imp {if (b) {c₁} else {c₂}}
apply Com.congruence_if cond.hb b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ b.optimize0plus ≃ bcond.hc₁ b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₁.optimize0plus ≃ c₁cond.hc₂ b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₂.optimize0plus ≃ c₂ <;> cond.hb b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ b.optimize0plus ≃ bcond.hc₁ b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₁.optimize0plus ≃ c₁cond.hc₂ b:Bexpc₁:Comc₂:Comih₁:c₁.optimize0plus ≃ c₁ih₂:c₂.optimize0plus ≃ c₂⊢ c₂.optimize0plus ≃ c₂ try assumption All goals completed! 🐙
apply Bexp.optimize0plus_sound All goals completed! 🐙
| whileDo b c ih => whileDo b:Bexpc:Comih:c.optimize0plus ≃ c⊢ imp {while (b) {c}}.optimize0plus ≃ imp {while (b) {c}}
apply Com.congruence_while whileDo.hb b:Bexpc:Comih:c.optimize0plus ≃ c⊢ b.optimize0plus ≃ bwhileDo.hc b:Bexpc:Comih:c.optimize0plus ≃ c⊢ c.optimize0plus ≃ c <;> whileDo.hb b:Bexpc:Comih:c.optimize0plus ≃ c⊢ b.optimize0plus ≃ bwhileDo.hc b:Bexpc:Comih:c.optimize0plus ≃ c⊢ c.optimize0plus ≃ c try assumption All goals completed! 🐙
apply Bexp.optimize0plus_sound 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 (using Com.optimize0plus).
def optimizer (c : Com) := Com.optimize0plus (Com.foldConstants c)
Prove that this optimizer is sound.
theorem optimizer_sound : Com.TransSound optimizer := by ⊢ Com.TransSound optimizer
solution!
intro c c:Com⊢ optimizer c ≃ c
apply Com.equiv_trans h₁ c:Com⊢ optimizer c ≃ ?c₂h₂ c:Com⊢ ?c₂ ≃ cc₂ c:Com⊢ Com
· h₁ c:Com⊢ optimizer c ≃ ?c₂ apply Com.optimize0plus_sound All goals completed! 🐙
· h₂ c:Com⊢ c.foldConstants ≃ c apply Com.foldConstants_sound All goals completed! 🐙
4.4. 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, these 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 counterexample 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 : Ident) (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) }) := by ⊢ Aexp.subst X (aexp {42 + 53}) (aexp {Y + X}) = aexp {Y + (42 + 53)} rfl 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₂ : Ident) (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 := by ⊢ ¬SubstEquivProperty
rw [SubstEquivProperty ⊢ ¬∀ (x₁ x₂ : Ident) (a₁ a₂ : Aexp), imp {x₁ := a₁; x₂ := a₂} ≃ imp {x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}] ⊢ ¬∀ (x₁ x₂ : Ident) (a₁ a₂ : Aexp), imp {x₁ := a₁; x₂ := a₂} ≃ imp {x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}
intro contra contra:∀ (x₁ x₂ : Ident) (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... -/
let c₁ := imp {X := X + 1; Y := X} contra:∀ (x₁ x₂ : Ident) (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
let c₂ := imp {X := X + 1; Y := X + 1} contra:∀ (x₁ x₂ : Ident) (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
have h : c₁ ≃ c₂ := by ⊢ ¬SubstEquivProperty
apply contra contra:∀ (x₁ x₂ : Ident) (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
clear contra 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). -/
let st₁ := Y →ₜ 1 ; 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
let 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 →ₜ 1st₂:TotalMap Ident Nat := Y →ₜ 2 ; X →ₜ 1⊢ False
have h₁ : ∅ =[ c₁ ]=> st₁ := by ⊢ ¬SubstEquivProperty
constructor h₁ 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⊢ imp {X := X + 1}.EvalR ∅ ?st'h₂ 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⊢ imp {Y := X}.EvalR ?st' st₁st' 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⊢ State <;> h₁ 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⊢ imp {X := X + 1}.EvalR ∅ ?st'h₂ 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⊢ imp {Y := X}.EvalR ?st' st₁st' 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⊢ State constructor h₂ 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⊢ Aexp.eval ("X" →ₜ 1) (aexp {X}) = 1 <;> h₁.h 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⊢ Aexp.eval ∅ (aexp {X + 1}) = 1h₂ 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⊢ Aexp.eval ("X" →ₜ 1) (aexp {X}) = 1 rfl 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
have h₂ : ∅ =[ c₂ ]=> st₂ := by ⊢ ¬SubstEquivProperty
constructor h₁ 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₁⊢ imp {X := X + 1}.EvalR ∅ ?st'h₂ 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₁⊢ imp {Y := X + 1}.EvalR ?st' st₂st' 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₁⊢ State <;> h₁ 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₁⊢ imp {X := X + 1}.EvalR ∅ ?st'h₂ 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₁⊢ imp {Y := X + 1}.EvalR ?st' st₂st' 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₁⊢ State constructor h₂ 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₁⊢ Aexp.eval ("X" →ₜ 1) (aexp {X + 1}) = 2 <;> h₁.h 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₁⊢ Aexp.eval ∅ (aexp {X + 1}) = 1h₂ 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₁⊢ Aexp.eval ("X" →ₜ 1) (aexp {X + 1}) = 2 rfl 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.
apply h.mp at h₁ 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
apply ceval_deterministic h₁ at h₂ 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
have contra : st₁[Y] = st₂[Y] := by ⊢ ¬SubstEquivProperty rw [h₂ 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₂⊢ st₂[Y] = st₂[Y]] 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
rw [TotalMap.update_eq, 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 = st₂[Y]⊢ False TotalMap.update_eq 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] at contra 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
contradiction All goals completed! 🐙
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 : Ident) : Aexp → Prop where
| num {n : Nat} : VarNotUsedInAexp x (Aexp.num n)
| id {y : Ident} (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 : Ident} {st : State} {a : Aexp} {ni : Nat}
(h : VarNotUsedInAexp x a) :
a.eval (x →ₜ ni ; st) = a.eval st := by x:Identst:Statea:Aexpni:Nath:VarNotUsedInAexp x a⊢ eval (x →ₜ ni ; st) a = eval st a
induction a with
| num n => num x:Identst:Stateni:Natn:Nath:VarNotUsedInAexp x (num n)⊢ eval (x →ₜ ni ; st) (num n) = eval st (num n) rfl All goals completed! 🐙
| id y => id x:Identst:Stateni:Naty:Identh:VarNotUsedInAexp x (id y)⊢ eval (x →ₜ ni ; st) (id y) = eval st (id y)
inversion h id x:Identst:Stateni:Naty:Identh✝:x ≠ y⊢ eval (x →ₜ ni ; st) (id y) = eval st (id y); simp only [Aexp.eval] id x:Identst:Stateni:Naty:Identh✝:x ≠ y⊢ (x →ₜ ni ; st)[y] = st[y]
apply TotalMap.update_neq id x:Identst:Stateni:Naty:Identh✝:x ≠ y⊢ x ≠ y; assumption All goals completed! 🐙
| plus a₁ a₂ ih₁ ih₂ plus x:Identst: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₂})
| minus a₁ a₂ ih₁ ih₂ minus x:Identst: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₂})
| mult a₁ a₂ ih₁ ih₂ mult x:Identst: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₂}) => mult x:Identst: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₂})minus x:Identst: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₂})plus x:Identst: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₂})
simp only [Aexp.eval] mult x:Identst: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₂; inversion h mult x:Identst: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₂;
rw [ih₁, plus x:Identst: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 st a₁ + eval (x →ₜ ni ; st) a₂ = eval st a₁ + eval st a₂plus x:Identst: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₁ ih₂ plus x:Identst: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 st a₁ + eval st a₂ = eval st a₁ + eval st a₂plus x:Identst: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₂plus x:Identst: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₁] minus x:Identst: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₂minus x:Identst: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₁ mult x:Identst: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₂mult x:Identst: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₁ <;> mult x:Identst: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₂mult x:Identst: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₁ assumption All goals completed! 🐙
Using VarNotUsedInAexp, formalize and prove a correct version
of SubstEquivProperty.
theorem Aexp.eval_subst {x : Ident} {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) := by x:Identst:Statea₁:Aexpa₂:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)
induction a₂ generalizing a₁ st with
| num n => num x:Identn:Natst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (num n) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (num n)) rfl All goals completed! 🐙
| id y => id x:Identy:Identst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (id y))
simp only [Aexp.subst] id x:Identy:Identst:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (if x = y then a₁ else id y)
by_cases h : x = y pos x:Identy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:x = y⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (if x = y then a₁ else id y)neg x:Identy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:¬x = y⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (if x = y then a₁ else id y)
· pos x:Identy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:x = y⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (if x = y then a₁ else id y) subst_vars pos y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ eval (y →ₜ eval st a₁ ; st) (id y) = eval (y →ₜ eval st a₁ ; st) (if y = y then a₁ else id y); symm pos y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ eval (y →ₜ eval st a₁ ; st) (if y = y then a₁ else id y) = eval (y →ₜ eval st a₁ ; st) (id y);
simp only [Aexp.eval_id, TotalMap.update_eq] pos y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ eval (y →ₜ eval st a₁ ; st) (if True then a₁ else id y) = eval st a₁
apply Aexp.eval_weakening pos y:Identst:Statea₁:Aexph:VarNotUsedInAexp y a₁⊢ VarNotUsedInAexp y (if True then a₁ else id y); assumption All goals completed! 🐙
· neg x:Identy:Identst:Statea₁:Aexph✝:VarNotUsedInAexp x a₁h:¬x = y⊢ eval (x →ₜ eval st a₁ ; st) (id y) = eval (x →ₜ eval st a₁ ; st) (if x = y then a₁ else id y) simp_all All goals completed! 🐙
| plus a₁ a₂ ih₁ ih₂ plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ + a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ + a₂}))
| minus a₁ a₂ ih₁ ih₂ minus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ - a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ - a₂}))
| mult a₁ a₂ ih₁ ih₂ mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ * a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ * a₂})) => mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ * a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ * a₂}))minus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ - a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ - a₂}))plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (aexp {a₁✝ + a₂}) = eval (x →ₜ eval st a₁ ; st) (subst x a₁ (aexp {a₁✝ + a₂}))
simp only [Aexp.eval, Aexp.subst] mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) a₁✝ * eval (x →ₜ eval st a₁ ; st) a₂ =
eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝) * eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)
rw [ih₁, plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝) + eval (x →ₜ eval st a₁ ; st) a₂ =
eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝) + eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ ih₂ plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝) + eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂) =
eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝) + eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁plus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁] minus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁minus x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ <;> mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁mult x:Identa₁✝:Aexpa₂:Aexpih₁:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₁✝ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₁✝)ih₂:∀ {st : State} {a₁ : Aexp},
VarNotUsedInAexp x a₁ → eval (x →ₜ eval st a₁ ; st) a₂ = eval (x →ₜ eval st a₁ ; st) (subst x a₁ a₂)st:Statea₁:Aexph:VarNotUsedInAexp x a₁⊢ VarNotUsedInAexp x a₁ assumption All goals completed! 🐙
theorem subst_equiv {x₁ x₂ : Ident} {a₁ a₂ : Aexp}
(h : VarNotUsedInAexp x₁ a₁) :
imp { x₁ := a₁; x₂ := a₂ } ≃
imp { x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)} := by x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁⊢ imp {x₁ := a₁; x₂ := a₂} ≃ imp {x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂)}
intro st st' x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := a₁; x₂ := a₂ ]=> st') ↔ st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'; constructor mp x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := a₁; x₂ := a₂ ]=> st') → st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'mpr x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st') → st =[ x₁ := a₁; x₂ := a₂ ]=> st' <;> mp x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := a₁; x₂ := a₂ ]=> st') → st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'mpr x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':State⊢ (st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st') → st =[ x₁ := a₁; x₂ := a₂ ]=> st' intro heval mpr x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Stateheval:st =[ x₁ := a₁; x₂ := ~(Aexp.subst x₁ a₁ a₂) ]=> st'⊢ st =[ x₁ := a₁; x₂ := a₂ ]=> st'
· mp x₁:Identx₂:Identa₁: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₂ =>
constructor seq.h₁ x₁:Identx₂:Identa₁: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'seq.h₂ x₁:Identx₂:Identa₁: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'seq.st' x₁:Identx₂:Identa₁:Aexpa₂:Aexph:VarNotUsedInAexp x₁ a₁st:Statest':Statest'✝:Stateh₁:imp {x₁ := a₁}.EvalR st st'✝h₂:imp {x₂ := a₂}.EvalR st'✝ st'⊢ State; assumption seq.h₂ x₁:Identx₂:Identa₁: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'
inversion h₂ asgn x₁:Identx₂:Identa₁: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'✝) <;> asgn x₁:Identx₂:Identa₁: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'✝) subst_vars asgn x₁:Identx₂:Identa₁: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'✝); constructor asgn x₁:Identx₂:Identa₁: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₂
inversion h₁ asgn x₁:Identx₂:Identa₁: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₂ <;> asgn x₁:Identx₂:Identa₁: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₂ subst_vars asgn x₁:Identx₂:Identa₁: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₂; symm asgn x₁:Identx₂:Identa₁: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₂)
apply Aexp.eval_subst h All goals completed! 🐙
· mpr x₁:Identx₂:Identa₁: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₂ =>
constructor seq.h₁ x₁:Identx₂:Identa₁: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'seq.h₂ x₁:Identx₂:Identa₁: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'seq.st' x₁:Identx₂:Identa₁: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; assumption seq.h₂ x₁:Identx₂:Identa₁: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'
inversion h₂ asgn x₁:Identx₂:Identa₁: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'✝) <;> asgn x₁:Identx₂:Identa₁: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'✝) subst_vars asgn x₁:Identx₂:Identa₁: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'✝); constructor asgn x₁:Identx₂:Identa₁: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₂)
inversion h₁ asgn x₁:Identx₂:Identa₁: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₂) <;> asgn x₁:Identx₂:Identa₁: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₂) subst_vars asgn x₁:Identx₂:Identa₁: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₂)
apply Aexp.eval_subst h All goals completed! 🐙
Prove that an infinite loop is not equivalent to skip.
theorem inequiv_exercise :
¬ (imp { while (true) {skip} } ≃ imp { skip }) := by ⊢ ¬imp {while (true) {skip}} ≃ imp {skip}
solution!
intro contra contra:imp {while (true) {skip}} ≃ imp {skip}⊢ False
have h : ¬ (∅ =[ while (true) { skip } ]=> ∅) := by ⊢ ¬imp {while (true) {skip}} ≃ imp {skip}
apply Com.while_true_nonterm contra:imp {while (true) {skip}} ≃ imp {skip}⊢ bexp {true} ≃ bexp {true}; apply Bexp.equiv_refl contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ False
apply h contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ ∅ =[ while (true) {skip} ]=> ∅
rw [@contra ∅ ∅ contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ ∅ =[ skip ]=> ∅] contra:imp {while (true) {skip}} ≃ imp {skip}h:¬∅ =[ while (true) {skip} ]=> ∅⊢ ∅ =[ skip ]=> ∅
constructor All goals completed! 🐙
4.5. 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 on 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 : Ident → Aexp → Com
| seq : Com → Com → Com
| cond : Bexp → Com → Com → Com
| whileDo : Bexp → Com → Com
| havoc : Ident → Com -- <--- NEW
Notation encoding: commands, macro rules
namespace Com
/-- Havoc -/
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 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 }
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 : Ident} (n : Nat) :
EvalR (imp {havoc x}) st (x →ₜ n ; st)
Notation encoding: commands
open 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' := by c:Comst:Statest':State⊢ c.EvalR st st' ↔ st =[ c ]=> st' rfl All goals completed! 🐙
As a sanity check, the following claims should be provable for your definition:
example : ∅ =[ havoc X ]=> (X →ₜ 0) := by ⊢ ∅ =[ havoc X ]=> X →ₜ 0
solution!
constructor All goals completed! 🐙
example : ∅ =[ skip; havoc Z ]=> (Z →ₜ 42) := by ⊢ ∅ =[ skip; havoc Z ]=> Z →ₜ 42
solution!
apply Com.EvalR.seq h₁ ⊢ imp {skip}.EvalR ∅ ?st'h₂ ⊢ imp {havoc Z}.EvalR ?st' (Z →ₜ 42)st' ⊢ State; constructor h₂ ⊢ imp {havoc Z}.EvalR ∅ (Z →ₜ 42); constructor 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₂ := by c₁:Comc₂:Com⊢ c₁.Equiv c₂ ↔ c₁ ≃ c₂ rfl All goals completed! 🐙
@[simp]
theorem Com.equiv_def {c₁ c₂ : Com} : c₁ ≃ c₂ ↔
∀ {st st' : State}, (st =[ c₁ ]=> st') ↔ (st =[ c₂ ]=> st') := by c₁:Comc₂:Com⊢ c₁ ≃ c₂ ↔ ∀ {st st' : State}, (st =[ c₁ ]=> st') ↔ st =[ c₂ ]=> st' rfl All goals completed! 🐙
Let's apply this definition to prove some nondeterministic programs equivalent / inequivalent.
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 : Ident} {st st' : State}
(h : st =[ havoc x; havoc y ]=> st') :
st =[ havoc y; havoc x ]=> st' := by x:Identy:Identst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'⊢ st =[ havoc y; havoc x ]=> st'
by_cases hid : x = y pos x:Identy:Identst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:x = y⊢ st =[ havoc y; havoc x ]=> st'neg x:Identy:Identst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:¬x = y⊢ st =[ havoc y; havoc x ]=> st'
· pos x:Identy:Identst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:x = y⊢ st =[ havoc y; havoc x ]=> st' subst_vars pos y:Identst:Statest':Stateh:st =[ havoc y; havoc y ]=> st'⊢ st =[ havoc y; havoc y ]=> st'; assumption All goals completed! 🐙
· neg x:Identy:Identst:Statest':Stateh:st =[ havoc x; havoc y ]=> st'hid:¬x = y⊢ st =[ havoc y; havoc x ]=> st' inversion h with
| seq h₁ h₂ =>
subst_vars seq x:Identy:Identst:Statest':Statehid:¬x = yst'✝:Stateh₁:imp {havoc x}.EvalR st st'✝h₂:imp {havoc y}.EvalR st'✝ st'⊢ st =[ havoc y; havoc x ]=> st'
inversion h₁ havoc x:Identy:Identst:Statest':Statehid:¬x = yn✝:Nath₂:imp {havoc y}.EvalR (x →ₜ n✝ ; st) st'⊢ st =[ havoc y; havoc x ]=> st'; inversion h₂ havoc x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ st =[ havoc y; havoc x ]=> y →ₜ n✝ ; x →ₜ n✝¹ ; st
constructor havoc.h₁ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc y}.EvalR st ?«havoc».st'havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR ?«havoc».st' (y →ₜ n✝ ; x →ₜ n✝¹ ; st)havoc.st' x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ State; constructor havoc.h₁ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ Nathavoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ ?«havoc».h₁ ; st) (y →ₜ n✝ ; x →ₜ n✝¹ ; st); assumption havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ n✝ ; st) (y →ₜ n✝ ; x →ₜ n✝¹ ; st)
rw [TotalMap.update_permute havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ n✝ ; st) (x →ₜ n✝¹ ; y →ₜ n✝ ; st)havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ y ≠ x] havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ imp {havoc x}.EvalR (y →ₜ n✝ ; st) (x →ₜ n✝¹ ; y →ₜ n✝ ; st)havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ y ≠ x; constructor havoc.h₂ x:Identy:Identst:Statehid:¬x = yn✝¹:Natn✝:Nat⊢ y ≠ x; lia All goals completed! 🐙
theorem pXY_cequiv_pYX :
(pXY ≃ pYX) ∨ ¬ (pXY ≃ pYX) := by ⊢ pXY ≃ pYX ∨ ¬pXY ≃ pYX
/- Hint: You may want to use `TotalMap.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!
left ⊢ pXY ≃ pYX; intro st st' st:Statest':State⊢ (st =[ pXY ]=> st') ↔ st =[ pYX ]=> st'
constructor mp st:Statest':State⊢ (st =[ pXY ]=> st') → st =[ pYX ]=> st'mpr st:Statest':State⊢ (st =[ pYX ]=> st') → st =[ pXY ]=> st' <;> mp st:Statest':State⊢ (st =[ pXY ]=> st') → st =[ pYX ]=> st'mpr st:Statest':State⊢ (st =[ pYX ]=> st') → st =[ pXY ]=> st' apply pXY_approx_pYX All goals completed! 🐙
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) := by ⊢ ptwice ≃ pcopy ∨ ¬ptwice ≃ pcopy
solution!
right ⊢ ¬ptwice ≃ pcopy; intro contra contra:ptwice ≃ pcopy⊢ False
have h : ∅ =[ ptwice ]=> (Y →ₜ 1 ; X →ₜ 0) := by ⊢ ptwice ≃ pcopy ∨ ¬ptwice ≃ pcopy
apply @Com.EvalR.seq (st' := X →ₜ 0) h₁ contra:ptwice ≃ pcopy⊢ imp {havoc X}.EvalR ∅ (X →ₜ 0)h₂ contra:ptwice ≃ pcopy⊢ imp {havoc Y}.EvalR (X →ₜ 0) (Y →ₜ 1 ; X →ₜ 0) <;> h₁ contra:ptwice ≃ pcopy⊢ imp {havoc X}.EvalR ∅ (X →ₜ 0)h₂ contra:ptwice ≃ pcopy⊢ imp {havoc Y}.EvalR (X →ₜ 0) (Y →ₜ 1 ; X →ₜ 0) constructor contra:ptwice ≃ pcopyh:∅ =[ ptwice ]=> Y →ₜ 1 ; X →ₜ 0⊢ False
rw [contra contra:ptwice ≃ pcopyh:∅ =[ pcopy ]=> Y →ₜ 1 ; X →ₜ 0⊢ False] at h contra:ptwice ≃ pcopyh:∅ =[ pcopy ]=> Y →ₜ 1 ; X →ₜ 0⊢ False
inversion h with
| seq h₁ h₂ =>
inversion h₁ havoc contra:ptwice ≃ pcopyn✝:Nath₂:imp {Y := X}.EvalR (X →ₜ n✝) (Y →ₜ 1 ; X →ₜ 0)⊢ False; inversion h₂ with
| asgn n x h =>
subst_vars asgn 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; simp only [cond_eq_ite, beq_iff_eq, Aexp.eval_id, TotalMap.update_eq] at h asgn 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
have hy := congrFun h Y asgn 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
have hx := congrFun h X asgn 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
simp only [↓reduceIte] at hy asgn 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
simp only [TotalMap.update_eq, ite_self] at hx asgn 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; rw [←hy asgn 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] at hx asgn 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
contradiction All goals completed! 🐙
The definition of program equivalence we are using here has some
subtle consequences for 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 last of the following exercises, p₅_p₆_equiv, illustrates
this phenomenon.
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') := by st:Statest':Stateh:st[X] ≠ 0⊢ ¬st =[ p₁ ]=> st'
solution!
intro contra st:Statest':Stateh:st[X] ≠ 0contra:st =[ p₁ ]=> st'⊢ False
generalize h : p₁ = p₁' at contra st:Statest':Stateh✝:st[X] ≠ 0p₁':Comh:p₁ = p₁'contra:st =[ p₁' ]=> st'⊢ False
induction contra with inversion h All goals completed! 🐙
| whileFalse h' => refl st:Statest':Statep₁':Comst✝:Stateh:st✝[X] ≠ 0h':Bexp.eval st✝ (bexp {¬ (X = 0)}) = false⊢ False simp_all [Bexp.eval] All goals completed! 🐙
| whileTrue hb hc hloop ihc ihloop => refl 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
apply ihloop refl.h 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] ≠ 0refl.h 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⊢ p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} <;> refl.h 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] ≠ 0refl.h 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⊢ p₁ = imp {while (¬ (X = 0)) {havoc Y; X := X + 1}} try rfl All goals completed! 🐙
inversion hc with
| seq h₁ h₂ =>
inversion h₁ havoc 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; inversion h₂ asgn 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
rw [TotalMap.update_eq asgn 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] asgn 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; simp_all asgn 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; lia All goals completed! 🐙
theorem p₂_may_diverge (st st' : State) (h : st[X] ≠ 0) :
¬ (st =[ p₂ ]=> st') := by st:Statest':Stateh:st[X] ≠ 0⊢ ¬st =[ p₂ ]=> st'
solution!
intro contra st:Statest':Stateh:st[X] ≠ 0contra:st =[ p₂ ]=> st'⊢ False
generalize h : p₂ = p₂' at contra st:Statest':Stateh✝:st[X] ≠ 0p₂':Comh:p₂ = p₂'contra:st =[ p₂' ]=> st'⊢ False
induction contra with inversion h All goals completed! 🐙
| whileFalse h' => refl st:Statest':Statep₂':Comst✝:Stateh:st✝[X] ≠ 0h':Bexp.eval st✝ (bexp {¬ (X = 0)}) = false⊢ False simp_all [Bexp.eval] All goals completed! 🐙
| whileTrue hb hc hloop ihc ihloop => refl 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
inversion hc 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⊢ False; apply ihloop skip.h 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] ≠ 0skip.h 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⊢ p₂ = imp {while (¬ (X = 0)) {skip}} <;> skip.h 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] ≠ 0skip.h 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⊢ p₂ = imp {while (¬ (X = 0)) {skip}} trivial All goals completed! 🐙
Use these two lemmas to prove that p₁ and p₂ are actually
equivalent.
theorem p₁_p₂_equiv : p₁ ≃ p₂ := by ⊢ p₁ ≃ p₂
solution!
intro st st' st:Statest':State⊢ (st =[ p₁ ]=> st') ↔ st =[ p₂ ]=> st'; constructor mp st:Statest':State⊢ (st =[ p₁ ]=> st') → st =[ p₂ ]=> st'mpr st:Statest':State⊢ (st =[ p₂ ]=> st') → st =[ p₁ ]=> st' <;> mp st:Statest':State⊢ (st =[ p₁ ]=> st') → st =[ p₂ ]=> st'mpr st:Statest':State⊢ (st =[ p₂ ]=> st') → st =[ p₁ ]=> st' intro h mpr st:Statest':Stateh:st =[ p₂ ]=> st'⊢ st =[ p₁ ]=> st'
· mp st:Statest':Stateh:st =[ p₁ ]=> st'⊢ st =[ p₂ ]=> st' cases h with
| whileFalse h' => mp.whileFalse st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ st =[ p₂ ]=> st constructor mp.whileFalse st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ Bexp.eval st (bexp {¬ (X = 0)}) = false; assumption All goals completed! 🐙
| whileTrue hb hc hloop => mp.whileTrue 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'
apply p₁_may_diverge at hloop mp.whileTrue 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'h 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; contradiction h 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
simp only [Bexp.eval, Aexp.eval,
Bool.not_eq_eq_eq_not, Bool.not_true, beq_eq_false_iff_ne, ne_eq] at hb h 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₂ =>
inversion h₁ havoc 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; inversion h₂ asgn 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
rw [TotalMap.update_eq asgn 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] asgn 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; simp_all asgn 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; lia All goals completed! 🐙
· mpr st:Statest':Stateh:st =[ p₂ ]=> st'⊢ st =[ p₁ ]=> st' cases h with
| whileFalse h' => mpr.whileFalse st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ st =[ p₁ ]=> st constructor mpr.whileFalse st:Stateh':Bexp.eval st (bexp {¬ (X = 0)}) = false⊢ Bexp.eval st (bexp {¬ (X = 0)}) = false; assumption All goals completed! 🐙
| whileTrue hb hc hloop => mpr.whileTrue 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'
apply p₂_may_diverge at hloop mpr.whileTrue st:Statest':Statest'✝:Statehb:Bexp.eval st (bexp {¬ (X = 0)}) = truehc:imp {skip}.EvalR st st'✝hloop:False⊢ st =[ p₁ ]=> st'h 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; contradiction h 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
simp only [Bexp.eval, Aexp.eval,
Bool.not_eq_eq_eq_not, Bool.not_true, beq_eq_false_iff_ne, ne_eq] at hb h 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
inversion hc skip st:Statest':Statehb:¬st["X"] = 0hloop:imp {while (¬ (X = 0)) {skip}}.EvalR st st'⊢ st[X] ≠ 0; assumption All goals completed! 🐙
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₄) := by ⊢ ¬p₃ ≃ p₄
solution!
intro contra contra:p₃ ≃ p₄⊢ False
let st := X →ₜ 1 contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ False
have h : st =[ p₃ ]=> (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) := by ⊢ ¬p₃ ≃ p₄
constructor h₁ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {Z := 1}.EvalR st ?st'h₂ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {while (X ≠ 0) {havoc X; havoc Z}}.EvalR ?st' (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st)st' contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ State
· h₁ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {Z := 1}.EvalR st ?st' constructor h₁.h contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Aexp.eval st (aexp {1}) = ?h₁.nh₁.n contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Nat; rfl All goals completed! 🐙
· h₂ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {while (X ≠ 0) {havoc X; havoc Z}}.EvalR (Z →ₜ Aexp.eval st (aexp {1}) ; st) (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) simp only [Aexp.eval_num, Com.evalR_eq] h₂ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ (Z →ₜ 1 ; st) =[ while (X ≠ 0) {havoc X; havoc Z} ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st
apply Com.EvalR.whileTrue h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Bexp.eval (Z →ₜ 1 ; st) (bexp {X ≠ 0}) = trueh₂.hc contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {havoc X; havoc Z}.EvalR (Z →ₜ 1 ; st) ?h₂.st'h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {while (X ≠ 0) {havoc X; havoc Z}}.EvalR ?h₂.st' (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st)h₂.st' contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ State
· h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Bexp.eval (Z →ₜ 1 ; st) (bexp {X ≠ 0}) = true simp only [Bexp.eval_neq, Aexp.eval_id, Aexp.eval_num, bne_iff_ne, ne_eq] h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ ¬(Z →ₜ 1 ; st)["X"] = 0
rw [TotalMap.update_neq, h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ ¬st["X"] = 0h₂.hb.h contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ "X" TotalMap.update_eq h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ ¬1 = 0h₂.hb.h contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ "X"] h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ ¬1 = 0h₂.hb.h contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ "X" <;> h₂.hb contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ ¬1 = 0h₂.hb.h contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ "X" trivial All goals completed! 🐙
· h₂.hc contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {havoc X; havoc Z}.EvalR (Z →ₜ 1 ; st) ?h₂.st' constructor h₂.hc.h₁ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {havoc X}.EvalR (Z →ₜ 1 ; st) ?h₂.hc.st'h₂.hc.h₂ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {havoc Z}.EvalR ?h₂.hc.st' ?h₂.st'h₂.hc.st' contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ State
apply Com.EvalR.havoc (n := 0) h₂.hc.h₂ contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {havoc Z}.EvalR (X →ₜ 0 ; Z →ₜ 1 ; st) ?h₂.st'
apply Com.EvalR.havoc (n := 0) All goals completed! 🐙
· h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ imp {while (X ≠ 0) {havoc X; havoc Z}}.EvalR (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) apply Com.EvalR.whileFalse h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Bexp.eval (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) (bexp {X ≠ 0}) = false
· h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Bexp.eval (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st) (bexp {X ≠ 0}) = false simp only [Bexp.eval_neq, Aexp.eval_id, Aexp.eval_num, bne_eq_false_iff_eq] h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ (Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st)["X"] = 0
rw [TotalMap.update_permute, h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ (X →ₜ 0 ; Z →ₜ 0 ; Z →ₜ 1 ; st)["X"] = 0h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ X TotalMap.update_eq h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ 0 = 0h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ X] h₂.hloop contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1⊢ Z ≠ X; trivial contra:p₃ ≃ p₄st:TotalMap Ident Nat := X →ₜ 1h:st =[ p₃ ]=> Z →ₜ 0 ; X →ₜ 0 ; Z →ₜ 1 ; st⊢ False
simp only [Com.equiv_def] at contra 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
apply contra.mp at h 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₂ =>
inversion h₁ asgn 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; simp_all only [Aexp.eval_num, Com.evalR_eq] asgn 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; subst_vars asgn 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
rw [TotalMap.update_shadow, asgn st: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⊢ False TotalMap.update_permute, asgn st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ p₃ ]=> st') ↔ st =[ p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> X →ₜ 0 ; Z →ₜ 0 ; Z →ₜ 1 ; st⊢ Falseasgn st: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
TotalMap.update_shadow, asgn st:TotalMap Ident Nat := X →ₜ 1contra:∀ {st st' : State}, (st =[ p₃ ]=> st') ↔ st =[ p₄ ]=> st'h₂:(X →ₜ 0) =[ Z := 1 ]=> X →ₜ 0 ; Z →ₜ 0 ; st⊢ Falseasgn st: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 TotalMap.update_permute asgn 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⊢ Falseasgn st: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 ≠ Zasgn st: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] at h₂ asgn 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⊢ Falseasgn st: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 ≠ Zasgn st: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 <;> asgn 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⊢ Falseasgn st: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 ≠ Zasgn st: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 trivial All goals completed! 🐙
inversion h₂ with
| asgn _ h _ h' =>
have hz := congrFun h' Z asgn 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
simp only [Aexp.eval] at h asgn 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; subst_vars asgn 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
simp at hz All goals completed! 🐙
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 the 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) := by st:Statest':Stateh:st =[ p₅ ]=> st'⊢ st' = X →ₜ 1 ; st
generalize hp : p₅ = p₅' at h st:Statest':Statep₅':Comhp:p₅ = p₅'h:st =[ p₅' ]=> st'⊢ st' = X →ₜ 1 ; st
induction h with inversion hp All goals completed! 🐙
| whileFalse h' => refl st:Statest':Statep₅':Comst✝:Stateh':Bexp.eval st✝ (bexp {X ≠ 1}) = false⊢ st✝ = X →ₜ 1 ; st✝
simp only [Bexp.eval_neq, Aexp.eval_id, Aexp.eval_num, bne_eq_false_iff_eq] at h' refl st:Statest':Statep₅':Comst✝:Stateh':st✝["X"] = 1⊢ st✝ = X →ₜ 1 ; st✝
rw [←h', refl st:Statest':Statep₅':Comst✝:Stateh':st✝["X"] = 1⊢ st✝ = X →ₜ st✝["X"] ; st✝ TotalMap.update_same refl st:Statest':Statep₅':Comst✝:Stateh':st✝["X"] = 1⊢ st✝ = st✝] All goals completed! 🐙
| whileTrue hb hc hloop ihc ihloop => refl 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✝
specialize ihloop rfl refl 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✝; subst_vars refl 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✝
inversion hc havoc 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✝
apply TotalMap.update_shadow All goals completed! 🐙
theorem p₅_p₆_equiv : p₅ ≃ p₆ := by ⊢ p₅ ≃ p₆
solution!
intro st st' st:Statest':State⊢ (st =[ p₅ ]=> st') ↔ st =[ p₆ ]=> st'; constructor mp st:Statest':State⊢ (st =[ p₅ ]=> st') → st =[ p₆ ]=> st'mpr st:Statest':State⊢ (st =[ p₆ ]=> st') → st =[ p₅ ]=> st' <;> mp st:Statest':State⊢ (st =[ p₅ ]=> st') → st =[ p₆ ]=> st'mpr st:Statest':State⊢ (st =[ p₆ ]=> st') → st =[ p₅ ]=> st' intro h mpr st:Statest':Stateh:st =[ p₆ ]=> st'⊢ st =[ p₅ ]=> st'
· mp st:Statest':Stateh:st =[ p₅ ]=> st'⊢ st =[ p₆ ]=> st' apply p₅_summary at h mp st:Statest':Stateh:st' = X →ₜ 1 ; st⊢ st =[ p₆ ]=> st'; subst_vars mp st:State⊢ st =[ p₆ ]=> X →ₜ 1 ; st
constructor mp st:State⊢ Aexp.eval st (aexp {1}) = 1; rfl All goals completed! 🐙
· mpr st:Statest':Stateh:st =[ p₆ ]=> st'⊢ st =[ p₅ ]=> st' inversion h with
| asgn n h =>
simp only [Aexp.eval_num] at h asgn st:Staten:Nath:1 = n⊢ st =[ p₅ ]=> X →ₜ n ; st; rw [←h asgn st:Staten:Nath:1 = n⊢ st =[ p₅ ]=> X →ₜ 1 ; st] asgn st:Staten:Nath:1 = n⊢ st =[ p₅ ]=> X →ₜ 1 ; st
by_cases hx : st[X] = 1 pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ p₅ ]=> X →ₜ 1 ; stneg st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ st =[ p₅ ]=> X →ₜ 1 ; st
· pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ p₅ ]=> X →ₜ 1 ; st rw [←hx, pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ p₅ ]=> X →ₜ st[X] ; st TotalMap.update_same pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ p₅ ]=> st] pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ st =[ p₅ ]=> st
apply Com.EvalR.whileFalse pos st:Staten:Nath:1 = nhx:st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = false; simp_all All goals completed! 🐙
· neg st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ st =[ p₅ ]=> X →ₜ 1 ; st apply Com.EvalR.whileTrue (st' := X →ₜ 1 ; st) neg.hb st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = trueneg.hc st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {havoc X}.EvalR st (X →ₜ 1 ; st)neg.hloop st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {while (X ≠ 1) {havoc X}}.EvalR (X →ₜ 1 ; st) (X →ₜ 1 ; st)
· neg.hb st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval st (bexp {X ≠ 1}) = true simp_all All goals completed! 🐙
· neg.hc st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {havoc X}.EvalR st (X →ₜ 1 ; st) constructor All goals completed! 🐙
· neg.hloop st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ imp {while (X ≠ 1) {havoc X}}.EvalR (X →ₜ 1 ; st) (X →ₜ 1 ; st) apply Com.EvalR.whileFalse neg.hloop st:Staten:Nath:1 = nhx:¬st[X] = 1⊢ Bexp.eval (X →ₜ 1 ; st) (bexp {X ≠ 1}) = false; simp_all All goals completed! 🐙
end Himp
4.6. Additional Exercises
(Hint: You may or may not - depending on how you approach it - need
to use ext explicitly for this one.)
theorem swap_noninterfering_assignments (l₁ l₂ : Ident) (a₁ a₂ : Aexp)
(hl : l₁ ≠ l₂)
(h₁ : VarNotUsedInAexp l₁ a₂)
(h₂ : VarNotUsedInAexp l₂ a₁) :
imp { l₁ := a₁; l₂ := a₂ } ≃ imp { l₂ := a₂; l₁ := a₁ } := by l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁⊢ imp {l₁ := a₁; l₂ := a₂} ≃ imp {l₂ := a₂; l₁ := a₁}
solution!
have hs : ∀ {l₁ l₂ : Ident} {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' := by
intro l₁ l₂ a₁ a₂ hneq hne₁ hne₂ st st' h l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Statest':Stateh:st =[ l₁ := a₁; l₂ := a₂ ]=> st'⊢ st =[ l₂ := a₂; l₁ := a₁ ]=> st'
inversion h with
| seq h₁ h₂ =>
inversion h₂ asgn l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁✝:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Statest'✝:Stateh₁:imp {l₁ := a₁}.EvalR st st'✝n✝:Nath✝:Aexp.eval st'✝ a₂ = n✝⊢ st =[ l₂ := a₂; l₁ := a₁ ]=> l₂ →ₜ n✝ ; st'✝; inversion h₁ asgn l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Staten✝¹:Natn✝:Nath✝¹:Aexp.eval st a₁ = n✝h✝:Aexp.eval (l₁ →ₜ n✝ ; st) a₂ = n✝¹⊢ st =[ l₂ := a₂; l₁ := a₁ ]=> l₂ →ₜ n✝¹ ; l₁ →ₜ n✝ ; st; subst_vars asgn l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ st =[ l₂ := a₂; l₁ := a₁ ]=> l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; l₁ →ₜ Aexp.eval st a₁ ; st
constructor asgn.h₁ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ imp {l₂ := a₂}.EvalR st ?asgn.st'asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ imp {l₁ := a₁}.EvalR ?asgn.st' (l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; l₁ →ₜ Aexp.eval st a₁ ; st)asgn.st' l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ State; constructor asgn.h₁.h l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ Aexp.eval st a₂ = ?asgn.h₁.nasgn.h₁.n l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ Natasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ imp {l₁ := a₁}.EvalR (l₂ →ₜ ?asgn.h₁.n ; st)
(l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; l₁ →ₜ Aexp.eval st a₁ ; st); rfl asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ imp {l₁ := a₁}.EvalR (l₂ →ₜ Aexp.eval st a₂ ; st)
(l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; l₁ →ₜ Aexp.eval st a₁ ; st)
simp only [Com.evalR_eq] asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=>
l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; l₁ →ₜ Aexp.eval st a₁ ; st
rw [TotalMap.update_permute asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=>
l₁ →ₜ Aexp.eval st a₁ ; l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; stasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁] asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=>
l₁ →ₜ Aexp.eval st a₁ ; l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; stasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁
have heq : a₂.eval (l₁ →ₜ Aexp.eval st a₁ ; st) = a₂.eval st := by l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁⊢ imp {l₁ := a₁; l₂ := a₂} ≃ imp {l₂ := a₂; l₁ := a₁}
apply Aexp.eval_weakening l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ VarNotUsedInAexp l₁ a₂; assumption asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Stateheq:Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval st a₂⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=>
l₁ →ₜ Aexp.eval st a₁ ; l₂ →ₜ Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ ; stasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁
rw [heq asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Stateheq:Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval st a₂⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=> l₁ →ₜ Aexp.eval st a₁ ; l₂ →ₜ Aexp.eval st a₂ ; stasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁] asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Stateheq:Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval st a₂⊢ (l₂ →ₜ Aexp.eval st a₂ ; st) =[ l₁ := a₁ ]=> l₁ →ₜ Aexp.eval st a₁ ; l₂ →ₜ Aexp.eval st a₂ ; stasgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁; constructor asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Stateheq:Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval st a₂⊢ Aexp.eval (l₂ →ₜ Aexp.eval st a₂ ; st) a₁ = Aexp.eval st a₁asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁; apply Aexp.eval_weakening asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:Stateheq:Aexp.eval (l₁ →ₜ Aexp.eval st a₁ ; st) a₂ = Aexp.eval st a₂⊢ VarNotUsedInAexp l₂ a₁asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁; assumption asgn.h₂ l₁✝:Identl₂✝:Identa₁✝:Aexpa₂✝:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁l₁:Identl₂:Identa₁:Aexpa₂:Aexphneq:l₁ ≠ l₂hne₁:VarNotUsedInAexp l₁ a₂hne₂:VarNotUsedInAexp l₂ a₁st:State⊢ l₂ ≠ l₁; lia l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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₁}
intro st st' l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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'; constructor mp l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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'mpr l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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' <;> mp l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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'mpr l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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' intro h mpr l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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'
· mp l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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 hl h₁ h₂ h All goals completed! 🐙
· mpr l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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 (by l₁:Identl₂:Identa₁:Aexpa₂:Aexphl:l₁ ≠ l₂h₁:VarNotUsedInAexp l₁ a₂h₂:VarNotUsedInAexp l₂ a₁hs:∀ {l₁ l₂ : Ident} {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₁ lia All goals completed! 🐙) h₂ h₁ h
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₂
}
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₃) := by ⊢ ¬Approx c₃ c₄ ∧ ¬Approx c₄ c₃
solution!
constructor left ⊢ ¬Approx c₃ c₄right ⊢ ¬Approx c₄ c₃ <;> left ⊢ ¬Approx c₃ c₄right ⊢ ¬Approx c₄ c₃ intro contra right contra:Approx c₄ c₃⊢ False
· left contra:Approx c₃ c₄⊢ False have h : ∅ =[ c₃ ]=> (X →ₜ 1) := by ⊢ ¬Approx c₃ c₄ ∧ ¬Approx c₄ c₃
constructor contra:Approx c₃ c₄⊢ Aexp.eval ∅ (aexp {1}) = 1; simp left contra:Approx c₃ c₄h:∅ =[ c₃ ]=> X →ₜ 1⊢ False
apply contra at h left contra:Approx c₃ c₄h:∅ =[ c₄ ]=> X →ₜ 1⊢ False
inversion h with
| asgn n h _ h' =>
have hx := congrFun h' X asgn 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
rw [←h asgn 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] at hx asgn 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
simp at hx All goals completed! 🐙
· right contra:Approx c₄ c₃⊢ False have h : ∅ =[ c₄ ]=> (X →ₜ 2) := by ⊢ ¬Approx c₃ c₄ ∧ ¬Approx c₄ c₃
constructor contra:Approx c₄ c₃⊢ Aexp.eval ∅ (aexp {2}) = 2; simp right contra:Approx c₄ c₃h:∅ =[ c₄ ]=> X →ₜ 2⊢ False
apply contra at h right contra:Approx c₄ c₃h:∅ =[ c₃ ]=> X →ₜ 2⊢ False
inversion h with
| asgn n h _ h' =>
have hx := congrFun h' X asgn 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
rw [←h asgn 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] at hx asgn 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
simp at hx 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 := by c:Com⊢ Approx cMin c
solution!
intro st st' h c:Comst:Statest':Stateh:st =[ cMin ]=> st'⊢ st =[ c ]=> st'
apply loop_never_stops at h c:Comst:Statest':Stateh:False⊢ st =[ c ]=> st'
contradiction 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' := by c:Comc':Comhc:zprop cha:Approx c c'⊢ zprop c'
solution!
rw [zprop c:Comc':Comhc:∀ (st : State), ∃ st', st =[ c ]=> st'ha:Approx c c'⊢ ∀ (st : State), ∃ st', st =[ c' ]=> st'] at * c:Comc':Comhc:∀ (st : State), ∃ st', st =[ c ]=> st'ha:Approx c c'⊢ ∀ (st : State), ∃ st', st =[ c' ]=> st'
intro st c:Comc':Comhc:∀ (st : State), ∃ st', st =[ c ]=> st'ha:Approx c c'st:State⊢ ∃ st', st =[ c' ]=> st'
specialize hc st c:Comc':Comha:Approx c c'st:Statehc:∃ st', st =[ c ]=> st'⊢ ∃ st', st =[ c' ]=> st'
obtain ⟨st', h⟩ := hc c:Comc':Comha:Approx c c'st:Statest':Stateh:st =[ c ]=> st'⊢ ∃ st', st =[ c' ]=> st'
apply ha at h c:Comc':Comha:Approx c c'st:Statest':Stateh:st =[ c' ]=> st'⊢ ∃ st', st =[ c' ]=> st'; exists st' All goals completed! 🐙