ProgCorr LectureNotes
ProgCorr LectureNotes
ProgCorr LectureNotes
Program Correctness
Wim H. Hesselink
Gerard R. Renardel de Lavalette
1 Introduction 5
1.1 The meaning of program correctness . . . . . . . . . . . . . . . . . . . . . . 5
1.2 How to arrive at program correctness? . . . . . . . . . . . . . . . . . . . . . 5
1.3 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4 History of these lecture notes . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2 Logical-mathematical preparation 9
2.1 The specification language . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Strength of formulas and proof format . . . . . . . . . . . . . . . . . . . . . 11
2.3 Operators and their properties . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.1 Division with remainder . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.2 The counting operator . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.3.3 Maxima and minima of sets of numbers . . . . . . . . . . . . . . . . 12
2.3.4 Sums and products of sequences of numbers . . . . . . . . . . . . . . 13
2.3.5 Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3.6 The logical quantifiers . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3 Specifications 15
3.1 States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2 Programs and commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3 Hoare triples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4 Example of a specification . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.5 Preregular and postregular specifications . . . . . . . . . . . . . . . . . . . . 17
3.6 Unsatisfiable specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 Program constructs 21
4.1 Pseudocode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2 Proof rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5 Annotated commands 25
5.1 Two assignments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.1.1 A first problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.1.2 A second problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5.2 Sequential composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3 Conditional commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.1 A first problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3.2 A second problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.4 Additional rules for annotations . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3
6 Repetitions 35
6.1 Roadmap for the design of a repetition . . . . . . . . . . . . . . . . . . . . . 35
6.2 Example: exponentiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.3 Example: powers of 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6.4 Example: Euclid’s algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6.5 Active finalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
8 Search problems 55
8.1 Linear search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.2 Linear search of a value in an array . . . . . . . . . . . . . . . . . . . . . . . 57
8.3 Binary search in ordered sequences . . . . . . . . . . . . . . . . . . . . . . . 57
8.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
9 Two-dimensional counting 65
9.1 Number of grid points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.2 A southwestern slope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.3 The method of the shrinking rectangle . . . . . . . . . . . . . . . . . . . . . 69
9.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Introduction
For larger programs and for software systems, it is often impossible to give a complete
specification. Such systems have requirements. There is a whole field within software en-
gineering to collect requirements and to investigate whether the requirements are satisfied.
In this course, however, we restrict the attention to small programs that can be specified
completely.
We specify a program by means of a precondition and a postcondition in a so-called
Hoare triple:
is called the precondition, the postcondition. The meaning of the Hoare triple is:
As explained in Chapter 3, the conditions and are formulas from predicate logic. In
Chapter 4, we present a logical formalism to prove the correctness of Hoare triples. The
remainder of these lecture notes, the largest part, is devoted to the question how to do this
in practice.
5
6 CHAPTER 1. INTRODUCTION
Programming and testing. We investigate the specification, invent a program, and test it
with test data that satisfy precondition . There are three possibilities. (1) The program
does not terminate within a reasonable time; (2) the program terminates in a state that
does not satisfy ; (3) the program terminates in a state that satisfies . In the cases
(1) and (2), we can conclude that the program is incorrect, or at least unsatisfactory. It
must be modified. What about case (3)? The program passed one test. Are we allowed
to conclude that it satisfies the specification? Alas, definitely not. For almost every useful
specification, there are many (often infinitely many) states that satisfy the precondition,
and it is impossible to test all these initial states. Conclusion: in general, this approach
cannot ensure that the program satisfies its specification. Moreover, it also gives no clue
how to find a program that does satisfy the specification.
Programming and operational reasoning. In this case, we invent a program and then
reason operationally that the program starting in any state that satisfies precondition
, always terminates in a state that satisfies postcondition . Operational reasoning
means that you play the role of the computer and execute the program yourself. You
record the actions of the program, and the effects of these actions on the state of the
computer. For many programmers, this is the usual approach, although they know that
operational reasoning often fails when the program gets more complicated. Operational
reasoning is often restricted to the generic case (‘in general, we have . . . ’). It is then easy
to ignore relevant special cases. When one tries to avoid this restriction to the generic case,
however, one may end up with a large number of cases that have to be treated separately.
A computer might be able to handle it, but we human beings are not good at it. In
general, this approach does not guarantee that a program has been found that satisfies the
specification.
We use the proof method of annotated programs, because this enables us to integrate
the program and the proof. In this course, we restrict ourselves to the correct design of
small well-specified programs. This is only one of the aspects of reliable programming, but
it is an aspect that can save you from drowning in a swamp of spaghetti code.
Programmers are constantly making choices, usually based on intuition or experience.
We here indicate how a systematic analysis can guide you in your choices. The aim is
that analysis and intuition are complementary and strengthen each other. Intuition alone
often leads to commands that are not completely correct, and therefore incorrect. Analysis
without intuition can sometimes lead to a command that completely misses the mark, due
to a small computation error.
1.3 Overview
The mathematical and logical background material that we need is presented in Chapter 2.
Chapter 3 is about specifications. Chapter 4 treats the program constructs and their proof
rules. These three chapters are only briefly treated in the course. They form material to
be read when the course starts and to be consulted when specific points come up. The
course proper consists of the remaining chapters.
The foundations of program correctness are laid in the Chapters 5, 6, and 7. Chapter 5
introduces annotated commands that connect programming with correctness arguments.
Chapter 6 introduces a roadmap to derive programs with repetitions in a systematic way.
In Chapter 7 we present heuristics to find invariants. This is the main creative challenge
in the derivation of while-statements.
Chapter 8 treats search problems, in particular linear search and binary search, because
such problems occur in practice very often, and they deserve to be treated carefully.
The harvest is reaped in the Chapters 9 and 10. Here, we finally treat problems for
which the methods presented in this course are undoubtably beneficial and perhaps even
indispensable. Chapter 9 treats problems like saddleback search and presents the method
of the shrinking rectangle. Chapter 10 treats the method of strengthening of the invariant
with auxiliary information, and illustrates this with segment problems.
Logical-mathematical preparation
In this chapter, we treat the concepts and notations from mathematics and logic that will
be used in this course.
Identifiers. These are names for the objects that occur in the specifications. In these
notes, we use the following identifiers:
Strictly speaking, specification constants are logical variables. We call them specifica-
tion constants because they only occur in the specifications and not in the programs. In
particular, they cannot be modified by the programs.
The variables etc. can occur both free or bound. A variable can be bound by a
quantifier ( , ) or in the definition of a set (e.g. in N ; more about
this below). According to tradition, the argument of an array is written between square
brackets, e.g. .
Declarations. Identifiers have a domain: a set that contains the values the identifier can
refer to. In these notes, the following domains occur:
Variables and arrays are declared with an indication of the domain. We use a var decla-
ration to indicate that the value of the variable can be modified by the program, and a
const declaration otherwise. These notes do not treat array modification. All arrays are
therefore constant. An example of a declaration:
const N R array of Z
var N R
9
10 CHAPTER 2. LOGICAL-MATHEMATICAL PREPARATION
This declaration indicates that we can use the identifiers , , , , , in the specification
and the program, and it gives the domains of the identifiers. It further indicates that the
program can contain assignments to the variables and , but not to , , and .
We prefer to use and rather than and , because the values on the real axis increase
from left to right.
The conditions on the first line have no logical symbols: such conditions are called atomic.
The conditions on the first two lines have no quantifiers: these are propositional conditions.
The following expressions are numerical. They are not conditions:
mod div
Note that the second one can only be used in a specification because of the occurrence of
the specification constant .
Occasionally, we use the conditional expression , with the meaning: if
then , else . Conditional expressions make it often possible to postpone case distinc-
tions. This is useful because early case distinctions tend to increase your work load.
We denote expressions with the letters , , possibly with subscript: etc. We
denote by the result of substitution of expression for variable in expression .
Conditions are indicated by the capital letters , possibly with subscript.
Sets is the standard notation for the set that contains all objects from the domain
of for which condition holds. In this notation, is a bound variable. A set that contains
not too many elements can also be denoted by enumerating its elements:
We can further write for the set of values of expression for all
objects that satisfy : here is a bound variable. This notation can be written in
the standard notation:
2.2. STRENGTH OF FORMULAS AND PROOF FORMAT 11
The interval is called closed, the interval half-open. (We could also define
intervals and , but we do not need them here.) Working with half-open
interval has a number of advantages. For instance, the number of elements of the interval
equals , provided (the interval is empty if ). We also have
that provided .
We often use sets to restrict the domain of bound variables:
means the same as ;
means the same as ;
means the same as .
The colon in , is added to improve readability.
In words: the remainder mod is a natural number smaller than , and the integral
quotient div is such that equals times the integral quotient plus the remainder. It
follows that: div and mod . You may note that, for negative values of
, these definitions of div and mod deviate from the definitions used in the programming
languages C and Java.
The integral quotient div is the greatest integer . This implies two properties
of inequalities in which div plays a role, which we shall use later. Firstly:
div (2.2)
We derive the second property from the first one, using that is equivalent with
, and that follows from :
div (2.3)
In the splitting rule, the set is the splitting criterion. Recall that is the intersection
, and is the set difference . If, e.g., we want to count
all participants to some event, we may count all paying participants, and all nonpaying
participants, and then add these two numbers to get the number of all participants.
Occasionally, we combine the one-point rule with the rule for the empty set to
ord
Max
Min
2.3. OPERATORS AND THEIR PROPERTIES 13
We use the infix operators max and min for the maximum and minimum of pairs. We
therefore have max Max and min Min . The one-point rules are
Max
Min
Max
Min
These (perhaps unexpected) rules are useful, because the following splitting rules then hold
without exceptions:
We define the product of the sequence in the same way. The product of
the empty sequence is 1, the identity element for multiplication. The three rules are:
2.3.5 Distribution
Multiplication distributes over sums:
Multiplication with a positive number also distributes over (nonempty) maxima and
minima, while multiplication with a negative number interchanges maxima and minima:
Max Max
Min Min
Max Min
Min Max
Max Min
true
false
Disjunction with a condition that does not depend on distributes over . Similarly,
conjunction with distributes over :
Chapter 3
Specifications
This chapter is about specifications of programs. We use conditions to describe the program
state, as given by the values of the program variables. The aim of the program is usually
expressed in terms of modifications of the state. We specify the (allowed or intended)
changes of the state by using the precondition to describe the state before execution, and
the postcondition for the state after execution.
3.1 States
The starting point for program specification is the fact that execution of the program
modifies the values stored in the memory locations associated with the program. We do
not regard the values stored on the level of bits or bytes, but rather on the more abstract
level of numbers or truth values. We define the state of a program as follows:
Definition 3.1 (state) Let a program be given, with a declaration of the variables .
Then a state of the program is a set of pairs
where, for , the value is an element of the domain of the variable . The
state thus indicates the value of each program variable . The set of all states of a
program is called the state space of the program.
Example. If is a program with the variables Z, then e.g. is a state,
just as . When we fix an order of the variables (e.g. before ), we
can alternatively denote the state of by a pair of numbers: or . Then
the states of are elements of the state space Z Z .
Remark. In these notes, we ignore the fact that most programming languages cannot
represent all integer or real values. In particular, we ignore problems with overflow and
underflow, although this is an important issue for program correctness. Therefore, in these
notes, Z and R are indeed the mathematical sets of integers and reals, respectively.
15
16 CHAPTER 3. SPECIFICATIONS
Definition 3.2 (specification, precondition, postcondition, Hoare triple) Let the program vari-
ables be declared. The specification of a command in these variables consists
of a precondition and a postcondition . These conditions are formulas with no other
free variables than the variables , and possibly program constants and specifi-
cation constants. The specification means that, in every state that satisfies , command
is executable and will terminate in a state that satisfies . We denote this specification in
the form of a Hoare triple1 :
or, shorter, by
Min
The latter definition of has the additional advantage that we can replace condition (3.1)
by (in fact, we have Min for Z). In this way, we obtain
the specification
Min
In this case, a preregular specification is impossible because the initial value of the
variable is needed to determine the final value that is required.
18 CHAPTER 3. SPECIFICATIONS
true
3.7 Exercises
A number of exercises in which you have to specify a command by providing a declaration
and a specifying Hoare triple.
Exercise 3.1. Specify a command that, given integers and , interchanges the values of
these variables.
Exercise 3.2. Specify a command that, given integers and , stores in the maximum
of both values, and stores in the minimum.
Exercise 3.3. Specify a command that, given , , with and ,
subdivides the interval in intervals of almost-equal lengths, numbered ,
and determines the number of the interval that contains . (This comes from image
processing, to partition a rectangular image of width in vertical strips).
Exercise 3.4. Specify a command that determines a power of 2 that is greater than a
given number .
Exercise 3.5. Specify a command that, given and , determines the value .
Exercise 3.6. Specify a command that, given , determines the integral square root of
, i.e., the greatest integer for which the square does not exceed . Do not use the square
root symbol .
Exercise 3.7. Give a definition of the sequence of Fibonacci, and specify a command that,
given , determines the th number of the sequence.
Exercise 3.8. Specify a command that computes the sum of the elements of an array
array of Z.
Exercise 3.9. Specify a command that computes the maximum of the elements of an array
array of Z.
Exercise 3.10. Specify a command that computes the inner product of two arrays.
Exercise 3.11. Given is a function N Z that can be used in the command. Specify
a command that computes the least number with , given that such a number
exists.
3.7. EXERCISES 19
Exercise 3.12. Specify a command that, given an array, computes the length of the longest
subarray with positive values.
Exercise 3.13. Specify a command that, given an array, computes the length of the longest
subarray for which all elements are different.
Exercise 3.14. Specify a command that sorts a given array in which all elements are
different. You may use a specification-array .
Exercise 3.15. Same as the previous exercise, but now without the assumption that all
elements of the array are different.
Exercise 3.16. Specify a command that assigns to the variable Z a smaller value. Is
it possible to do this, in a satisfactory way, with a postregular specification?
Exercise 3.17. Specify a command that for a given array of numbers computes the greatest
difference between subsequent elements. Discuss the ambiguities in the previous sentence.
20 CHAPTER 3. SPECIFICATIONS
Chapter 4
Program constructs
In the previous chapter we have discussed commands without telling what they are or
what they look like. We come to this point here. We define pseudocode, an abstract and
very simple programming language that only contains declarations and a few fundamental
programming constructs: skip, the assignment, the sequential composition, the if/then/else
construct, and the while/do construct. One can regard this pseudocode as the mother of
all imperative programming languages.
4.1 Pseudocode
Definition 4.1 (program expression, program condition) A program expression is an ex-
pression constructed in the usual way from
integer numbers (in decimal notation),
program variables,
program constants,
arithmetic operations , , , div, mod, min, max,
predicates , , , , , ,
propositional connectives , , , , .
A program condition is a program expression of type B (boolean).
We now define our pseudocode. We use declarations of program constants and program
variables, just as in the specification language. We have the following commands and
constructs:
a. skip is a command (the empty command);
21
22 CHAPTER 4. PROGRAM CONSTRUCTS
We keep the explanation short, because we assume that you have had some introductory
programming course.
b. evaluates the value of expression and assigns this to the variable . The
evaluation of does not change the state, but the state usually changes by the
assignment to .
e. while do end begins with evaluating the truth value of . If is true, then
is executed, and is evaluated once more. This is repeated as long as the evaluation
of yields true. The command terminates when the truth value of is false. The
evaluations of do not change the state.
skip
The first two proof rules have no premises, and are therefore called axioms: the skip axiom
and the assignment axiom.
The other rules have to be read as follows. Each of them has a line that separates the
premises of the rule from the conclusion of the rule. If the premises (the expressions above
the line) hold, then the conclusion (the expression below the line) also holds.
The first three proper proof rules are named after the command in the conclusion.
They are named therefore the composition rule, the conditional rule, and the repetition
4.2. PROOF RULES 23
vf boundedness of vf
invariance of
vf vf decreasingness of vf
The last three proof rules contain the ‘meta-arrow’ introduced in Chapter 2.2.
For conditions and , the universal implication means that in all states where
holds, holds as well. If this is the case, replacing by is called weakening, and
replacing by is called strengthening. Therefore, the last two proof rules are named:
strengthening of the precondition and weakening of the postcondition.
We turn to the justification of the proof rules. Recall from the previous chapter that
means that, if we execute program in a state where holds, execution of
terminates in a state where holds. We discuss the proof rules one by one.
skip: If we execute skip in a state where holds, nothing happens and remains valid.
sequential composition: The premises of the rule say that command transforms every
state in which holds into a state where holds, and that command transforms
every state where holds into a state where holds. This justifies the conclusion
that the sequential composition ( ) of followed by transforms every state
where holds into a state where holds.
conditional: The premises of the rule say that command transforms every state where
holds, into a state where holds, and that transform every state where
holds, into a state where holds. If we now execute if then else end
in a state where holds, there are two possibilities. Either holds in this state
and command is executed, which transforms the initial state into a state where
holds, or is false and command is executed, which transforms the initial state
into a state where holds. Either way, the resulting state satisfies .
repetition: This is the most difficult rule. The first premise vf means that
in all states where holds, the value of vf is nonnegative. The second premise
means that command transforms every state where holds
into a state where holds. The third premise vf vf means
that transforms every state where holds and where vf has a value into a
state where vf has a value smaller than . As the value of specification constant
is unmodified, this means that the value of vf has become smaller.
The conclusion of the rule is about execution of while do end in a state that
satisfies . This repetition executes as long as guard holds. In other words, every
time is executed, it has the precondition . Therefore, the second premise implies
that, if holds before the repetition, repeated executions of preserve validity of
24 CHAPTER 4. PROGRAM CONSTRUCTS
strengthening of the precondition: The premises are that holds and that
transforms every state where holds into a state where holds. If we executes
in a state where holds, this state also satisfies because of . Therefore
transforms it into a state where holds. This proves .
weakening of the postcondition: This is proved in the same way as the previous rule.
4.3 Exercises
Exercise 4.1. Above we defined do while end using while/do and if/then/else.
Show that conversely it is also possible to define while do end using do/while and
if/then/else.
Exercise 4.2. Define for to do end by means of the do/while construct.
Exercise 4.3. How could we construct skip if it was not yet in the programming language
(there are at least two possibilities), and how can we construct a command that never
terminates?
Exercise 4.4. Give proof rules for the program constructs defined at the end of Chapter 4.1:
if then end, do while end, and for to do end.
Chapter 5
Annotated commands
In this chapter, we show how to annotate commands with conditions. We write conditions
in the program text in the same way as comments are added. The meaning is that the
program state satisfies the condition when execution of the program arrives at the point
of the condition. The condition is therefore the precondition of the next command in
the program text, and it is the postcondition of the previous command in the program
text. This must be done in accordance with the proof rules for the constructs used in the
program. Furthermore, it is possible to add reasoning steps to weaken a condition (or to
keep it equivalent). Such steps are justified by means of comment between and . A
program annotated according to the annotation rules can be read as a proof of correctness.
We give various examples in this chapter.
Note that we have named the precondition , and the postcondition . We shall use
this abbreviation mechanism more often in annotations. It is useful, because it enables
us in the remainder of the text to use the names and for the (possibly complicated)
expressions that they represent.
Which command can satisfy this specification? We expect an assignment of the form
for some term . The proof rule for an assignment with postcondition is:
25
26 CHAPTER 5. ANNOTATED COMMANDS
;
The precondition obtained in this way is not yet our precondition . We have to choose
a program expression such that is implied by , i.e., by . Simple
arithmetic shows that implies the condition . We can make this
condition equal to , by choosing for the term . We call this step the
preparation of the assignment . The resulting annotation is:
Again, we expect that will be an assignment of the form . How to prepare it? We
need to massage precondition in such a way that becomes the righthand side of the
equality. Starting with , we therefore have to subtract 1, followed by an integer
division by 3. In this way, we can arrive at the annotation
To summarize:
If two conditions, say , are written after each other without some
command between them, then holds, and the two conditions are
separated by a comment that argues why the implication holds (this can
be a standard argument like “arithmetic” or it can refer to an annotated
linear proof elsewhere).
5.2. SEQUENTIAL COMPOSITION 27
(5.1)
How to implement this? At least we have to modify . Let us therefore first give the
value required, i.e., establish . It looks as if this can be done with the assignment
. As the proof rule for this assignment requires that all occurrences of in the
postcondition are replaced by , we also must remove isolated occurrences of in the
other conjunct of . After the first assignment more must be done, and eventually must
be modified. In this way, we may obtain the annotated command:
( arithmetic in preparation of )
( arithmetic in preparation of )
( arithmetic )
max min
max min
if then
28 CHAPTER 5. ANNOTATED COMMANDS
else
skip;
To ascertain the correctness of this annotated command, we need to verify the following
items:
• the annotations of the three assignments satisfy the assignment rule;
and
(5.2)
We can treat the two disjuncts of separately, if we are prepared to make additional
assumptions. Indeed we have
5.4. ADDITIONAL RULES FOR ANNOTATIONS 29
and
if then
( arithmetic )
( logic )
else
( arithmetic )
( logic )
Naming conditions on the fly. As we have seen, it is sometimes useful to introduce a name
for a condition that occurs several times. In particular, this can apply to the precondition
or the postcondition of a conditional command.
30 CHAPTER 5. ANNOTATED COMMANDS
Omitting constant conditions. A condition that only contains constants (program con-
stants and specification constants), cannot change during execution of a command. When
it holds somewhere in the annotation, it holds everywhere. It is useless to write down
such a condition repeatedly. Just mention that it is obviously constant, and you can use
it everywhere. This applies in particular to the precondition of a preregular specification
(cf. Chapter 3.5).
A sufficient condition for ‘not influencing each other’ is: the variables assigned to are dif-
ferent, and each of them does not occur in the expressions assigned to the other variables of
the sequence. In this case, we can perform the assignments simultaneously, and we can de-
rive the precondition of the sequence from the postcondition by simultaneous substitution.
This eliminates some writing. We illustrate this with the above example:
5.5 Exercises
Unless specified otherwise, every exercise in this course that asks for the design of a com-
mand that satisfies a specification, includes the task to prove the correctness of your
solution. In the exercises below, this should be done by means of a correct annotation.
Unless specified otherwise, we use the declaration: var Z.
Exercise 5.1. Determine for each of the following Hoare triples a single assignment that
satisfies the specification, and prove this with a correct annotation.
(a)
(b)
(c)
(d)
(e)
Exercise 5.2. Determine an annotated command with
(5.3)
true
if then
else
end
if mod then
else
end
32 CHAPTER 5. ANNOTATED COMMANDS
Exercise 5.9. A problem from image processing: partition a rectangular image of width
in vertical strips of approximately equal width, and determine for a pixel with horizontal
coordinate the number of the strip it is contained in.
Given are natural numbers and . We partition the interval in subintervals.
The point Z is then in the subinterval with number if and only if
div div
div div
const Z
; ;
(b) Determine an annotated command that satisfies
const Z
mod and
mod
if then
div
else
div
end
(a) Extend the annotation as far as possible, and use this to derive sufficient conditions
for guard to ensure correctness of the command.
(b) Show that the guard is wrong. Use the condition derived to construct a
counterexample.
(c) Choose some correct guard and prove its correctness.
34 CHAPTER 5. ANNOTATED COMMANDS
Chapter 6
Repetitions
We turn to the derivation of correct repetitions. This is more difficult than commands
without repetitions, because a repetition is a repeated sequential composition of the loop
body with itself, and we do not yet know intermediate conditions. In order to reason
successfully about a repetition, we use an invariant: a condition that can serve repeatedly
as the intermediate condition. In other words, the invariant is a condition that remains
valid by execution of the loop body. Finding a suitable invariant is often a difficult part
of the derivation of a correct command: more about this in the next chapter.
We begin by recalling the proof rule for repetitions from Chapter 4, in adapted form.
vf vf vf
while do end
Here we have combined the last two premises of the proof rule, because this often makes
the task of annotating the loop body less cumbersome.
In this chapter, we give a roadmap for the derivation of repetitions, followed by some
examples. The roadmap serves to structure the search for components that satisfy the
premises of the repetition rule. It does this by going slightly beyond the rule itself. In order
to have a general precondition and a general postcondition , it allows an initialization
command before the repetition, and it allows weakening of to .
Before proceeding, we note the relationship between the time complexity (the number
of computation steps) of a repetition and the variant function:
The problem is to implement this, and to prove the correctness of the implementation
obtained. For a repetition, we proceed as follows.
Step 0: choice for repetition. In view of the specification, we may decide to use a repeti-
tion.
35
36 CHAPTER 6. REPETITIONS
If we then can apply the repetition rule, the postcondition is within reach.
Step 2: initialization. In general, the precondition does not imply the invariant . We
usually need to determine an initialization command , for which we can prove
Step 3: variant function. We choose a suitable integer-valued function vf and prove the
boundedness of vf:
vf
Usually we have some idea how to reach the postcondition by repeatedly doing steps in
the right direction. The function vf is a measure for the number of steps needed before the
goal (the postcondition) is reached. A suitable vf is nonnegative as long as the goal has
not yet been reached, and it should decrease by every step in the right direction.
Step 4: body of the repetition. We determine a command for which we can prove
the invariance of and the decrease of vf. We often do these things simultaneously and
derive:
vf vf
A rule of thumb for finding a suitable loop body is that the body should make vf smaller
and yet preserve the invariant .
Step 5: conclusion. When the steps 1, 2, 3, and 4 have been taken successfully, we may
conclude:
end
, because of step 4 and the proof rule for the repetition
( by finalization in step 1 )
To summarize, the steps 1, 2, 3, 4 each have their own proof obligations. The steps
1 and 3 have proof obligations ( and vf , respectively) that
can be met by annotated linear proofs, but sometimes an informal argument is acceptable.
The steps 2 and 4 require an annotated command. Occasionally, step 2 does not need a
command if the precondition happens to imply the invariant (e.g., see Chapter 6.4).
Step 5, the conclusion, may look superfluous because it can be derived mechanically
from the preceding steps. It is important to add it, however, because it is the point where
you can use your programmer’s intuition to see that your formal activities have not gone
astray.
We illustrate the roadmap by three examples: exponentiation, powers of 2, and Euclid’s
algorithm.
(6.1)
var R
var Z
Step 1: choice of invariant and guard, finalization. To find a suitable invariant, we look at
the postcondition . Its righthand side also occurs in the precondition, we therefore
leave it unchanged. As exponentiation is repeated multiplication, we expect multiplica-
tions. First, a very modest multiplication: the postcondition is equivalent with .
Looking at the precondition , we see that can be established from the
precondition by putting . We therefore choose the invariant
Which guard do we need? The loop can terminate when , because then
implies . Of course, we cannot use in the guard, because this would require
(repeated) computation of (which is the problem we need to solve!). As for all
(also for ), however, the loop can also terminate when . Indeed, we have
. We therefore choose the guard
38 CHAPTER 6. REPETITIONS
Step 3: variant function. Function vf should be a suitable measure for the number of steps
needed to reach the postcondition. This suggests the choice vf . The proof obligation
vf now directly follows from the invariant .
vf vf
In order to decrease vf , it is natural to use . In order to restore the invariant
we have to multiply with . This is done in the following annotated command:
vf
( definitions of , , vf )
( definitions of and vf )
vf
( vf : )
while do
end
In this summary, we only repeat the precondition, postcondition, invariant, and variant
function. We do not repeat the detailed annotations of the steps 2 and 4, or the proofs
given in the steps 1 and 3.
6.3. EXAMPLE: POWERS OF 2 39
Remark. Before going to the next example, we have a closer look at the properties of
exponentiation used in the above derivation:
const Z
var Z
The specification is preregular: only depends on constants. We can therefore omit the
condition from the annotation, and use it whenever needed. We now follow the
roadmap.
Step 1: choice of invariant and guard, finalization. We take a part of the postcondition
as the invariant :
The choice for the guard is easy: the negation of , the remaining part of postcon-
dition . We thus choose
( calculus )
This annotation is constructed from bottom to top, because the invariant gives us more
indication how to proceed than the precondition.
40 CHAPTER 6. REPETITIONS
Step 4: body of the repetition. To decrease vf, we have to increase because is constant.
The invariant implies . We therefore increase by multiplying it by 2. These
considerations lead to the annotated body:
vf
( definitions of , , vf )
( definitions of and vf )
vf
Step 5: conclusion.
( vf )
while do
end
(6.2)
var Z
gcd
6.4. EXAMPLE: EUCLID’S ALGORITHM 41
The Greek mathematician Euclid (around 300 BC) designed an algorithm for this that
is still in use. It is based on the idea: if you need gcd , replace the arguments and
by smaller numbers with the same gcd; repeat this until one of the arguments is 0; then
the other argument is the gcd.
We first derive a recurrence relation for gcd, which we need for the algorithm. Every
number is the greatest common divisor of itself and 0, because it is a divisor of
itself ( ) and of 0 ( , and the greatest one because . We thus have
gcd , the base of the recurrence relation. We shall use this in the last
step of the algorithm: if one of the arguments is 0, we are done.
As long as the arguments of gcd differ from 0, we want to make them smaller. For this
purpose, we use formula (2.1) from Chapter 2.3.1:
For , we have that every common divisor of and is a common divisor of and
mod , and vice versa. This is proved in
divides and
{ and (2.1) }
divides and div mod
(6.3)
{ if divides , then ( divides ) ( divides ) }
divides and mod
This is an annotated linear proof with the relation . Note the and around the
arguments. The conditions are without braces, because this is not an annotated program.
It follows that gcd gcd mod provided (why?). We now have the
following recurrence relation:
gcd
(6.4)
gcd gcd mod
Step 0: choice for repetition. We choose a repetition because we expect a command that
decreases the arguments of gcd in steps.
Step 1: choice of invariant and guard, finalization. Because of the first formula of (6.4),
we allow in the invariant. We therefore define the invariant and the guard by
gcd
Finalization is proved in
gcd
{ substitute , omit superfluous conjuncts }
gcd
(6.5)
{ first formula of (6.4) }
42 CHAPTER 6. REPETITIONS
Step 2: initialization. In this case, the precondition happens to imply the invariant .
Initialization is therefore superfluous: the command skip suffices.
Step 4: body of the repetition. In the body of the repetition, we use an auxiliary variable
Z to store the value of mod . The annotated loop body becomes:
vf
gcd
( second formula of (6.4), arithmetic )
gcd mod mod
mod
gcd
( arithmetic, omitting conjuncts )
gcd
gcd
gcd
vf
Step 5: conclusion.
gcd
gcd
( vf : y )
while do
var Z
mod
end
6.6 Exercises
Exercise 6.1. Design a more efficient command for exponentiation according to speci-
fication (6.1) of Chapter 6.2. Choose the same , , and vf . Verify and use that, if is
even, the invariant is also preserved by div .
Compare the efficiency of command with the efficiency of the solution in Chapter 6.2.
For this purpose, determine the number of assignments needed for the computation of
for both programs.
Exercise 6.2. Multiplication can be specified by
var Z
Design a command for this specification that only uses addition, multiplication by 2,
and division by 2 with remainder. Use the invariant .
Exercise 6.3. Function N Z is defined by the recurrence relation:
It is known as the sequence of Fibonacci. Derive a command that for given number
computes the value of . Use integer variables Z and the invariant
true
( vf )
while do
true
( arithmetic )
true
true
end
div mod
Design a command that satisfies
var Z
44 CHAPTER 6. REPETITIONS
Here you can use active finalization. Use variables and with the invariant
div mod
var Z
div mod
var Z
Exercise 6.8. Assume that someone follows the roadmap to find a repetition that
satisfies , and, in step 1, chooses and in such a way that . Why is
his effort doomed to fail? Which step of the roadmap must fail?
Chapter 7
In this chapter, we treat an essential aspect of correct programming: the search for a
suitable invariant in the derivation of a repetition. We begin with some considerations
concerning invariants and repetitions. Next we discuss several heuristics (rules of thumb)
that can help to find suitable invariants. We finally give some applications of these heuris-
tics.
You can often find a suitable invariant by taking a condition, weaker than
the postcondition, which can easily be established from the precondition.
Therefore, search for a condition , in between and , and a guard
such that follows from .
Below we give several heuristics (rules of thumb), that can be helpful. We illustrate the
heuristics by means of the examples in the previous chapter.
45
46 CHAPTER 7. FINDING THE INVARIANT
This heuristic has two special cases that are worth mentioning:
What we have done here, is a form of generalization. We have first rewritten the postcon-
dition and strengthened it slightly to gcd . We have then replaced the
constant 0 by the variable . We finally added a lower bound for , which allows ,
but does not contradict the precondition.
To summarize, the application of the heuristics can be helpful in the search for a
suitable invariant. Indiscriminate application, however, is impossible. You have to choose:
which conjunct? which constant, variable, or expression, which generalization? You need
experience to make suitable choices.
We proceed with yet another example.
(7.1)
const N
const array of R
var R
true
We follow the roadmap to derive command . First, however, we derive some properties of
sums that we need. For the sake of conciseness and clarity, we introduce the abbreviation
{ definition }
{ definition }
(7.2)
We have because
true
( N, and first formula of (7.2) )
Reading from top to bottom, this annotation may look unexpected. In fact, we designed
it by arguing from bottom ( ) to top ( ). We choose because is the only
directly available value of .
vf
vf
( definitions of , , vf )
( definitions of and vf )
vf
Step 5. Conclusion.
const N
const array of R
var R
true
( vf= n-k )
while do
end
7.5 Exercises
Exercise 7.1. Derive a command to compute the second and third power of a number
, when the only allowed operations are addition, and multiplication by 2 and 3. The
specification is
const N
var Z
true
Use the heuristic replacing a constant by a variable to find and . More specifically,
replace the constant by a variable N. Define vf . Note that, because of the
invariant, the values of and can be expressed in , , and :
var Z
Hint: recall the heuristic generalization, and compare this exercise with the example Ex-
ponentiation in Chapter 6.2.
Exercise 7.3. Computing the integral square root. Consider the specification
const N
var Z
true
Derive as follows. Use the heuristic isolating a conjunct to find a suitable invariant
and guard . Use the initialization , the variant function , and the body
.
Exercise 7.4. A more efficient command for the computation of the integral square root
according to the specification of the previous exercise.
(a) Determine an invariant and a guard with the heuristic replacing expression by variable,
applied to the expression and a new variable . Include a conjunct in the
invariant. Initialize with and . Choose a suitable variant function, and
use a body of the form
var Z
div
if then
?
else
?
end
(b) Determine the number of assignments, executed by this command in the case of
. Compare this with the number of assignments performed by the command from the
previous exercise.
Exercise 7.5. Integral division with remainder of an integer by a positive integer can
be specified by
const N
var Z
Determine a command that satisfies this specification and that does not use multiplica-
tion, div, and mod. Apply the heuristic of isolating a conjunct to find and .
Exercise 7.6. A variation on the previous exercise. Next to additions and subtractions,
you may now use multiplications and divisions of the form and div . Determine a
command that satisfies
7.5. EXERCISES 51
const N
var Z
var Z
; ;
is not valid.
(b) Does the program remain correct, when guard is replaced by ? If so, adapt
the proof. If not, why?
(c) Same question, with guard replaced by .
(d) Is it possible to adapt the proof of the program to the invariant
1
Such a variable is called a ghost variable.
2
This function was invented under the name fusc by the Dutch computer scientist Edsger W. Dijkstra.
52 CHAPTER 7. FINDING THE INVARIANT
const N
const array of Z
var Z
true
Max
(a) Determine a command that satisfies this specification, using the ideas of Chapter
7.4. Use the operator max and the value .
(b) Strengthen the precondition to . Now determine a command that satisfies
the specification and that does not use max and .
Exercise 7.11. Design a program to compute the product of elements of an array. For
this purpose, in the specification of Chapter 7.4, replace the operator by . Give the
formal specification. Modify the program of Chapter 7.4 in such a way that it satisfies
the new specification and adapt the proof.
Exercise 7.12. We specify a command to determine the inner product of two arrays
by
const N
const array of R
var R
true
const N
const array of Z
var Z
true
const N
const array of Z
var Z
true
7.5. EXERCISES 53
At first sight, it may seem that implementing this specification requires computa-
tion steps, but it can be done with linear complexity. For this purpose, first determine
recurrence relations for the expressions and , defined by
Use the recurrence relations to derive a program that satisfies the above specification
with complexity linear in .
Exercise 7.15. A polynomial can be represented by an
array , by identifying . We specify the computation of the value of the
polynomial for the argument by
const N
const R
const array of R
var R
true
Determine a command that satisfies this. Because exponentiation is not in our pro-
gramming language, it may seem that the implementation requires computation
steps, but there is a simple repetition that implements with time complexity linear in
. Determine such a solution using the expression
Search problems
var Z
Min N prop
Note that this specification is biregular: the precondition is constant (independent of the
program variables) and the postcondition is of the form . We can therefore keep
precondition out of the annotation, and yet use it whenever needed.
Precondition implies . It follows that is a natural number that satisfies
prop. In fact, precondition is equivalent with
Step 1. We apply the heuristic weakening the postcondition to choose the invariant:
The idea is to search until some value has been found that satisfies prop. We therefore
choose the guard
prop
55
56 CHAPTER 8. SEARCH PROBLEMS
In fact, we have
{ definitions of and }
prop
{ prop : apply the third conjunct of (8.1) }
{ arithmetic }
true
( use )
vf
prop
( implies prop , therefore )
vf
Min N prop
( vf : M-k )
while prop do
end
8.2. LINEAR SEARCH OF A VALUE IN AN ARRAY 57
(8.2)
const N
const Z
const array of Z
var Z
Min N
Note that implies that and hence . We can therefore directly apply
(8.1), when we define
prop
while do
end
What happens in this program when does not occur in the array? In this case, the
command considers all elements of the array, and then evaluates the test .
The first conjunct yields false, but the expression is undefined, because the index is not
in the domain of the array. Therefore, the truth value of cannot be determined.
This truth value seems to be unnecessary because false is false for every value of .
For most programming languages (e.g., C and Java), this is not a problem, because
they evaluate conjunctions from left to right, and when the lefthand argument of a con-
junction is false, they return false and do not evaluate the righthand conjunct (short
circuit evaluation).
It follows that reversing the order of a conjunction can affect the execution of a com-
mand. For example, if we translate the above command in Java and replace the guard
by the logically equivalent expression , this would give
a runtime error when value does not occur in the array. In C, it would also be incorrect,
but it would probably not result in an error message but crash instead.
divided by 2. If the dictionary has a million words, in 20 steps the search space is a single
word, because , and then you see whether occurs. This algorithm is called
binary search.
We treat this algorithm for the search in ordered arrays of numbers. We start with a
definition to distinguish various types of ordered arrays.
Consider an interval of Z and a function R. We may regard as a sequence
of numbers. We define:
is called ascending if
is called increasing if
is called descending if
is called decreasing if
is called monotonic if it has one of the above properties. Warning: other definitions are
also used (but not in these notes)!
We now consider specification (8.2) with the additional assumption that array is
ascending. If the value occurs in the array, the smallest index with is also
the smallest index with . If does not occur, the smallest index with
is also a useful result: in that case, the simple test suffices to decide that does
not occur in the array (an example of active finalization).
We therefore change specification (8.2) into
const N
const Z
const array of Z
var Z
is ascending
Min N
Min N
{ properties of Min }
N
{ logic: contraposition }
N
{ use N ; arithmetic }
N
{ logic; array is ascending }
{ rewrite }
The array elements and were not yet defined. We can therefore give them
a value. In the program, however, we will not inspect the values of and . We
introduce them only to give meaning to the boundary cases and in .
8.3. BINARY SEARCH IN ORDERED SEQUENCES 59
It now suffices to establish the postcondition . The surprise is that we can do this
without using that array is ascending! In other words, we implement the specification
const N
const Z
const array of Z
var Z
Step 1. We apply the heuristic splitting a variable and introduce a new variable Z:
It is clear that .
( N and arithmetic )
Step 3. We take
vf
Step 4. How to find the body of the repetition? We need to decrease the difference .
We therefore choose a number Z with , and compare with . If
, we need to continue the search to the right of , and we can put .
If , we need to continue the search to the left of , and we can put .
We postpone the details of choosing by assuming that we have some command that
satisfies
(8.3)
vf
vf
if then
else
We prove
vf vf (8.4)
vf
vf
( definitions of and vf )
if then
else
end
( collect the branches; definitions and vf )
vf
div
div
This is proved as follows, using two properties of div from Chapter 2.3.1.
8.4. EXERCISES 61
div div
{ formulas (2.2) and (2.3) }
{ arithmetic }
{ logic }
{ definitions of and }
const N
const Z
const array of Z
var Z
is ascending
( vf: )
while do
div
if then
else
This command binary search is much more efficient than linear search. It has loga-
rithmic complexity. If , the loop body is executed at most times. Note that,
as announced, the only elements inspected are for . In particular, the
elements and are not inspected.
8.4 Exercises
Exercise 8.1. (a) Consider functions Z with an interval of Z. Is it possible
that the following holds?
Exercise 8.2. Design a command to compute the greatest index with for
an array as in specification (8.2). First, give a biregular specification.
Exercise 8.3. Design a command that satisfies
const Z
var N
Min N
The command is not allowed to use exponentiation. Use a variable with an invariant
that contains . If the specification constant does not occur in vf, there is no need
to prove that , compare Chapter 2.3.3 (it follows from the fact that the command
terminates).
Exercise 8.4. This is a variation on the previous exercise. Determine a command that
satisfies
const Z
var N
Min N
const N
const array of Z
var Z
8.4. EXERCISES 63
Exercise 8.9. A variation on the previous exercise. Design a command with logarithmic
complexity that satisfies
const N
const array of Z
var Z
var Z
For this purpose, strengthen the invariant also with this conjunct.
Exercise 8.11. The boolean function is defined for all Z. It is true for at least
one argument . Design a command to determine such an argument.
Exercise 8.12. The king’s problem. Given is a group of persons with the numbers
. One of them is the king, say with number . Every person knows him (by
sight), and he knows nobody (except for, possibly, himself). The boolean function
is given and expresses that knows . Design a command to determine . The formal
specification is:
const N
var N
Two-dimensional counting
In this chapter, we treat problems about functions of two integer variables. We are espe-
cially interested in cases where there is an efficient solution due to monotonicity properties
of the function. Such cases are known as saddleback search. We concentrate on saddleback
search as a counting problem.
(9.1)
const Z
var Z
(9.2)
65
66 CHAPTER 9. TWO-DIMENSIONAL COUNTING
between the grid points with height and the points with height . There need not
be any grid points with height . In fact, we do not use the contour lines in the formal
derivation, but only to direct the design.
S map
S high
SS
a aa
a a
a
Z
Z
Z
Z contour line of height
low shrinking
rectangle
The number of points to be counted is . We use the variable for the number of
points that have been counted, and a function for the number of points that have
yet to be counted. This gives the invariant . The initial search
area is the whole map (see the sketch). We treat the search area as a shrinking rectangle,
determined by a search point close to the contour line. As the contour line goes
between NW and SE, we choose the shrinking rectangle to the SE of the search point (it
is also possible to choose the shrinking rectangle to the NW of the search point, but the
other two possibilities fail because those rectangles typically have too few points close to
the contour line). The number of points to be counted in the rectangle to the SE of the
search point is
(9.3)
(9.4)
We have introduced the variables and of formula (9.3) in order to be able to make
the shrinking rectangle smaller by increasing or decreasing , see the sketch. We now
determine how these modifications of and affect the value of .
We extend the format for annotated linear proofs by allowing the introduction of con-
ditions (“Suppose . . . ”) in computation steps.
9.2. A SOUTHWESTERN SLOPE 67
{ definition of }
{ Suppose }
{ Splitting: or }
{ Counting, use }
{ definition of }
{ Suppose . Splitting: or }
This proves
(9.6)
We have thus obtained the recurrence equations (9.4), (9.5), and (9.6) for . Now we can
turn to the roadmap.
{ definition of }
{ see (9.4) }
(9.8)
Step 2: Initialization.
Step 4. The annotated loop body is obtained using the other two recurrence relations:
vf
( definitions of , , and vf )
if then
vf
else
vf
end; ( collect branches )
vf
Step 5. Under the assumption (9.2) that is ascending in both arguments, we thus obtain
the solution:
(9.9)
9.3. THE METHOD OF THE SHRINKING RECTANGLE 69
( vf )
while do
if then
else
The initial value of the variant function is vf . The loop body has the precondition
vf . The body decreases vf. This implies that the body is executed at most
times. The time complexity is therefore linear in the sum of the lengths of the sides of the
rectangle. The command of Exercise 9.1 requires inspections of , and has therefore
a time complexity of order . This shows that command (9.9) is much more efficient
than the command of Exercise 9.1.
Let us call the method employed in Chapter 9.2 the method of the shrinking rectangle.
The point is that we use the invariant and a guard such that
implies that . As is the number of points to be counted in the shrinking
rectangle, this rectangle needs to shrink to reach the postcondition .
At first sight, it may look more natural to use the invariant . This would
require initialization with an empty rectangle and steps in which the rectangle grows to
the full map. It turns out that this idea of a growing rectangle is usually infeasible because
it is hard to get enough information to decide how the rectangle should grow.
The command of Chapter 9.2 is important for two reasons. One reason is that it can
serve as a building block for other commands. The other reason is that it is one of the
simplest applications of the method of the shrinking rectangle.
In Chapter 9.2, we systematically worked with half-open intervals (see 2.1). This had
the effect that the grid point turned out to play the role we might have expected
to be played by . This is not a good reason to deviate from our choice: systematically
working with half-open intervals makes the probability of counting errors smaller.
When you need properties of a complicated function like in Chapter 9.2, it is
best to derive such properties before starting with the roadmap.
70 CHAPTER 9. TWO-DIMENSIONAL COUNTING
9.4 Exercises
Exercise 9.1. Design a command that satisfies specification (9.1) with , N. It
can be done by a single repetition with the invariant
const Z
var Z
Sketch an elevation map, with the contour line of and a shrinking rectangle. In this case,
you do not need a recurrence relation. Use the heuristic “isolating a conjunct” to find a
suitable invariant.
Exercise 9.3. Given is a function Z Z that is descending in the first argument and
ascending in the second argument:
and
(a) Sketch an elevation map which indicates where is high and where is low, and how
the contour lines go.
We choose a shrinking rectangle in the southwest and define
(b) Determine recurrence equations for the function analogous to (9.4), (9.5), and (9.6).
(c) Construct a command that computes for given and .
Exercise 9.4. Let function be decreasing in and ascending in . Determine a
command that satisfies
const N
const Z
var Z
Hint: you can use function ord introduced in Chapter 2.3.2 to postpone a case distinction.
Exercise 9.5. Given are constants Z and a function Z Z that is increasing in
both arguments. Design a command to determine the number of pairs
with . First give a formal specification.
9.4. EXERCISES 71
const N
const Z
var Z
In this case, the search area is triangular. Therefore, use a “shrinking triangle” rather than
a shrinking rectangle.
Exercise 9.7. The function N Z is increasing in the first argument and descending
in the second argument. For given constants N and Z, specify and design a
command to compute the number of pairs N with and .
Exercise 9.8. The function N Z is ascending in both arguments. For a given
constant N, specify and design a command to compute the number of pairs N
with and .
Exercise 9.9. Let function Z Z be ascending in both arguments. Derive a command
to compute
Determine recurrence equations for this function and use the invariant (9.7).
Exercise 9.10. (a) In the situation of Chapter 9.2, determine a command to compute the
number of grid points in the rectangle that satisfy .
(b) Similarly for the number of grid points that satisfy .
Hint: combine the solutions of 9.2 and of part (a).
Exercise 9.11. Design a command to compute
N N
Next, use the result to determine the number of grid points within a circle with radius
, and use this to approximate the value of .
Exercise 9.12. The boolean function of two integer arguments satisfies
As before, the search area is triangular. Make a sketch of the area where can be true.
Exercise 9.13. Coincidence counting. Determine a command that satisfies
72 CHAPTER 9. TWO-DIMENSIONAL COUNTING
const N
const array of Z
const array of Z
var Z
and are increasing
const N
var Z
Min
You may use the operator min, the value , and the function (absolute value).
Exercise 9.15. Given are positive integers and , and a sequence of positive inte-
gers for . Determine a command to compute the number of contiguous
subsequences of with the sum , i.e.,
const Z
var Z
Min
9.4. EXERCISES 73
You may use the values and and the binary operator min.
Does your program and its proof remain correct when the function is only
ascending in ? Or when is only ascending in ? In both cases: why, or why not?
Exercise 9.20. The function N Z is ascending in both arguments. Let Z and
N. Specify and design a command to compute .
Exercise 9.21. The function N Z is ascending in the first argument and increasing
in the second argument. Let Z and N be such that . Specify and design
a command to compute
Note that there is an additional difficulty in the proof of termination. Do not solve this by
complicating the guard. It can be solved by strengthening the invariant.
Exercise 9.22. The function Z Z B is convex in the first argument, i.e.
In this case, you need a shrinking rectangle that is determined by two moving vertices.
Make a sketch and use the function
Max
Replace the upper limit of and the lower bound of by variables and . Add an upper
bound for to the invariant in order to form a variant function.
This shows that the method of the shrinking rectangle can be used without contour lines.
74 CHAPTER 9. TWO-DIMENSIONAL COUNTING
Chapter 10
This chapter is about repetitions where the invariant obtained from the postcondition does
not contain enough information to be kept invariant, while this can be done by adding
additional variables with corresponding invariants. In fact, we did this above (e.g., in
Exercise 6.3 for the sequence of Fibonacci and Exercise 7.1 for and ).
A typical case is that we need to compute a certain value, and assign it to a variable
. We solve this by generalizing the intended value to a function, in this chapter usually
of one argument. We then search for a recurrence relation for this function. In the present
chapter, this recurrence relation contains a second function that is not directly available.
This may repeat itself. When we have enough recurrence relations to determine the values
of all occurring functions, we can turn to the roadmap. We may have to add additional
conjuncts to the invariant for every auxiliary function.
We illustrate the technique by segment exercises. This is not because segment exercises
themselves are very important, but because these problems are appropriate for learning
the technique.
We define a segment of an array to be a subarray of the form with
. In this chapter, we use the declaration
const N
const array of Z
var Z
To eliminate boundary conditions from the argument, we allow mentioning all values
with Z. In the specifications and commands, we only use for indices .
true
Max
75
76 CHAPTER 10. ADD AUXILIARY INFORMATION TO THE INVARIANT
We need a thorough preparation before turning to the roadmap. We apply the heuristic
replacing a constant by a variable, and replace the constant by a variable . We therefore
define the function
Max
Max Max
Max Min
Max (10.1)
Min
{ (10.1) }
Max
{ splitting: or }
(10.2)
max Max
{ (10.1) }
max
{ definition of }
{ splitting: or ; use }
(10.3)
{ definition of }
{ definition of }
Min
{ Suppose ; splitting with ; use }
min Min
{ use (10.3) }
min Min
{ Suppose }
min Min
{ definition of ; use and hence }
If , we find
{ see above }
min Min
{ , hence empty domain; arithmetic }
(10.4)
Formula (10.4) together with are enough for recursive computation of function
. We can now turn to the roadmap.
true
( see above; N )
; ;
Step 3. In view of the invariant, the guard, and the initialization, we choose vf .
Boundedness is proved in
{ definition vf }
vf
Step 4. In the body of the repetition, must be incremented, while preserving invariant
. This is done in
vf
( definitions of , , vf, use )
( formula (10.4) )
(
formula (10.2) and arithmetic )
max
max
vf
Step 5. Conclusion.
const N
const array of Z
var Z
true
; ;
( vf )
while do
10.2. LONGEST LEFT-MINIMAL SEGMENTS 79
max
end
Max
where
Max
max (10.5)
Z (10.6)
(10.7)
{ definition of }
{ Suppose . Splitting: or }
{ definition of }
This proves
(10.8)
80 CHAPTER 10. ADD AUXILIARY INFORMATION TO THE INVARIANT
We are not yet ready to derive a recurrence relation for , and therefore characterize
the value of in a different way. For Z, we have
{ ( ) because }
{ ( ) Assume that for some .}
{ Then there is with Min .}
{ By , this implies . }
{ (10.9) }
const N
const array of Z
var Z
true
10.2. LONGEST LEFT-MINIMAL SEGMENTS 81
In view of formulas (10.5) and (10.10), however, we introduce an auxiliary variable with
the additional invariant . This leads to the invariant and guard:
true
( declaration of and formula (10.7) )
; ;
vf
( definitions of , , vf ; arithmetic )
( formula (10.10) )
max
max
vf
Step 5. Conclusion.
const N
const array of Z
var Z
true
; ;
82 CHAPTER 10. ADD AUXILIARY INFORMATION TO THE INVARIANT
( vf )
while do
max
end
10.3 Exercises
In each case, first give a formal specification. We use the array declared above.
Exercise 10.1. Determine a command to compute
Take care with the inequality signs. Compare this exercise with Exercise 7.14.
Exercise 10.2. Determine a command to compute
For this purpose, express this value as where the argument of replaces the
lower bound 0 for the bound variable . Determine a recurrence equation of
expressed in and , and for .
Exercise 10.3. Assume . Design a command to compute the greatest length of the
increasing segments of array .
Exercise 10.4. Assume . As a variation on Chapter 10.2, design a command with
the postcondition
Max , where
Min
Exercise 10.6. Determine the maximal sum of the segments of array , i.e., establish the
postcondition
Max
Exercise 10.7. Determine the maximal sum of the positive segments of array .
Exercise 10.8. The greatest common divisor of natural numbers and can be expressed
as an integral linear combination of and . Design a command that proves
this by determining such integers and .
For this purpose, introduce variables and with the invariant that and are linear
combinations of and (initially and ). Establish the postcondition
gcd .
Exercise 10.9. Design a command to compute the greatest length of segments of array
for which all elements are different. For this purpose, use the type set of Z with
suitable corresponding operations.
10.3. EXERCISES 83
Exercise 10.10. Extend the program of Chapter 10.1 in such a way that it also yields
bounds and of a positive segment of maximal length. You need not provide a complete
derivation. It suffices to give the additional conjuncts of the invariant with the proofs of
the steps 1 and 2, and the result in step 5. Active finalization is an option.
Exercise 10.11. Assume . Design a command to compute the greatest length of
segments of array with at most two different values.
Exercise 10.12. The same as the previous exercise, but now for segments with at most
three different values. Would you be able to generalize this to different values?
Exercise 10.13. Given are two arrays declared in
const N array of R
The time complexity of command should be . First derive recurrence relations for
relevant functions and give a formal specification.
Exercise 10.14. Given are arrays and of real numbers. All elements of are nonnega-
tive.
const N array of R
Max
In the program (and its derivation), you may apply the binary operator max and the
constants and . The time complexity of command should be .
(b) What goes wrong when some elements of array are negative? Can this be repaired?
For instance, when all elements of array are nonnegative?
Index
, 11, 21 expression, 10
, 11
, 11 finalization, 32
skip axiom, 20 generalization, 42
Max, 12 guard, 19
Min, 12
div, 12 heuristic, 41
gcd, 36 Hoare triple, 16
max, 13 Horner’s scheme, 48
min, 13
mod, 12 identifier, 9
implication, universally valid, 11
active finalization, 38 increasing, 51
annotated command, 23 initialization, 32
annotated linear proof, 11 interval, 11
ascending, 51 invariant, 31
assignment, 19 isolating a conjunct, 41
assignment axiom, 20
king’s problem, 56
binary search, 54
monotonic, 51
biregular, 17
body, 19 operational reasoning, 6
branch, 19
postcondition, 16
command, 15 postregular, 17
composition rule, 20 precondition, 16
conclusion, 20 premise, 20
condition, 10 preparation of assignment, 24
conditional expression, 10 preregular, 17
conditional command, 19 priority, 10
conditional rule, 20 program condition, 19
constant, 9 program expression, 19
contour line, 57 pseudocode, 19
correct, 5
recurrence relations, 35
declaration, 9 repetition, 19
decreasing, 51 repetition rule, 21
descending, 51 replacing a constant by a variable, 42
division with remainder, 12 replacing expression by variable, 41
domain, 9 roadmap, 31
rule of thumb, 41
empty command, 19
equivalence symbol, 11 saddleback search, 57
Euclid’s algorithm, 36 segment, 65
exponentiation, 33 sequence, 13
84
INDEX 85
sequential composition, 19
set, 10
shrinking rectangle, 58
simultaneous assignment, 27
specification, 16
specification constant, 9
specification language, 9
splitting a variable, 42
state, 15
strengthening of the precondition, 21
variable, 9
variant function, 21