Example: systematic construction of an integer square root program W. Drabent 13 May 2004 We want to construct a program computing the integer square root of a non-negative integer n. In other words, the program has to be totally correct w.r.t. precondition P = n ≥ 0 and postcondition Q = (x2 ≤ n < (x + 1)2 ). (The latter also says that x will contain the result). My intuition tells me that we should try to use a loop. So a skeleton of our program is { P } ?? ; while b do ?? { ⇓ Q }. (I will not write ⇓ below, but remember – we are dealing with total correctness). The most important thing in a loop is its invariant. A typical way of obtaining a loop invariant is to weaken the postcondition. It should be easy to obtain the invariant from the precondition and the postcondition from the invariant. In the previous example we constructed the invariant by removing n < (x + 1)2 from Q. Here we introduce a new variable y and take I = ( x2 ≤ n < y 2 ∧ 0 ≤ x < y, ) as the invariant.1 The intuition behind I is that the number we are going to compute is somewhere between x and y. It is good to remember the proof rule for while which we use: {b ∧ I ∧ t=z } S {⇓I ∧ t<z } { I } while b do S { ⇓ ¬b ∧ I } if I ⇒ t ≥ 0 (where t – the bound function – is an integer expression and z is a variable that does not appear in P, b, t or S). Q, I and the proof rule for while suggest the choice of b, as it is necessary that ¬b ∧ I implies Q. We achieve this by taking b = (y 6= x+1). P does non imply I, so the loop requires initialization. We are looking for a statement S0 satisfying { P } S0 { I }. It is easy to check that S0 = (x := 0; y := n+1) does the work. 1 Strictly speaking, I is not weaker than Q (in other words, Q does not imply I). We first strengthened Q by introducing y, obtaining Q1 = (x2 ≤ n < y 2 ∧ y = x + 1). This implies 0 ≤ x < y, so Q1 is equivalent to Q1 ∧ 0 ≤ x < y. Then we weakened the last formula by removing y = x + 1. 1 This is the current version of our program { P }(x := 0; y := n+1); { I } while b do (1) ({ b| ∧ I {z ∧ t = z} } S { I ∧ t < z }) { Q }. P0 Now we are looking for a suitable bound function t. It should be ≥ 0 and it should decrease at each repetition of the loop. Our intention is that the loop shrinks the interval [x..y) containing the solution until the solution is found. So a good candidate for t is the size of this interval: t = y − x. Note that the invariant indeed implies t ≥ 0. It remains to construct the loop body S satisfying the stated pre- and postcondition. S has to decrease t. We want to be efficient, so we use binary search. The future solution is somewhere between x and y. We halve the interval [x..y) and choose that half that preserves the invariant (in other words, contains the solution). As there are two cases, S is likely to be a conditional statement. Halving will be done by setting either x or y to the midpoint (x+y)÷2 (where ÷ is the integer division, e.g. 7 ÷ 2 = 3). We abbreviate (x+y)÷2 by A. Together we have S = if b0 then y := A else x {z A} | := | {z } S2 S1 The postcondition for both S1 and S2 is I ∧ t<z. The axiom [ass] gives 2 {x ≤ n < A2 ∧ 0 ≤{zx < A ∧ A−x < z} } y := A { I ∧ t<z } | (2) R1 2 2 { A ≤ n < y ∧ 0 ≤ A < y ∧ y−A < z } x := A { I ∧ t<z } | {z } R2 (3) We want to apply the rule [if], but this requires the preconditions to be of the form b0 ∧ R and ¬b0 ∧ R. So we are looking for such conditions implying, respectively, R1 and R2 . A good candidate for b0 is n < A2 , as n < A2 occurs in R1 and A2 ≤ n occurs in R2 ). Taking b0 = n < A2 and R = x2 ≤ n < y 2 ∧ 0 ≤ x < A < y ∧ A−x < z ∧ y−A < z we see that indeed b0 ∧ R implies R1 and ¬b0 ∧ R implies R2 . From (2), (3) by rule [cons] we obtain { b0 ∧ R } y := A { I ∧ t<z } { ¬b0 ∧ R } x := A { I ∧ t<z } 2 (4) (5) and then { R } S { I ∧ t<z } We are almost ready, but the required precondition for S is P0 (see (1)). We will show that P0 implies R, then applying [cons] gives { P0 } S { I ∧ t<z } and completes the construction of the program (1) and proof of its correctness. Back to the unproven implication P0 ⇒ R. Remember that P0 = y 6= x+1 ∧ x2 ≤ n < y 2 ∧ 0 ≤ x < y ∧ y−x = z. Compare this with R. You see that it remains to show that P0 implies x < A < y and A−x < z ∧ y−A < z. (This says that both parts [x..A) and [A..y), into which we divide the interval [x..y), are smaller than [x..y), whose length is z. This sounds obvious but, for instance, if y = x + 1 then A = x). From x < y and y 6= x+1 we have x+2 ≤ y. This implies2 x < A < y. From this and y − x = z we obtain3 the required inequalities. END OF THE PROOF & CONSTRUCTION Our work can be summarized in the following proof outline. {n ≥ 0} x := 0; y := n+1; { invariant: I } { boundfunction: y − x } while y 6= x + 1 do ( { b ∧ I ∧ y−x = z } | {z b } { x2 ≤ n < (x + 1)2 } | {z P0 } {R} if n < A2 then { n<A2 ∧ R } { R1 } y := A else { ¬(n<A2 ) ∧ R } { R2 } x := A { I ∧ y−x < z } ) 2 How? x + x + 2 ≤ x + y, hence x + 1 ≤ (x + y) ÷ 2 = A. Similarly, x + y ≤ y − 2 + y, hence A ≤ y − 1. 3 How? From A < y we have A − x < y − x = z. From x < A we have y − A < y − x = z. 3