Hoare21Hoare Logic, Part 2.1
Set Warnings "-notation-overridden,-parsing,-deprecated-hint-without-locality".
Set Warnings "-intuition-auto-with-star".
From Coq Require Import Strings.String.
From PLF Require Import Maps.
From Coq Require Import Bool.
From Coq Require Import Arith.
From Coq Require Import EqNat.
From Coq Require Import PeanoNat. Import Nat.
From Coq Require Import Lia.
From PLF Require Export Imp.
From PLF Require Export Hoare12.
Set Default Goal Selector "!".
Definition FILL_IN_HERE := <{True}>.
Set Warnings "-intuition-auto-with-star".
From Coq Require Import Strings.String.
From PLF Require Import Maps.
From Coq Require Import Bool.
From Coq Require Import Arith.
From Coq Require Import EqNat.
From Coq Require Import PeanoNat. Import Nat.
From Coq Require Import Lia.
From PLF Require Export Imp.
From PLF Require Export Hoare12.
Set Default Goal Selector "!".
Definition FILL_IN_HERE := <{True}>.
Decorated Programs
X := m;
Z := p;
while X ≠ 0 do
Z := Z - 1;
X := X - 1
end Here is one possible specification for this program, in the form of a Hoare triple:
{{ True }}
X := m;
Z := p;
while X ≠ 0 do
Z := Z - 1;
X := X - 1
end
{{ Z = p - m }} (Note the parameters m and p, which stand for fixed-but-arbitrary numbers. Formally, they are simply Coq variables of type nat.)
{{ True }} ->>
{{ m = m }}
X := m
{{ X = m }} ->>
{{ X = m ∧ p = p }};
Z := p;
{{ X = m ∧ Z = p }} ->>
{{ Z - X = p - m }}
while X ≠ 0 do
{{ Z - X = p - m ∧ X ≠ 0 }} ->>
{{ (Z - 1) - (X - 1) = p - m }}
Z := Z - 1
{{ Z - (X - 1) = p - m }};
X := X - 1
{{ Z - X = p - m }}
end
{{ Z - X = p - m ∧ ¬(X ≠ 0) }} ->>
{{ Z = p - m }}
Example: Swapping
X := X + Y;
Y := X - Y;
X := X - Y We can give a proof, in the form of decorations, that this program is correct -- i.e., it really swaps X and Y -- as follows.
(1) {{ X = m ∧ Y = n }} ->>
(2) {{ (X + Y) - ((X + Y) - Y) = n ∧ (X + Y) - Y = m }}
X := X + Y
(3) {{ X - (X - Y) = n ∧ X - Y = m }};
Y := X - Y
(4) {{ X - Y = n ∧ Y = m }};
X := X - Y
(5) {{ X = n ∧ Y = m }}
- We begin with the undecorated program (the unnumbered lines).
- We add the specification -- i.e., the outer precondition (1)
and postcondition (5). In the precondition, we use parameters
m and n to remember the initial values of variables X
and Y so that we can refer to them in the postcondition (5).
- We work backwards, mechanically, starting from (5) and proceeding until we get to (2). At each step, we obtain the precondition of the assignment from its postcondition by substituting the assigned variable with the right-hand-side of the assignment. For instance, we obtain (4) by substituting X with X - Y in (5), and we obtain (3) by substituting Y with X - Y in (4).
Example: Simple Conditionals
(1) {{ True }}
if X ≤ Y then
(2) {{ True ∧ X ≤ Y }} ->>
(3) {{ (Y - X) + X = Y ∨ (Y - X) + Y = X }}
Z := Y - X
(4) {{ Z + X = Y ∨ Z + Y = X }}
else
(5) {{ True ∧ ~(X ≤ Y) }} ->>
(6) {{ (X - Y) + X = Y ∨ (X - Y) + Y = X }}
Z := X - Y
(7) {{ Z + X = Y ∨ Z + Y = X }}
end
(8) {{ Z + X = Y ∨ Z + Y = X }} These decorations can be constructed as follows:
- We start with the outer precondition (1) and postcondition (8).
- Following the format dictated by the hoare_if rule, we copy the
postcondition (8) to (4) and (7). We conjoin the precondition (1)
with the guard of the conditional to obtain (2). We conjoin (1)
with the negated guard of the conditional to obtain (5).
- In order to use the assignment rule and obtain (3), we substitute
Z by Y - X in (4). To obtain (6) we substitute Z by X - Y
in (7).
- Finally, we verify that (2) implies (3) and (5) implies (6). Both of these implications crucially depend on the ordering of X and Y obtained from the guard. For instance, knowing that X ≤ Y ensures that subtracting X from Y and then adding back X produces Y, as required by the first disjunct of (3). Similarly, knowing that ¬ (X ≤ Y) ensures that subtracting Y from X and then adding back Y produces X, as needed by the second disjunct of (6). Note that n - m + m = n does not hold for arbitrary natural numbers n and m (for example, 3 - 5 + 5 = 5).
Exercise: 2 stars, standard (if_minus_plus_reloaded)
Fill in valid decorations for the following program:
(*
{{ True }}
if X <= Y then
{{ }} ->>
{{ }}
Z := Y - X
{{ }}
else
{{ }} ->>
{{ }}
Y := X + Z
{{ }}
end
{{ Y = X + Z }}
*)
{{ True }}
if X <= Y then
{{ }} ->>
{{ }}
Z := Y - X
{{ }}
else
{{ }} ->>
{{ }}
Y := X + Z
{{ }}
end
{{ Y = X + Z }}
*)
Briefly justify each use of ->>.
(* Do not modify the following line: *)
Definition manual_grade_for_if_minus_plus_reloaded : option (nat×string) := None.
☐
Definition manual_grade_for_if_minus_plus_reloaded : option (nat×string) := None.
☐
Example: Reduce to Zero
(1) {{ True }}
while X ≠ 0 do
(2) {{ True ∧ X ≠ 0 }} ->>
(3) {{ True }}
X := X - 1
(4) {{ True }}
end
(5) {{ True ∧ ~(X ≠ 0) }} ->>
(6) {{ X = 0 }} The decorations can be constructed as follows:
- Start with the outer precondition (1) and postcondition (6).
- Following the format dictated by the hoare_while rule, we copy
(1) to (4). We conjoin (1) with the guard to obtain (2). We also
conjoin (1) with the negation of the guard to obtain (5).
- Because the final postcondition (6) does not syntactically match (5),
we add an implication between them.
- Using the assignment rule with assertion (4), we trivially substitute
and obtain assertion (3).
- We add the implication between (2) and (3).
Example: Division
X := m;
Y := 0;
while n ≤ X do
X := X - n;
Y := Y + 1
end; If we replace m and n by concrete numbers and execute the program, it will terminate with the variable X set to the remainder when m is divided by n and Y set to the quotient.
(1) {{ True }} ->>
(2) {{ n × 0 + m = m }}
X := m;
(3) {{ n × 0 + X = m }}
Y := 0;
(4) {{ n × Y + X = m }}
while n ≤ X do
(5) {{ n × Y + X = m ∧ n ≤ X }} ->>
(6) {{ n × (Y + 1) + (X - n) = m }}
X := X - n;
(7) {{ n × (Y + 1) + X = m }}
Y := Y + 1
(8) {{ n × Y + X = m }}
end
(9) {{ n × Y + X = m ∧ ¬(n ≤ X) }} ->>
(10) {{ n × Y + X = m ∧ X < n }} Assertions (4), (5), (8), and (9) are derived mechanically from the loop invariant and the loop's guard. Assertions (8), (7), and (6) are derived using the assignment rule going backwards from (8) to (6). Assertions (4), (3), and (2) are again backwards applications of the assignment rule.
- (1) ->> (2): trivial, by algebra.
- (5) ->> (6): because n ≤ X, we are guaranteed that the subtraction in (6) does not get zero-truncated. We can therefore rewrite (6) as n × Y + n + X - n and cancel the ns, which results in the left conjunct of (5).
- (9) ->> (10): if ¬ (n ≤ X) then X < n. That's straightforward from high-school algebra.
Exercise: 3 stars, standard (slow_assignment)
A roundabout way of assigning a number currently stored in X to the variable Y is to start Y at 0, then decrement X until it hits 0, incrementing Y at each step. Here is a program that implements this idea. Fill in valid decorations.
(*
forall m.
{{ X = m }}
Y := 0;
{{ }} ->>
{{ }}
while X <> 0 do
{{ }} ->>
{{ }}
X := X - 1;
{{ }}
Y := Y + 1
{{ }}
end
{{ }} ->>
{{ Y = m }}
*)
(* Do not modify the following line: *)
Definition manual_grade_for_slow_assignment : option (nat×string) := None.
☐
forall m.
{{ X = m }}
Y := 0;
{{ }} ->>
{{ }}
while X <> 0 do
{{ }} ->>
{{ }}
X := X - 1;
{{ }}
Y := Y + 1
{{ }}
end
{{ }} ->>
{{ Y = m }}
*)
(* Do not modify the following line: *)
Definition manual_grade_for_slow_assignment : option (nat×string) := None.
☐
From Decorated Programs to Formal Proofs
Definition reduce_to_zero : com :=
<{ while X ≠ 0 do
X := X - 1
end }>.
Theorem reduce_to_zero_correct' :
{{True}}
reduce_to_zero
{{X = 0}}.
Proof.
unfold reduce_to_zero.
(* First we need to transform the postcondition so
that hoare_while will apply. *)
eapply hoare_consequence_post.
- apply hoare_while.
+ (* Loop body preserves loop invariant *)
(* Massage precondition so hoare_asgn applies *)
eapply hoare_consequence_pre.
× apply hoare_asgn.
× (* Proving trivial implication (2) ->> (3) *)
unfold assertion_sub, "->>". simpl. intros.
exact I.
- (* Loop invariant and negated guard imply post *)
intros st [Inv GuardFalse].
unfold bassertion in GuardFalse. simpl in GuardFalse.
rewrite not_true_iff_false in GuardFalse.
rewrite negb_false_iff in GuardFalse.
apply eqb_eq in GuardFalse.
apply GuardFalse.
Qed.
<{ while X ≠ 0 do
X := X - 1
end }>.
Theorem reduce_to_zero_correct' :
{{True}}
reduce_to_zero
{{X = 0}}.
Proof.
unfold reduce_to_zero.
(* First we need to transform the postcondition so
that hoare_while will apply. *)
eapply hoare_consequence_post.
- apply hoare_while.
+ (* Loop body preserves loop invariant *)
(* Massage precondition so hoare_asgn applies *)
eapply hoare_consequence_pre.
× apply hoare_asgn.
× (* Proving trivial implication (2) ->> (3) *)
unfold assertion_sub, "->>". simpl. intros.
exact I.
- (* Loop invariant and negated guard imply post *)
intros st [Inv GuardFalse].
unfold bassertion in GuardFalse. simpl in GuardFalse.
rewrite not_true_iff_false in GuardFalse.
rewrite negb_false_iff in GuardFalse.
apply eqb_eq in GuardFalse.
apply GuardFalse.
Qed.
In Hoare we introduced a series of tactics named
assertion_auto to automate proofs involving assertions.
The following declaration introduces a more sophisticated tactic
that will help with proving assertions throughout the rest of this
chapter. You don't need to understand the details, but briefly:
it uses split repeatedly to turn all the conjunctions into
separate subgoals, tries to use several theorems about booleans
and (in)equalities, then uses eauto and lia to finish off as
many subgoals as possible. What's left after verify_assertion does
its thing should be just the "interesting parts" of the proof --
which, if we're lucky, might be nothing at all!
Ltac verify_assertion :=
repeat split;
simpl;
unfold assert_implies;
unfold ap in *; unfold ap2 in *;
unfold bassertion in *; unfold beval in *; unfold aeval in *;
unfold assertion_sub; intros;
repeat (simpl in *;
rewrite t_update_eq ||
(try rewrite t_update_neq;
[| (intro X; inversion X; fail)]));
simpl in *;
repeat match goal with [H : _ ∧ _ ⊢ _] ⇒
destruct H end;
repeat rewrite not_true_iff_false in *;
repeat rewrite not_false_iff_true in *;
repeat rewrite negb_true_iff in *;
repeat rewrite negb_false_iff in *;
repeat rewrite eqb_eq in *;
repeat rewrite eqb_neq in *;
repeat rewrite leb_iff in *;
repeat rewrite leb_iff_conv in *;
try subst;
simpl in *;
repeat
match goal with
[st : state ⊢ _] ⇒
match goal with
| [H : st _ = _ ⊢ _] ⇒
rewrite → H in *; clear H
| [H : _ = st _ ⊢ _] ⇒
rewrite <- H in *; clear H
end
end;
try eauto;
try lia.
repeat split;
simpl;
unfold assert_implies;
unfold ap in *; unfold ap2 in *;
unfold bassertion in *; unfold beval in *; unfold aeval in *;
unfold assertion_sub; intros;
repeat (simpl in *;
rewrite t_update_eq ||
(try rewrite t_update_neq;
[| (intro X; inversion X; fail)]));
simpl in *;
repeat match goal with [H : _ ∧ _ ⊢ _] ⇒
destruct H end;
repeat rewrite not_true_iff_false in *;
repeat rewrite not_false_iff_true in *;
repeat rewrite negb_true_iff in *;
repeat rewrite negb_false_iff in *;
repeat rewrite eqb_eq in *;
repeat rewrite eqb_neq in *;
repeat rewrite leb_iff in *;
repeat rewrite leb_iff_conv in *;
try subst;
simpl in *;
repeat
match goal with
[st : state ⊢ _] ⇒
match goal with
| [H : st _ = _ ⊢ _] ⇒
rewrite → H in *; clear H
| [H : _ = st _ ⊢ _] ⇒
rewrite <- H in *; clear H
end
end;
try eauto;
try lia.
This makes it pretty easy to verify reduce_to_zero:
Theorem reduce_to_zero_correct''' :
{{True}}
reduce_to_zero
{{X = 0}}.
Proof.
unfold reduce_to_zero.
eapply hoare_consequence_post.
- apply hoare_while.
+ eapply hoare_consequence_pre.
× apply hoare_asgn.
× verify_assertion.
- verify_assertion.
Qed.
{{True}}
reduce_to_zero
{{X = 0}}.
Proof.
unfold reduce_to_zero.
eapply hoare_consequence_post.
- apply hoare_while.
+ eapply hoare_consequence_pre.
× apply hoare_asgn.
× verify_assertion.
- verify_assertion.
Qed.
This example shows that it is conceptually straightforward to read
off the main elements of a formal proof from a decorated program.
Indeed, the process is so straightforward that it can be
automated, as we will see next.
Exercise: 2 stars, standard (division_example)
Provide a formal proof for the decorated program we developed in the lecture. (Note that this example is slightly different from the division example given in this book chapter.)
Example division_example : ∀ (m : nat),
{{ X = m }}
Y := 0;
while Z ≤ X do
X := X - Z;
Y := Y + 1
end
{{ Z × Y + X = m ∧ X < Z }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
{{ X = m }}
Y := 0;
while Z ≤ X do
X := X - Z;
Y := Y + 1
end
{{ Z × Y + X = m ∧ X < Z }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
Exercise: 2 stars, standard (slow_assignment_formal)
Turn the decorated program you provided for the slow_assignment exercise into a formal proof.
Example slow_assignment_formal : ∀ (m : nat),
{{ X = m }}
Y := 0;
while X ≠ 0 do
X := X - 1;
Y := Y + 1
end
{{ Y = m }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
{{ X = m }}
Y := 0;
while X ≠ 0 do
X := X - 1;
Y := Y + 1
end
{{ Y = m }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
Example: Parity
{{ X = m }}
while 2 ≤ X do
X := X - 2
end
{{ X = parity m }} The parity function used in the specification is defined in Coq as follows:
The postcondition does not hold at the beginning of the loop,
since m = parity m does not hold for an arbitrary m, so we
cannot hope to use that as a loop invariant. To find a loop invariant
that works, let's think a bit about what this loop does. On each
iteration it decrements X by 2, which preserves the parity of X.
So the parity of X does not change, i.e., it is invariant. The initial
value of X is m, so the parity of X is always equal to the
parity of m. Using parity X = parity m as an invariant we
obtain the following decorated program:
{{ X = m }} ->> (a - OK)
{{ parity X = parity m }}
while 2 ≤ X do
{{ parity X = parity m ∧ 2 ≤ X }} ->> (c - OK)
{{ parity (X-2) = parity m }}
X := X - 2
{{ parity X = parity m }}
end
{{ parity X = parity m ∧ ~(2 ≤ X) }} ->> (b - OK)
{{ X = parity m }} With this loop invariant, conditions (a), (b), and (c) are all satisfied. For verifying (b), we observe that, when X < 2, we have parity X = X (we can easily see this in the definition of parity). For verifying (c), we observe that, when 2 ≤ X, we have parity X = parity (X-2).
Hint: There are actually several possible loop invariants that all
lead to good proofs; one that leads to a particularly simple proof
is parity X = parity m -- or more formally, using the
ap operator to lift the application of the parity function
into the syntax of assertions, {{ ap parity X = parity m }}.
If you use the suggested loop invariant, you may find the following
lemmas helpful (as well as leb_complete and leb_correct).
{{ X = m }} ->> (a - OK)
{{ parity X = parity m }}
while 2 ≤ X do
{{ parity X = parity m ∧ 2 ≤ X }} ->> (c - OK)
{{ parity (X-2) = parity m }}
X := X - 2
{{ parity X = parity m }}
end
{{ parity X = parity m ∧ ~(2 ≤ X) }} ->> (b - OK)
{{ X = parity m }} With this loop invariant, conditions (a), (b), and (c) are all satisfied. For verifying (b), we observe that, when X < 2, we have parity X = X (we can easily see this in the definition of parity). For verifying (c), we observe that, when 2 ≤ X, we have parity X = parity (X-2).
Exercise: 3 stars, standard (parity_correct)
Translate the above informal decorated program into a formal proof.
Lemma parity_ge_2 : ∀ x,
2 ≤ x →
parity (x - 2) = parity x.
Lemma parity_lt_2 : ∀ x,
¬ 2 ≤ x →
parity x = x.
Theorem parity_correct : ∀ (m:nat),
{{ X = m }}
while 2 ≤ X do
X := X - 2
end
{{ X = parity m }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
2 ≤ x →
parity (x - 2) = parity x.
Proof.
destruct x; intros; simpl.
- reflexivity.
- destruct x; simpl.
+ lia.
+ rewrite sub_0_r. reflexivity.
Qed.
destruct x; intros; simpl.
- reflexivity.
- destruct x; simpl.
+ lia.
+ rewrite sub_0_r. reflexivity.
Qed.
Lemma parity_lt_2 : ∀ x,
¬ 2 ≤ x →
parity x = x.
Proof.
induction x; intros; simpl.
- reflexivity.
- destruct x.
+ reflexivity.
+ lia.
Qed.
induction x; intros; simpl.
- reflexivity.
- destruct x.
+ reflexivity.
+ lia.
Qed.
Theorem parity_correct : ∀ (m:nat),
{{ X = m }}
while 2 ≤ X do
X := X - 2
end
{{ X = parity m }}.
Proof.
(* FILL IN HERE *) Admitted.
☐
Formal Decorated Programs
Syntax
Module DComFirstTry.
Inductive dcom : Type :=
| DCSkip (P : Assertion)
(* {{ P }} skip {{ P }} *)
| DCSeq (P : Assertion) (d1 : dcom) (Q : Assertion)
(d2 : dcom) (R : Assertion)
(* {{ P }} d1 {{Q}}; d2 {{ R }} *)
| DCAsgn (X : string) (a : aexp) (Q : Assertion)
(* etc. *)
| DCIf (P : Assertion) (b : bexp) (P1 : Assertion) (d1 : dcom)
(P2 : Assertion) (d2 : dcom) (Q : Assertion)
| DCWhile (P : Assertion) (b : bexp)
(P1 : Assertion) (d : dcom) (P2 : Assertion)
(Q : Assertion)
| DCPre (P : Assertion) (d : dcom)
| DCPost (d : dcom) (Q : Assertion).
End DComFirstTry.
Inductive dcom : Type :=
| DCSkip (P : Assertion)
(* {{ P }} skip {{ P }} *)
| DCSeq (P : Assertion) (d1 : dcom) (Q : Assertion)
(d2 : dcom) (R : Assertion)
(* {{ P }} d1 {{Q}}; d2 {{ R }} *)
| DCAsgn (X : string) (a : aexp) (Q : Assertion)
(* etc. *)
| DCIf (P : Assertion) (b : bexp) (P1 : Assertion) (d1 : dcom)
(P2 : Assertion) (d2 : dcom) (Q : Assertion)
| DCWhile (P : Assertion) (b : bexp)
(P1 : Assertion) (d : dcom) (P2 : Assertion)
(Q : Assertion)
| DCPre (P : Assertion) (d : dcom)
| DCPost (d : dcom) (Q : Assertion).
End DComFirstTry.
But this would result in very verbose decorated programs with a
lot of repeated annotations: even a simple program like
skip;skip would be decorated like this,
{{P}} ({{P}} skip {{P}}) ; ({{P}} skip {{P}}) {{P}} with pre- and post-conditions around each skip, plus identical pre- and post-conditions on the semicolon!
In other words, we don't want both preconditions and
postconditions on each command, because a sequence of two commands
would contain redundant decorations--the postcondition of the
first likely being the same as the precondition of the second.
Instead, the formal syntax of decorated commands omits
preconditions whenever possible, trying to embed just the
postcondition.
Putting this all together gives us the formal syntax of decorated
commands:
{{P}} ({{P}} skip {{P}}) ; ({{P}} skip {{P}}) {{P}} with pre- and post-conditions around each skip, plus identical pre- and post-conditions on the semicolon!
- The skip command, for example, is decorated only with its
postcondition
skip {{ Q }} on the assumption that the precondition will be provided by the context.
- Sequences d1 ; d2 need no additional decorations.
- An assignment X := a is decorated only with its postcondition:
X := a {{ Q }}
- A conditional if b then d1 else d2 is decorated with a
postcondition for the entire statement, as well as preconditions
for each branch:
if b then {{ P1 }} d1 else {{ P2 }} d2 end {{ Q }}
- A loop while b do d end is decorated with its postcondition
and a precondition for the body:
while b do {{ P }} d end {{ Q }} The postcondition embedded in d serves as the loop invariant.
- Implications ->> can be added as decorations either for a
precondition
->> {{ P }} d or for a postcondition
d ->> {{ Q }} The former is waiting for another precondition to be supplied by the context (e.g., {{ P'}} ->> {{ P }} d); the latter relies on the postcondition already embedded in d.
Inductive dcom : Type :=
| DCSkip (Q : Assertion)
(* skip {{ Q }} *)
| DCSeq (d1 d2 : dcom)
(* d1 ; d2 *)
| DCAsgn (X : string) (a : aexp) (Q : Assertion)
(* X := a {{ Q }} *)
| DCIf (b : bexp) (P1 : Assertion) (d1 : dcom)
(P2 : Assertion) (d2 : dcom) (Q : Assertion)
(* if b then {{ P1 }} d1 else {{ P2 }} d2 end {{ Q }} *)
| DCWhile (b : bexp) (P : Assertion) (d : dcom)
(Q : Assertion)
(* while b do {{ P }} d end {{ Q }} *)
| DCPre (P : Assertion) (d : dcom)
(* ->> {{ P }} d *)
| DCPost (d : dcom) (Q : Assertion)
(* d ->> {{ Q }} *).
| DCSkip (Q : Assertion)
(* skip {{ Q }} *)
| DCSeq (d1 d2 : dcom)
(* d1 ; d2 *)
| DCAsgn (X : string) (a : aexp) (Q : Assertion)
(* X := a {{ Q }} *)
| DCIf (b : bexp) (P1 : Assertion) (d1 : dcom)
(P2 : Assertion) (d2 : dcom) (Q : Assertion)
(* if b then {{ P1 }} d1 else {{ P2 }} d2 end {{ Q }} *)
| DCWhile (b : bexp) (P : Assertion) (d : dcom)
(Q : Assertion)
(* while b do {{ P }} d end {{ Q }} *)
| DCPre (P : Assertion) (d : dcom)
(* ->> {{ P }} d *)
| DCPost (d : dcom) (Q : Assertion)
(* d ->> {{ Q }} *).
To provide the initial precondition that goes at the very top of a
decorated program, we introduce a new type decorated:
An example decorated program that decrements X to 0:
Example dec_while : decorated :=
<{
{{ True }}
while X ≠ 0
do
{{ True ∧ (X ≠ 0) }}
X := X - 1
{{ True }}
end
{{ True ∧ X = 0}} ->>
{{ X = 0 }} }>.
<{
{{ True }}
while X ≠ 0
do
{{ True ∧ (X ≠ 0) }}
X := X - 1
{{ True }}
end
{{ True ∧ X = 0}} ->>
{{ X = 0 }} }>.
Exercise: 2 stars, standard (parity_decorated_formal)
Turn the decorated program from the parity exercise into a formal decorated program.
Definition parity_dec (m:nat) : decorated :=
<{
{{ X = m }} ->>
{{ FILL_IN_HERE }}
while 2 ≤ X do
{{ FILL_IN_HERE }} ->>
{{ FILL_IN_HERE }}
X := X - 2
{{ FILL_IN_HERE }}
end
{{ FILL_IN_HERE }} ->>
{{ X = parity m }} }>.
(* Do not modify the following line: *)
Definition manual_grade_for_parity_decorated_formal : option (nat×string) := None.
☐
<{
{{ X = m }} ->>
{{ FILL_IN_HERE }}
while 2 ≤ X do
{{ FILL_IN_HERE }} ->>
{{ FILL_IN_HERE }}
X := X - 2
{{ FILL_IN_HERE }}
end
{{ FILL_IN_HERE }} ->>
{{ X = parity m }} }>.
(* Do not modify the following line: *)
Definition manual_grade_for_parity_decorated_formal : option (nat×string) := None.
☐
(* 2025-06-16 19:18 *)