Example Correctness Proof Involving a Subprogram

Consider the following subprogram, maximum(), which determines the larger of two input parameters passed to it and assigns that value to its lone output parameter.

procedure maximum(in first : int, second : int; out result : int);
|[ {pre: first = A  ∧  second = B}
   if first ≥ second —> result := first;
   [] first ≤ second —> result := second;
   fi
   {post: result = A max B}
]|

Note that maximum() is proper (using Denman's term) in that no input parameters are mentioned in its post-condition and no output parameters are mentioned in its pre-condition. (Input/output parameters, of which there are none in this example, may be mentioned in both pre- and post-conditions.)

Given that maximum() is proper, to show that it is valid requires only that we prove the Hoare triple {pre} S {post}, where S is its body. Using the following definition of the max operator, this is easy (and so we omit it).

(x = y max z) = ((x=y ∧ x≥z) ∨ (x=z ∧ x≥y))

Below is a program that uses invocations to maximum() to help it identify the largest element in an array. We focus on those aspects of its correctness proof that involve this subprogram invocation.

|[ con f : array of int;  { #f>0 }
   var big : int;
   var n : int;
   n, big := 1, f.0;
   { loop invariant I: big = (MAX k | 0≤k<n : f.k)  ∧  0≤n≤#f }
   { bound function t: #f-n }
   do n ≠ #f —>
      {I ∧ n≠#f}
      maximum(big, f.n, big);
      {I(n:=n+1)}
      n := n+1;
      {I}
   od
   {Q: big = (MAX k | 0≤k<#f : f.k)}
]|

As this program is a loop preceded by initialization, we prove its correctness by showing each item in the loop checklist. The only items involving maximum() are (ii) and (v).

Proof of (ii): {I ∧ n≠#f} maximum(big, f.n, big); n:=n+1 {I}

Applying the Hoare Triple Catenation Law and the wp Assignment Law, this reduces (as suggested by the annotations in the code) to showing

{I ∧ n!=#f} maximum(big, f.n, big) {I(n:=n+1)}

Supplying the details of I and applying textual substitution, we get the Hoare Triple

{big = (MAX k | 0≤k<n : f.k)  ∧  0≤n≤#f  ∧  n!=#f }
maximum(big, f.n, big)
{big = (MAX k | 0≤k<n+1 : f.k)  ∧  0≤n+1≤#f } 

The second and third conjuncts of the pre-condition simplify to 0≤n<#f. Using this fact, together with

{P} S {Q0 ∧ Q1}   =   {P} S {Q0} ∧ {P} S {Q1}

(which is the Law of Conjunctivity), we claim that it suffices to prove the two Hoare Triples

{big = (MAX k | 0≤k<n : f.k)  ∧  0≤n<#f }
maximum(big, f.n, big)
{0≤n+1≤#f}
and
{big = (MAX k | 0≤k<n : f.k)  ∧  0≤n<#f} 
maximum(big, f.n, big)
{big = (MAX k | 0≤k<n+1 : f.k)} 

Consider the first of these. From the fact that the invocation maximum(big,f.n,big) is independent of n and #f (i.e., changes neither of their values), the Independence Theorem tells us that

{ 0≤n+1≤#f } maximum(big, f.n, big) { 0≤n+1≤#f }

is valid. But we obtain the first Hoare Triple from this one by strengthening its pre-condition. Hence (by the Strengthening the Precondition Law), the first Hoare Triple is valid.

As for the second Hoare Triple, if we apply Split off Term to the post-condition (justified by the fact that the pre-condition guarantees that 0≤k<n+1 is a non-empty range), we get

{big = (MAX k | 0≤k<n : f.k)  ∧  0≤n<#f} 
maximum(big, f.n, big)
{big = (MAX k | 0≤k<n : f.k) max f.n} 

Denman's Procedure Call Rule says that

{ pre(first, second := big, f.n)(A,B := e0,e1) }
maximum(big, f.n, big)
{ post(result := big)(A,B := e0,e1) } 

is valid, for any choice of expressions e0 and e1 that are independent of the invocation maximum(big, f.n, big). Supplying the details for pre and post, this becomes

{ (first=A ∧ second=B)(first, second := big, f.n)(A,B := e0,e1) }
maximum(big, f.n, big)
{ (result = A max B)(result := big)(A,B := e0,e1) }

which, applying textual substitution, is

{ (big=A ∧ f.n=B)(A,B := e0,e1) }
maximum(big, f.n, big)
{ (big = A max B)(A,B := e0,e1) } 
Taking e0 to be (MAX k | 0≤k<n : f.k) and e1 to be f.n, we get (after applying textual substitution)

{ (big = (MAX k | 0≤k<n : f.k)  ∧  f.n = f.n) }
maximum(big, f.n, big)
{ (big = (MAX k | 0≤k<n : f.k) max f.n) } 

The Hoare Triple we seek to prove is obtained from this one by strengthening its pre-condition. Hence, by the Strengthening Precondition Law, we're done.


Proof of (v) {I ∧ n≠#f ∧ #f-n = T} maximum(big, f.n, big); n:=n+1 {#f-n < T}

For reasons that should not need to be explicitly pointed out, this reduces to showing

{I ∧ n≠#f ∧ #f-n = T} maximum(big, f.n, big) {#f-(n+1) < T}

By the Independence Theorem (and the fact that the invocation maximum(big, f.n, big) is independent of both n and #f), the Hoare Triple

{#f-(n+1) < T} maximum(big, f.n, big) {#f-(n+1) < T}

is valid. But the Hoare Triple we seek to show is obtained from this one by strengthening its pre-condition and hence (by the Strengthening the Precondition Law) is also valid.


Appendix

Independence Theorem: If S is independent of P, meaning that execution of S cannot possibly change the value of any variable mentioned in P, then the Hoare Triple {P} S {P} is valid.