Develop a program to satisfy the following specification.
|[ con N : int; { N > 0 }
con F : array [0..N) of int;
var h : array [0..N) of int;
S
{(∀k | 0≤k<N : F.k = (+i | 0≤i≤k : h.i)) |
]|
We strengthen the postcondition by replacing the constant N by the fresh variable n and adding the conjunct n=N. Then we take the first conjunct as a possible loop invariant and the negation of the second as the guard of the loop. It would appear that, as is often the case, the variable n should be incremented during each loop iteration. In addition, we probably need to modify h on each iteration. This gives rise to
|[ con N : int; { N > 0 }
con F : array [0..N) of int;
var h : array [0..N) of int;
var n : int;
n,h := D, E;
{invariant P: (∀k | 0≤k<n : F.k = (+i | 0≤i≤k : h.i)) ∧ 0≤n≤N }
do n ≠ N →
n,h := n+1,G;
od
{(∀k | 0≤k<N : F.k = (+i | 0≤i≤k : h.i)) }
]|
To make initialization easy, we could take D to be 0. That would truthify P and make initialization of h unnecessary. If we were to take D to be 1, then to truthify P we would have to initialize h to h(0:F.0). (That is, we would have to copy the value of F.0 into h.0.)
As for G, let us attempt to derive it by showing (ii) of the loop checklist:
{P ∧ B} n,h := n+1,G {P} (i.e., [P ∧ B ⇒ wp.(n,h := n+1,G).P] )
wp.(n,h := n+1,G).P
= < defn of P, wp assignment law, textual sub >
(∀k | 0≤k<n+1 : F.k = (+i | 0≤i≤k : G.i)) ∧ 0≤n+1≤N
= < 2nd conjunct is implied by assumption 0≤n≤N ∧ n≠N, (3.39) >
(∀k | 0≤k<n+1 : F.k = (+i | 0≤i≤k : G.i))
= < split off term (8.23) >
(∀k | 0≤k<n : F.k = (+i | 0≤i≤k : G.i)) ∧ F.n = (+i | 0≤i≤n : G.i)
= < assume that G satisfies G.i = h.i for i satisfying 0≤i<n >
(∀k | 0≤k<n : F.k = (+i | 0≤i≤k : h.i)) ∧ F.n = (+i | 0≤i≤n : G.i)
= < assumption P, (3.39) >
F.n = (+i | 0≤i≤n : G.i)
= < assume n>0, and split off term (8.23) >
F.n = (+i | 0≤i<n : G.i) + G.n
= < assume (again) that G satisfies G.i = h.i for i satisfying 0≤i<n >
F.n = (+i | 0≤i<n : h.i) + G.n
At this point, we notice that the first term on the right-hand side looks very much like a subexpression of the first conjunct of our loop invariant. It would look even more similar if we rewrote the inequality i<n as i≤n-1, so we do that now:
F.n = (+i | 0≤i<n : h.i) + G.n
= < rewrite inequality i<n as i≤n-1 >
F.n = (+i | 0≤i≤n-1 : h.i) + G.n
The first conjunct of the loop invariant (which we have taken as an
assumption, you will recall) says that
F.k = (+i | 0≤i≤k : h.i) for every k from zero
up to and including n-1.
In particular, then, by plugging in n-1 for k we get
F.(n-1) = (+i | 0≤i≤n-1 : h.i). Note that we need
n>0 to do this. To justify this step technically, we use
a slightly more general form of Gries's and Schneider's (9.13) Instantiation
theorem, which we call (9.13') and can be found in the Appendix below.
Continuing the proof:
F.n = (+i | 0≤i≤n-1 : h.i) + G.n
= < assumptions P (the loop invariant) and n > 0, and application
of (9.13') (see Appendix), instantiated by
x: k,
e: n-1,
R: 0≤k<n, and
P: F.k = (+i | 0≤i≤k : h.i) >
F.n = F.(n-1) + G.n
= < algebra >
G.n = F.n - F.(n-1)
= < choose G to be h(n : F.n - F.(n-1)), so that G.n = F.n - F.(n-1)
and so that earlier assumption that G.i = h.i for i satisfying
0≤i<n also holds >
true
Recall that during the derivation above, we assumed that n>0. Hence, our initialization of n should be n:=1 rather than n:=0. The assignment h := h(n : F.n - F.(n-1)) is usually written h.n := F.n - F.(n-1). The program is
|[ con N : int; { N > 0 }
con F : array [0..N) of int;
var h : array [0..N) of int;
var n : int;
n,h.0 := 1,F.0;
{invariant P: (∀k | 0≤k<n : F.k = (+i | 0≤i≤k : h.i)) ∧ 0≤n≤N }
do n ≠ N →
n, h.n := n+1, F.n - F.(n-1);
od
{(∀k | 0≤k<N : F.k = (+i | 0≤i≤k : h.i)) }
]|
Had we recognized from the start that the only modification needing to be made to h on each iteration were to the element h.n, we would have started with our loop body being the assignment
This would have made the calculation of G somewhat easier.
In Gries & Schneider, Theorem 9.13 (Instantiation) says
Informally, this says that, if every choice of x satisfies P, then e satisfies P.
A somewhat more general form of this theorem is as follows:
Theorem 9.13': R(x:=e) ⇒ ((∀x | R : P) ⇒ P(x:=e))
It is readily seen that (9.13) is obtained from (9.13') by taking R to be true (and then applying (3.73)).
Informally, (9.13') says that, if e satisfies R and P is satisfied by every x satisfying R, then e satisfies P.
Here is a proof of (9.13').
Assume R(x:=e).
(∀x | R : P)
= < (9.2) Trading >
(∀x |: R ⇒ P)
⇒ < (9.13) Instantiation >
(R ⇒ P)(x:=e)
= < textual substitution distributes over operators >
R(x:=e) ⇒ P(x:=e)
= < assumption R(x:=e) >
true ⇒ P(x:=e)
= < (3.73) true is left identity of ⇒ >
P(x:=e)