6 This chapter describes the WhyML specification and programming language.
7 A WhyML source file has suffix :file:`.mlw`. It contains a list of modules.
8 Each module contains a list of declarations. These include
10 - Logical declarations:
12 - types (abstract, record, or algebraic data types);
14 - functions and predicates;
16 - axioms, lemmas, and goals.
18 - Program data types. In a record type declaration, some fields can be
19 declared ``mutable`` and/or ``ghost``. Additionally, a record type
20 can be declared ``abstract`` (its fields are only visible in ghost
21 code / specification).
23 - Program declarations and definitions. Programs include many
24 constructs with no counterpart in the logic:
26 - mutable field assignment;
34 - local and anonymous functions;
36 - ghost parameters and ghost code;
38 - annotations: pre- and postconditions, assertions, loop invariants.
40 A program may be non-terminating. (But termination can be proved if
43 Command-line tools described in the previous chapter also apply to files
44 containing programs. For instance
50 displays the verification conditions for programs contained in file
51 :file:`myfile.mlw`, and
55 why3 prove -P alt-ergo myfile.mlw
57 runs the SMT solver Alt-Ergo on these verification conditions. All this
58 can be performed within the GUI tool :why3:tool:`why3 ide` as well. See
59 :numref:`chap.manpages` for more details regarding command lines.
61 As an introduction to WhyML, we use a small logical puzzle
62 (:numref:`sec.einstein`) and then the five problems from the VSTTE 2010
63 verification competition :cite:`vstte10comp`. The source
64 code for all these examples is contained in Why3’s distribution, in
65 sub-directory :file:`examples/`. Look for files :file:`logic/einstein.why` and
66 :file:`vstte10_xxx.mlw`.
68 .. index:: Einstein’s problem
71 Problem 0: Einstein’s Problem
72 -----------------------------
74 Let us use Why3 to solve a little puzzle known as “Einstein’s logic
75 problem”. (This Why3 example was contributed by Stéphane Lescuyer.)
76 The problem is stated as follows. Five persons, of five
77 different nationalities, live in five houses in a row, all painted with
78 different colors. These five persons own different pets, drink different
79 beverages, and smoke different brands of cigars. We are given the
80 following information:
82 - The Englishman lives in a red house;
86 - The Dane drinks tea;
88 - The green house is on the left of the white one;
90 - The green house’s owner drinks coffee;
92 - The person who smokes Pall Mall has birds;
94 - The yellow house’s owner smokes Dunhill;
96 - In the house in the center lives someone who drinks milk;
98 - The Norwegian lives in the first house;
100 - The man who smokes Blends lives next to the one who has cats;
102 - The man who owns a horse lives next to the one who smokes Dunhills;
104 - The man who smokes Blue Masters drinks beer;
106 - The German smokes Prince;
108 - The Norwegian lives next to the blue house;
110 - The man who smokes Blends has a neighbour who drinks water.
112 The question is: What is the nationality of the fish’s owner?
114 We start by introducing a general-purpose theory defining the notion of
115 *bijection*, as two abstract types together with two functions from one
116 to the other and two axioms stating that these functions are inverse of
119 .. code-block:: whyml
128 axiom To_of : forall x : t. to_ (of x) = x
129 axiom Of_to : forall y : u. of (to_ y) = y
132 We now start a new theory, ``Einstein``, which will contain all the
133 individuals of the problem.
135 .. code-block:: whyml
139 First, we introduce enumeration types for houses, colors, persons,
140 drinks, cigars, and pets.
142 .. code-block:: whyml
144 type house = H1 | H2 | H3 | H4 | H5
145 type color = Blue | Green | Red | White | Yellow
146 type person = Dane | Englishman | German | Norwegian | Swede
147 type drink = Beer | Coffee | Milk | Tea | Water
148 type cigar = Blend | BlueMaster | Dunhill | PallMall | Prince
149 type pet = Birds | Cats | Dogs | Fish | Horse
151 We now express that each house is associated bijectively to a color, by
152 *cloning* the ``Bijection`` theory appropriately.
154 .. code-block:: whyml
156 clone Bijection as Color with type t = house, type u = color
158 Cloning a theory makes a copy of all its declarations, possibly in
159 combination with a user-provided substitution
160 (see :numref:`Module Cloning`).
161 Here we substitute type
162 ``house`` for type ``t`` and type ``color`` for type ``u``. As a result,
163 we get two new functions, namely ``Color.of`` and ``Color.to_``, from
164 houses to colors and colors to houses, respectively, and two new axioms
165 relating them. Similarly, we express that each house is associated
166 bijectively to a person
168 .. code-block:: whyml
170 clone Bijection as Owner with type t = house, type u = person
172 and that drinks, cigars, and pets are all associated bijectively to
175 .. code-block:: whyml
177 clone Bijection as Drink with type t = person, type u = drink
178 clone Bijection as Cigar with type t = person, type u = cigar
179 clone Bijection as Pet with type t = person, type u = pet
181 Next, we need a way to state that a person lives next to another. We
182 first define a predicate ``leftof`` over two houses.
184 .. code-block:: whyml
186 predicate leftof (h1 h2 : house) =
195 Note how we advantageously used pattern matching, with an or-pattern for
196 the four positive cases and a universal pattern for the remaining 21
197 cases. It is then immediate to define a ``neighbour`` predicate over two
198 houses, which completes theory ``Einstein``.
200 .. code-block:: whyml
202 predicate rightof (h1 h2 : house) =
204 predicate neighbour (h1 h2 : house) =
205 leftof h1 h2 \/ rightof h1 h2
208 The next theory contains the 15 hypotheses. It starts by importing
211 .. code-block:: whyml
216 Then each hypothesis is stated in terms of ``to_`` and ``of`` functions.
217 For instance, the hypothesis “The Englishman lives in a red house” is
218 declared as the following axiom.
220 .. code-block:: whyml
222 axiom Hint1: Color.of (Owner.to_ Englishman) = Red
224 And so on for all other hypotheses, up to “The man who smokes Blends has
225 a neighbour who drinks water”, which completes this theory.
227 .. code-block:: whyml
231 neighbour (Owner.to_ (Cigar.to_ Blend)) (Owner.to_ (Drink.to_ Water))
234 Finally, we declare the goal in a fourth theory:
236 .. code-block:: whyml
242 goal G: Pet.to_ Fish = German
245 and we can use Why3 to discharge this goal with any prover of our
248 .. code-block:: console
250 $ why3 prove -P alt-ergo einstein.why
251 einstein.why Goals G: Valid (1.27s, 989 steps)
253 The source code for this puzzle is available in the source distribution
254 of Why3, in file :file:`examples/logic/einstein.why`.
258 Problem 1: Sum and Maximum
259 --------------------------
261 Let us now move to the problems of the VSTTE 2010 verification
262 competition :cite:`vstte10comp`. The first problem is stated
265 Given an :math:`N`-element array of natural numbers, write a program
266 to compute the sum and the maximum of the elements in the array.
268 We assume :math:`N \ge 0` and :math:`a[i] \ge 0` for
269 :math:`0 \le i < N`, as precondition, and we have to prove the following
272 .. math:: sum \le N \times max.
274 In a file :file:`max_sum.mlw`, we start a new module:
276 .. code-block:: whyml
280 We are obviously needing arithmetic, so we import the corresponding
281 theory, exactly as we would do within a theory definition:
283 .. code-block:: whyml
287 We are also going to use references and arrays from Why3 standard
288 library, so we import the corresponding modules:
290 .. code-block:: whyml
295 Modules ``Ref`` and ``Array`` respectively provide a type ``ref ’a`` for
296 references and a type ``array ’a`` for arrays, together with useful
297 operations and traditional syntax. They are loaded from the WhyML files
298 :file:`ref.mlw` and :file:`array.mlw` in the standard library.
300 We are now in position to define a program function ``max_sum``. A
301 function definition is introduced with the keyword ``let``. In our case,
302 it introduces a function with two arguments, an array ``a`` and its size
305 .. code-block:: whyml
307 let max_sum (a: array int) (n: int) : (int, int) = ...
309 (There is a function ``length`` to get the size of an array but we add
310 this extra parameter ``n`` to stay close to the original problem
311 statement.) The function body is a Hoare triple, that is a precondition,
312 a program expression, and a postcondition.
314 .. code-block:: whyml
316 let max_sum (a: array int) (n: int) : (int, int)
317 requires { n = length a }
318 requires { forall i. 0 <= i < n -> a[i] >= 0 }
319 ensures { let (sum, max) = result in sum <= n * max }
322 The first precondition expresses that ``n`` is equal to the length of
323 ``a`` (this will be needed for verification conditions related to array
324 bound checking). The second precondition expresses that all elements of
325 ``a`` are non-negative. The postcondition decomposes the value returned
326 by the function as a pair of integers ``(sum, max)`` and states the
329 .. code-block:: whyml
331 returns { sum, max -> sum <= n * max }
333 We are now left with the function body itself, that is a code computing
334 the sum and the maximum of all elements in ``a``. With no surprise, it
335 is as simple as introducing two local references
337 .. code-block:: whyml
342 scanning the array with a ``for`` loop, updating ``max`` and ``sum``
344 .. code-block:: whyml
346 for i = 0 to n - 1 do
347 if !max < a[i] then max := a[i];
351 and finally returning the pair of the values contained in ``sum`` and
354 .. code-block:: whyml
358 This completes the code for function ``max_sum``. As such, it cannot be
359 proved correct, since the loop is still lacking a loop invariant. In
360 this case, the loop invariant is as simple as ``!sum <= i * !max``,
361 since the postcondition only requires us to prove ``sum <= n * max``.
362 The loop invariant is introduced with the keyword ``invariant``,
363 immediately after the keyword ``do``:
365 .. code-block:: whyml
367 for i = 0 to n - 1 do
368 invariant { !sum <= i * !max }
372 There is no need to introduce a variant, as the termination of a ``for``
373 loop is automatically guaranteed. This completes module ``MaxAndSum``,
376 .. code-block:: whyml
384 let max_sum (a: array int) (n: int) : (int, int)
385 requires { n = length a }
386 requires { forall i. 0 <= i < n -> a[i] >= 0 }
387 returns { sum, max -> sum <= n * max }
390 for i = 0 to n - 1 do
391 invariant { !sum <= i * !max }
392 if !max < a[i] then max := a[i];
399 We can now proceed to its verification. Running :program:`why3`, or better
400 :why3:tool:`why3 ide`, on file :file:`max_sum.mlw` shows a single verification
401 condition with name ``WP max_sum``. Discharging this verification
402 condition requires a little bit of non-linear arithmetic. Thus some SMT
403 solvers may fail at proving it, but other succeed, *e.g.*, CVC4.
405 Note: It is of course possible to *execute* the code to test it,
406 before or after you prove it correct. This is detailed in
407 :numref:`sec.execute`.
409 .. index:: auto-dereference
410 .. rubric:: Auto-dereference
412 Why3 features an auto-dereferencing mechanism, which simplifies the use of
413 references. When a reference is introduced using ``let ref x`` (instead
414 of ``let x = ref``), the use of operator ``!`` to access its value
415 is not needed anymore. For instance, we can rewrite the program above
416 as follows (the contract being unchanged and omitted):
418 .. code-block:: whyml
420 let max_sum (a: array int) (n: int) : (sum: int, max: int)
423 for i = 0 to n - 1 do
424 invariant { sum <= i * max }
425 if max < a[i] then max <- a[i];
430 Note that use of operator ``<-`` for assignment (instead of ``:=``)
431 and the absence of ``!`` both in the loop invariant and in the program.
432 See :numref:`auto-dereference` for more details about the
433 auto-dereferencing mechanism.
436 Problem 2: Inverting an Injection
437 ---------------------------------
439 The second problem is stated as follows:
441 Invert an injective array :math:`A` on :math:`N` elements in the
442 subrange from :math:`0` to :math:`N - 1`, the output array :math:`B`
443 must be such that :math:`B[A[i]] = i` for :math:`0 \le i < N`.
445 The code is immediate, since it is as simple as
447 .. code-block:: whyml
449 for i = 0 to n - 1 do b[a[i]] <- i done
451 so it is more a matter of specification and of getting the proof done
452 with as much automation as possible. In a new file, we start a new
453 module and we import arithmetic and arrays:
455 .. code-block:: whyml
457 module InvertingAnInjection
461 It is convenient to introduce predicate definitions for the properties
462 of being injective and surjective. These are purely logical
465 .. code-block:: whyml
467 predicate injective (a: array int) (n: int) =
468 forall i j. 0 <= i < n -> 0 <= j < n -> i <> j -> a[i] <> a[j]
470 predicate surjective (a: array int) (n: int) =
471 forall i. 0 <= i < n -> exists j: int. (0 <= j < n /\ a[j] = i)
473 It is also convenient to introduce the predicate “being in the subrange
474 from 0 to :math:`n-1`”:
476 .. code-block:: whyml
478 predicate range (a: array int) (n: int) =
479 forall i. 0 <= i < n -> 0 <= a[i] < n
481 Using these predicates, we can formulate the assumption that any
482 injective array of size :math:`n` within the range :math:`0..n-1` is
485 .. code-block:: whyml
487 lemma injective_surjective:
488 forall a: array int, n: int.
489 injective a n -> range a n -> surjective a n
491 We declare it as a lemma rather than as an axiom, since it is actually
492 provable. It requires induction and can be proved using the Coq proof
493 assistant for instance. Finally we can give the code a specification,
494 with a loop invariant which simply expresses the values assigned to
497 .. code-block:: whyml
499 let inverting (a: array int) (b: array int) (n: int)
500 requires { n = length a = length b }
501 requires { injective a n /\ range a n }
502 ensures { injective b n }
503 = for i = 0 to n - 1 do
504 invariant { forall j. 0 <= j < i -> b[a[j]] = j }
508 Here we chose to have array ``b`` as argument; returning a freshly
509 allocated array would be equally simple. The whole module is given below.
510 The verification conditions for function
511 ``inverting`` are easily discharged automatically, thanks to the lemma.
513 .. code-block:: whyml
515 module InvertingAnInjection
520 predicate injective (a: array int) (n: int) =
521 forall i j. 0 <= i < n -> 0 <= j < n -> i <> j -> a[i] <> a[j]
523 predicate surjective (a: array int) (n: int) =
524 forall i. 0 <= i < n -> exists j: int. (0 <= j < n /\ a[j] = i)
526 predicate range (a: array int) (n: int) =
527 forall i. 0 <= i < n -> 0 <= a[i] < n
529 lemma injective_surjective:
530 forall a: array int, n: int.
531 injective a n -> range a n -> surjective a n
533 let inverting (a: array int) (b: array int) (n: int)
534 requires { n = length a = length b }
535 requires { injective a n /\ range a n }
536 ensures { injective b n }
537 = for i = 0 to n - 1 do
538 invariant { forall j. 0 <= j < i -> b[a[j]] = j }
544 Problem 3: Searching a Linked List
545 ----------------------------------
547 The third problem is stated as follows:
549 Given a linked list representation of a list of integers, find the
550 index of the first element that is equal to 0.
552 More precisely, the specification says
554 You have to show that the program returns an index *i* equal
555 to the length of the list if there is no such element. Otherwise,
556 the *i*-th element of the list must be equal to 0, and all the
557 preceding elements must be non-zero.
559 Since the list is not mutated, we can use the algebraic data type of
560 polymorphic lists from Why3’s standard library, defined in theory
561 ``list.List``. It comes with other handy theories: ``list.Length``,
562 which provides a function ``length``, and ``list.Nth``, which provides a
563 function ``nth`` for the nth element of a list. The latter
564 returns an option type, depending on whether the index is meaningful or
567 .. code-block:: whyml
569 module SearchingALinkedList
573 use export list.Length
576 It is helpful to introduce two predicates: a first one for a successful
579 .. code-block:: whyml
581 predicate zero_at (l: list int) (i: int) =
582 nth i l = Some 0 /\ forall j. 0 <= j < i -> nth j l <> Some 0
584 and a second one for a non-successful search,
586 .. code-block:: whyml
588 predicate no_zero (l: list int) =
589 forall j. 0 <= j < length l -> nth j l <> Some 0
591 We are now in position to give the code for the search function. We
592 write it as a recursive function ``search`` that scans a list for the
595 .. code-block:: whyml
597 let rec search (i: int) (l: list int) : int =
600 | Cons x r -> if x = 0 then i else search (i+1) r
603 Passing an index ``i`` as first argument allows to perform a tail call.
604 A simpler code (yet less efficient) would return 0 in the first branch
605 and ``1 + search ...`` in the second one, avoiding the extra argument
608 We first prove the termination of this recursive function. It amounts to
609 giving it a *variant*, that is a value that strictly decreases at each
610 recursive call with respect to some well-founded ordering. Here it is as
611 simple as the list ``l`` itself:
613 .. code-block:: whyml
615 let rec search (i: int) (l: list int) : int variant { l } = ...
617 It is worth pointing out that variants are not limited to values of
618 algebraic types. A non-negative integer term (for example, ``length l``)
619 can be used, or a term of any other type equipped with a well-founded
620 order relation. Several terms can be given, separated with commas, for
621 lexicographic ordering.
623 There is no precondition for function ``search``. The postcondition
624 expresses that either a zero value is found, and consequently the value
625 returned is bounded accordingly,
627 .. code-block:: whyml
629 i <= result < i + length l /\ zero_at l (result - i)
631 or no zero value was found, and thus the returned value is exactly ``i``
632 plus the length of ``l``:
634 .. code-block:: whyml
636 result = i + length l /\ no_zero l
638 Solving the problem is simply a matter of calling ``search`` with 0 as
639 first argument. The code is given below. The
640 verification conditions are all discharged automatically.
642 .. code-block:: whyml
644 module SearchingALinkedList
648 use export list.Length
651 predicate zero_at (l: list int) (i: int) =
652 nth i l = Some 0 /\ forall j. 0 <= j < i -> nth j l <> Some 0
654 predicate no_zero (l: list int) =
655 forall j. 0 <= j < length l -> nth j l <> Some 0
657 let rec search (i: int) (l: list int) : int variant { l }
658 ensures { (i <= result < i + length l /\ zero_at l (result - i))
659 \/ (result = i + length l /\ no_zero l) }
662 | Cons x r -> if x = 0 then i else search (i+1) r
665 let search_list (l: list int) : int
666 ensures { (0 <= result < length l /\ zero_at l result)
667 \/ (result = length l /\ no_zero l) }
672 Alternatively, we can implement the search with a ``while`` loop. To do
673 this, we need to import references from the standard library, together
674 with theory ``list.HdTl`` which defines functions ``hd`` and ``tl`` over
677 .. code-block:: whyml
682 Being partial functions, ``hd`` and ``tl`` return options. For the
683 purpose of our code, though, it is simpler to have functions which do
684 not return options, but have preconditions instead. Such a function
685 ``head`` is defined as follows:
687 .. code-block:: whyml
689 let head (l: list 'a) : 'a
690 requires { l <> Nil } ensures { hd l = Some result }
691 = match l with Nil -> absurd | Cons h _ -> h end
693 The program construct ``absurd`` denotes an unreachable piece of code.
694 It generates the verification condition ``false``, which is here
695 provable using the precondition (the list cannot be ``Nil``). Function
696 ``tail`` is defined similarly:
698 .. code-block:: whyml
700 let tail (l: list 'a) : list 'a
701 requires { l <> Nil } ensures { tl l = Some result }
702 = match l with Nil -> absurd | Cons _ t -> t end
704 Using ``head`` and ``tail``, it is straightforward to implement the
705 search as a ``while`` loop. It uses a local reference ``i`` to store the
706 index and another local reference ``s`` to store the list being scanned.
707 As long as ``s`` is not empty and its head is not zero, it increments
708 ``i`` and advances in ``s`` using function ``tail``.
710 .. code-block:: whyml
712 let search_loop (l: list int) : int
713 ensures { ... same postcondition as in search_list ... }
716 while not (is_nil !s) && head !s <> 0 do
724 The postcondition is exactly the same as for function ``search_list``.
725 The termination of the ``while`` loop is ensured using a variant,
726 exactly as for a recursive function. Such a variant must strictly
727 decrease at each execution of the loop body. The reader is invited to
728 figure out the loop invariant.
733 The fourth problem is probably the most challenging one. We have to
734 verify the implementation of a program which solves the *N*-queens
735 puzzle: place *N* queens on an *N*×*N* chess board so
736 that no queen can capture another one with a legal move. The program
737 should return a placement if there is a solution and indicates that
738 there is no solution otherwise. A placement is a *N*-element array
739 which assigns the queen on row *i* to its column. Thus we start
740 our module by importing arithmetic and arrays:
742 .. code-block:: whyml
748 The code is a simple backtracking algorithm, which tries to put a queen
749 on each row of the chess board, one by one (there is basically no better
750 way to solve the *N*-queens puzzle). A building block is a
751 function which checks whether the queen on a given row may attack
752 another queen on a previous row. To verify this function, we first
753 define a more elementary predicate, which expresses that queens on row
754 ``pos`` and ``q`` do no attack each other:
756 .. code-block:: whyml
758 predicate consistent_row (board: array int) (pos: int) (q: int) =
759 board[q] <> board[pos] /\
760 board[q] - board[pos] <> pos - q /\
761 board[pos] - board[q] <> pos - q
763 Then it is possible to define the consistency of row ``pos`` with
764 respect to all previous rows:
766 .. code-block:: whyml
768 predicate is_consistent (board: array int) (pos: int) =
769 forall q. 0 <= q < pos -> consistent_row board pos q
771 Implementing a function which decides this predicate is another matter.
772 In order for it to be efficient, we want to return ``False`` as soon as
773 a queen attacks the queen on row ``pos``. We use an exception for this
774 purpose and it carries the row of the attacking queen:
776 .. code-block:: whyml
778 exception Inconsistent int
780 The check is implemented by a function ``check_is_consistent``, which
781 takes the board and the row ``pos`` as arguments, and scans rows from 0
782 to ``pos - 1`` looking for an attacking queen. As soon as one is found,
783 the exception is raised. It is caught immediately outside the loop and
784 ``False`` is returned. Whenever the end of the loop is reached, ``True``
787 .. code-block:: whyml
789 let check_is_consistent (board: array int) (pos: int) : bool
790 requires { 0 <= pos < length board }
791 ensures { result <-> is_consistent board pos }
793 for q = 0 to pos - 1 do
795 forall j:int. 0 <= j < q -> consistent_row board pos j
798 let bpos = board[pos] in
799 if bq = bpos then raise (Inconsistent q);
800 if bq - bpos = pos - q then raise (Inconsistent q);
801 if bpos - bq = pos - q then raise (Inconsistent q)
804 with Inconsistent q ->
805 assert { not (consistent_row board pos q) };
809 The assertion in the exception handler is a cut for SMT solvers. This
810 first part of the solution is given below.
812 .. code-block:: whyml
818 predicate consistent_row (board: array int) (pos: int) (q: int) =
819 board[q] <> board[pos] /\
820 board[q] - board[pos] <> pos - q /\
821 board[pos] - board[q] <> pos - q
823 predicate is_consistent (board: array int) (pos: int) =
824 forall q. 0 <= q < pos -> consistent_row board pos q
826 exception Inconsistent int
828 let check_is_consistent (board: array int) (pos: int)
829 requires { 0 <= pos < length board }
830 ensures { result <-> is_consistent board pos }
832 for q = 0 to pos - 1 do
834 forall j:int. 0 <= j < q -> consistent_row board pos j
837 let bpos = board[pos] in
838 if bq = bpos then raise (Inconsistent q);
839 if bq - bpos = pos - q then raise (Inconsistent q);
840 if bpos - bq = pos - q then raise (Inconsistent q)
843 with Inconsistent q ->
844 assert { not (consistent_row board pos q) };
848 We now proceed with the verification of the backtracking algorithm. The
849 specification requires us to define the notion of solution, which is
850 straightforward using the predicate ``is_consistent`` above. However,
851 since the algorithm will try to complete a given partial solution, it is
852 more convenient to define the notion of partial solution, up to a given
853 row. It is even more convenient to split it in two predicates, one
854 related to legal column values and another to consistency of rows:
856 .. code-block:: whyml
858 predicate is_board (board: array int) (pos: int) =
859 forall q. 0 <= q < pos -> 0 <= board[q] < length board
861 predicate solution (board: array int) (pos: int) =
862 is_board board pos /\
863 forall q. 0 <= q < pos -> is_consistent board q
865 The algorithm will not mutate the partial solution it is given and, in
866 case of a search failure, will claim that there is no solution extending
867 this prefix. For this reason, we introduce a predicate comparing two
868 chess boards for equality up to a given row:
870 .. code-block:: whyml
872 predicate eq_board (b1 b2: array int) (pos: int) =
873 forall q. 0 <= q < pos -> b1[q] = b2[q]
875 The search itself makes use of an exception to signal a successful
878 .. code-block:: whyml
882 The backtracking code is a recursive function ``bt_queens`` which takes
883 the chess board, its size, and the starting row for the search. The
884 termination is ensured by the obvious variant ``n - pos``.
886 .. code-block:: whyml
888 let rec bt_queens (board: array int) (n: int) (pos: int) : unit
891 The precondition relates ``board``, ``pos``, and ``n`` and requires
892 ``board`` to be a solution up to ``pos``:
894 .. code-block:: whyml
896 requires { 0 <= pos <= n = length board }
897 requires { solution board pos }
899 The postcondition is twofold: either the function exits normally and
900 then there is no solution extending the prefix in ``board``, which has
901 not been modified; or the function raises ``Solution`` and we have a
902 solution in ``board``.
904 .. code-block:: whyml
906 ensures { eq_board board (old board) pos }
907 ensures { forall b:array int. length b = n -> is_board b n ->
908 eq_board board b pos -> not (solution b n) }
909 raises { Solution -> solution board n }
912 Whenever we reach the end of the chess board, we have found a solution
913 and we signal it using exception ``Solution``:
915 .. code-block:: whyml
917 if pos = n then raise Solution;
919 Otherwise we scan all possible positions for the queen on row ``pos``
922 .. code-block:: whyml
924 for i = 0 to n - 1 do
926 The loop invariant states that we have not modified the solution prefix
927 so far, and that we have not found any solution that would extend this
928 prefix with a queen on row ``pos`` at a column below ``i``:
930 .. code-block:: whyml
932 invariant { eq_board board (old board) pos }
933 invariant { forall b:array int. length b = n -> is_board b n ->
934 eq_board board b pos -> 0 <= b[pos] < i -> not (solution b n) }
936 Then we assign column ``i`` to the queen on row ``pos`` and we check for
937 a possible attack with ``check_is_consistent``. If not, we call
938 ``bt_queens`` recursively on the next row.
940 .. code-block:: whyml
943 if check_is_consistent board pos then bt_queens board n (pos + 1)
946 This completes the loop and function ``bt_queens`` as well. Solving the
947 puzzle is a simple call to ``bt_queens``, starting the search on row 0.
948 The postcondition is also twofold, as for ``bt_queens``, yet slightly
951 .. code-block:: whyml
953 let queens (board: array int) (n: int) : unit
954 requires { length board = n }
955 ensures { forall b:array int.
956 length b = n -> is_board b n -> not (solution b n) }
957 raises { Solution -> solution board n }
958 = bt_queens board n 0
960 This second part of the solution is given below. With
961 the help of a few auxiliary lemmas — not given here but available from
962 Why3’s sources — the verification conditions are all discharged
963 automatically, including the verification of the lemmas themselves.
965 .. code-block:: whyml
967 predicate is_board (board: array int) (pos: int) =
968 forall q. 0 <= q < pos -> 0 <= board[q] < length board
970 predicate solution (board: array int) (pos: int) =
971 is_board board pos /\
972 forall q. 0 <= q < pos -> is_consistent board q
974 predicate eq_board (b1 b2: array int) (pos: int) =
975 forall q. 0 <= q < pos -> b1[q] = b2[q]
979 let rec bt_queens (board: array int) (n: int) (pos: int) : unit
981 requires { 0 <= pos <= n = length board }
982 requires { solution board pos }
983 ensures { eq_board board (old board) pos }
984 ensures { forall b:array int. length b = n -> is_board b n ->
985 eq_board board b pos -> not (solution b n) }
986 raises { Solution -> solution board n }
987 = if pos = n then raise Solution;
988 for i = 0 to n - 1 do
989 invariant { eq_board board (old board) pos }
990 invariant { forall b:array int. length b = n -> is_board b n ->
991 eq_board board b pos -> 0 <= b[pos] < i -> not (solution b n) }
993 if check_is_consistent board pos then bt_queens board n (pos + 1)
996 let queens (board: array int) (n: int) : unit
997 requires { length board = n }
998 ensures { forall b:array int.
999 length b = n -> is_board b n -> not (solution b n) }
1000 raises { Solution -> solution board n }
1001 = bt_queens board n 0
1007 Problem 5: Amortized Queue
1008 --------------------------
1010 The last problem consists in verifying the implementation of a
1011 well-known purely applicative data structure for queues. A queue is
1012 composed of two lists, *front* and *rear*. We push elements at the head
1013 of list *rear* and pop them off the head of list *front*. We maintain
1014 that the length of *front* is always greater or equal to the length of
1015 *rear*. (See for instance Okasaki’s *Purely Functional Data
1016 Structures* :cite:`okasaki98` for more details.)
1018 We have to implement operations ``empty``, ``head``, ``tail``, and
1019 ``enqueue`` over this data type, to show that the invariant over lengths
1020 is maintained, and finally
1021 to show that a client invoking these operations observes an abstract
1022 queue given by a sequence.
1024 In a new module, we import arithmetic and theory ``list.ListRich``, a
1025 combo theory that imports all list operations we will require: length,
1026 reversal, and concatenation.
1028 .. code-block:: whyml
1030 module AmortizedQueue
1033 use export list.ListRich
1035 The queue data type is naturally introduced as a polymorphic record
1036 type. The two list lengths are explicitly stored, for greater
1039 .. code-block:: whyml
1041 type queue 'a = { front: list 'a; lenf: int;
1042 rear : list 'a; lenr: int; }
1043 invariant { length front = lenf >= length rear = lenr }
1044 by { front = Nil; lenf = 0; rear = Nil; lenr = 0 }
1046 The type definition is accompanied with an invariant — a logical
1047 property imposed on any value of the type. Why3 assumes that any
1048 queue satisfies the invariant at any function entry and
1049 it requires that any queue satisfies the invariant at function exit
1050 (be the queue created or modified).
1051 The ``by`` clause ensures the non-vacuity of this type with
1052 invariant. If you omit it, a goal with an existential statement is
1053 generated. (See :numref:`Record Types` for more details about record
1054 types with invariants.)
1056 For the purpose of the specification, it is convenient to introduce a
1057 function ``sequence`` which builds the sequence of elements of a queue,
1058 that is the front list concatenated to the reversed rear list.
1060 .. code-block:: whyml
1062 function sequence (q: queue 'a) : list 'a = q.front ++ reverse q.rear
1064 It is worth pointing out that this function can only be used in
1065 specifications. We start with the easiest operation: building the empty
1068 .. code-block:: whyml
1070 let empty () : queue 'a
1071 ensures { sequence result = Nil }
1072 = { front = Nil; lenf = 0; rear = Nil; lenr = 0 }
1074 The postcondition states that the returned queue represents the empty
1075 sequence. Another postcondition, saying that the returned queue
1076 satisfies the type invariant, is implicit. Note the cast to type
1077 ``queue 'a``. It is required, for the type checker not to complain about
1078 an undefined type variable.
1080 The next operation is ``head``, which returns the first element from a
1081 given queue ``q``. It naturally requires the queue to be non empty,
1082 which is conveniently expressed as ``sequence q`` not being ``Nil``.
1084 .. code-block:: whyml
1086 let head (q: queue 'a) : 'a
1087 requires { sequence q <> Nil }
1088 ensures { hd (sequence q) = Some result }
1089 = let Cons x _ = q.front in x
1091 The fact that the argument ``q`` satisfies the type invariant is
1092 implicitly assumed. The type invariant is required to prove the
1093 absurdity of ``q.front`` being ``Nil`` (otherwise, ``sequence q`` would
1094 be ``Nil`` as well).
1096 The next operation is ``tail``, which removes the first element from a
1097 given queue. This is more subtle than ``head``, since we may have to
1098 re-structure the queue to maintain the invariant. Since we will have to
1099 perform a similar operation when implementing operation ``enqueue``
1100 later, it is a good idea to introduce a smart constructor ``create``
1101 that builds a queue from two lists while ensuring the invariant. The
1102 list lengths are also passed as arguments, to avoid unnecessary
1105 .. code-block:: whyml
1107 let create (f: list 'a) (lf: int) (r: list 'a) (lr: int) : queue 'a
1108 requires { lf = length f /\ lr = length r }
1109 ensures { sequence result = f ++ reverse r }
1111 { front = f; lenf = lf; rear = r; lenr = lr }
1113 let f = f ++ reverse r in
1114 { front = f; lenf = lf + lr; rear = Nil; lenr = 0 }
1116 If the invariant already holds, it is simply a matter of building the
1117 record. Otherwise, we empty the rear list and build a new front list as
1118 the concatenation of list ``f`` and the reversal of list ``r``. The
1119 principle of this implementation is that the cost of this reversal will
1120 be amortized over all queue operations. Implementing function ``tail``
1121 is now straightforward and follows the structure of function ``head``.
1123 .. code-block:: whyml
1125 let tail (q: queue 'a) : queue 'a
1126 requires { sequence q <> Nil }
1127 ensures { tl (sequence q) = Some (sequence result) }
1128 = let Cons _ r = q.front in
1129 create r (q.lenf - 1) q.rear q.lenr
1131 The last operation is ``enqueue``, which pushes a new element in a given
1132 queue. Reusing the smart constructor ``create`` makes it a one line
1135 .. code-block:: whyml
1137 let enqueue (x: 'a) (q: queue 'a) : queue 'a
1138 ensures { sequence result = sequence q ++ Cons x Nil }
1139 = create q.front q.lenf (Cons x q.rear) (q.lenr + 1)
1141 The code is given below. The verification conditions are
1142 all discharged automatically.
1144 .. code-block:: whyml
1146 module AmortizedQueue
1152 type queue 'a = { front: list 'a; lenf: int;
1153 rear : list 'a; lenr: int; }
1154 invariant { length front = lenf >= length rear = lenr }
1155 by { front = Nil; lenf = 0; rear = Nil; lenr = 0 }
1157 function sequence (q: queue 'a) : list 'a =
1158 q.front ++ reverse q.rear
1160 let empty () : queue 'a
1161 ensures { sequence result = Nil }
1162 = { front = Nil; lenf = 0; rear = Nil; lenr = 0 }
1164 let head (q: queue 'a) : 'a
1165 requires { sequence q <> Nil }
1166 ensures { hd (sequence q) = Some result }
1167 = let Cons x _ = q.front in x
1169 let create (f: list 'a) (lf: int) (r: list 'a) (lr: int) : queue 'a
1170 requires { lf = length f /\ lr = length r }
1171 ensures { sequence result = f ++ reverse r }
1173 { front = f; lenf = lf; rear = r; lenr = lr }
1175 let f = f ++ reverse r in
1176 { front = f; lenf = lf + lr; rear = Nil; lenr = 0 }
1178 let tail (q: queue 'a) : queue 'a
1179 requires { sequence q <> Nil }
1180 ensures { tl (sequence q) = Some (sequence result) }
1181 = let Cons _ r = q.front in
1182 create r (q.lenf - 1) q.rear q.lenr
1184 let enqueue (x: 'a) (q: queue 'a) : queue 'a
1185 ensures { sequence result = sequence q ++ Cons x Nil }
1186 = create q.front q.lenf (Cons x q.rear) (q.lenr + 1)