PVS assignments After the choice of one of these assignments, first design an algorithm with a complete hand-written proof. Only then, encode the algorithm in PVS. Prove the correctness of your program with the theory programs.pvs unless otherwise specified. Write a report about the algorithm and your proof of it, and turn it in on paper. Also, send a dump-file of the pvs proof to w.h.hesselink@rug.nl. The assignments A, ..., E are presented in order of increasing complexity. Their solutions should have linear complexity. The other assignments have been added later in arbitrary order. The assignments O, P, R, S, T, and U are relatively easy (and you can always ask for assistance). Most of the following assignments suppose that you use and import the theory programs, and that you apply the ideas of the course in program correctness as in binsearch.pvs. Exceptions are the assignments M, N, V. -.-.- Assignment A. Given are three ascending (weakly increasing) functions f, g, h: natural -> real, such that there exists a triple of natural numbers x, y, z with f(x) = g(y) = h(z). Write a program to determine such a triple (the first one). Time complexity: linear in x+y+z. Assignment B. Merge. Given are two ascending (i.e. weakly increasing) arrays f and g of real numbers, say with lengths k and n. The state space holds a variable h for an array with length k+n. The program should fill array h with the elements of f and g in such a way that h is also ascending. Also show that the contents of h as a set is the union of the (initial) contents of f and g. Time complexity: linear in k+n. Assignment C. Plateau. Given is one nonempty array f. A plateau of f is a sequence of consecutive elements of f with the same value. The program should determine the length of the longest plateau of f, as well as an index where such a longest plateau starts. Time complexity linear in the size of f. Assignment D. Dutch National Flag. Given is an array f of length N of real numbers. Develop and prove a program that permutes the elements of array f and yields two integers a and b such that, for all i with 0 <= i < N, we have f(i) < 0 if and only if i < a, and f(i) < 1 if and only if i < b. Also compute the permutation p with f = F o p where F is the initial value of f. The time complexity must be linear in N. It is allowed to swap array elements in one statement. Assignment E. Incidence counting. Given are two increasing arrays f and g of real numbers, say with lengths k and n. The program should determine the number of pairs (i,j) with f(i) = g(j). Time complexity: linear in k+n. Assignment F. Selection sort is a well-known in-situ sorting algorithm of quadratic complexity. Prove its correctness, i.e., prove that the program does not change the contents of the array (as a multiset), but sorts it by changing the order of the elements. Note that the program has a double loop. First invent the invariant of the outer loop, then the invariant of the inner loop, but note that the inner loop must preserve the invariant of the outer loop also. Assignment G. Let f be a function [nat, nat -> real] that is ascending in its first argument and descending in its second argument, and that is 0 some some unknown pair of arguments. Develop a program that determines a pair (i, j) such that f(i, j) = 0 and that we have i <= x and j <= y for all x, y with f(x, y) = 0. In this case, the invariant and the variant function may depend on the unknown existing pair. Assignment H. Figure Six. Let T be a finite type and let x0: T and f: [T->T] be given. We use f^n to denote n-times repeated application of f. Put N = card[fullset(T)], based on the definition of Card in the prelude, and prove that there exist i < j <= N such that f^i(x0) = f^j(x0). Give a while program to determine a pair i and j with i < j and f^i(x0) = f^j(x0), and prove its correctness. The program must use bounded memory (no arbitrarily large sets). It must not use the unknown value of N, and also no auxiliary function for f^n. Assignment I. Slope search. Given are natural numbers m and n, a real number w, and a function f: [nat, nat -> real], which is descending in its first argument and increasing in its second argument. Design a program to determine the set {(x,y): [nat, nat] | x < m & y < n & f(x,y) = w } . The time complexity must be in the order of m+n. You can use a program variable of type setof[[nat, nat]] and the prelude function add. Assignment J. Floyd_Warshall. Let n be a positive natural number and let R be a binary relation on below[n]. Floyd-Warshall's algorithm determines the reflexive transitive closure star(R), in order n^3, with a triply nested loop. Prove its correctness. Note that an inner loop should preserve the invariant of any surounding loop. Assignment K. Graph search. Let R be a binary relation over a finite nonempty type T, interpreted as a directed graph. Let x0: T be a given vertex. Graph search is the common generalization of depth-first and breadth-first dearch. The aim is to form a subrelation F of R, which is a tree rooted at x0 that contains all nodes reachable from x0. The algorithm uses a node x, and two sets of nodes: S, the wave front, and U, the set of the unreached nodes. S := {x0} ; U := T - {x0} ; F := empty ; while S is not empty do extract some x from S ; W := {y in U | (x,y) in R} ; U := U - W ; S := S union W ; F := F union {(x,y) | y in W} ; end . Formalize the postcondition sketched above. Prove that the algorithm terminates and establishes this formalized postcondition. From the prelude, you will need is_finite to prove termination, and epsilon to choose some element. Comment. If S is treated as a stack, you get depth-first search; if it is treated as a queue, you get breadth-first search. If you would give the edges nonnegative weights and would attach variable weights to the reached nodes, you could get Dijkstra's shortest path algorithm (F can then be omitted). Assignment L. Tarjan's union algorithm. Let R: pred[[T,T]] be finite. The aim of the union algorithm is to determine the smallest equivalence relation E that contains R. It does so by means of a program variable (an array of pointers) parent: [T -> lift[T]], which must satisfy something like: forall x: exists n: parent^n(x) = bottom. Let this property be called toBottom. Construct a function ancestor: [T, (toBottom) -> T] that satisfies ancestor(x, p) = IF up?(p(x)) THEN ancestor(down(p(x)), p) ELSE x ENDIF . Now the algorithm should have the postcondition E = {(x,y) | ancestor(x, parent) = ancestor(y, parent) } . It has the form: {PRE: forall x: parent(x) = bottom } while R is not empty do extract some pair (x,y) from R ; x := ancestor (x, parent) ; y := ancestor (y, parent) ; parent(x) := y OR parent(y) := x ; // not both! end . Prove its correctness. Assignment M. Merge-sort. Define merge-sort as a functional program in Haskell and prove its correctness. Do not use the theory "programs". Use finite sequences from the prelude. The correctness should include that, for every value w, the number of occurrences of w in the initial list equals the number of occurrences in the sorted list. And, of course, the sorted list must be sorted. Assignment N. Search trees. Use a data type to define search trees. Define and prove the correctness of the recursive function "search", and of the function "insert". Now define AVL-trees and the insert function for AVL-trees, and prove its correctness. Do not use the theory "programs". Assignment O. The king's problem. There are N persons at a meeting, numbered 0 through N-1. One of them is the king, with an unknown number X. Everybody (possibly except for the king) knows the king, but the king knows nobody (except possibly himself). Predicate knows(i,j) expresses that person i knows person j. So, it is given that 0 <= x < N and that, for all i /= X, we have knows(i,X) and not knows(X,i). Design a program with linear time complexity that determines X. Use the theory "programs" to prove the correctness. Assignment P. Given is a natural number N and for every natural number i < N a set children(i), which consists of natural numbers < i. A descendant of i is defined to be a child of i or a descendant of a child of i. Give a recursive definition for a function descendants(i) that formalizes this and that is accepted by PVS. Design a sequential algorithm that determines descendants(i) for all i < N. The time complexity of the algorithm should be proportional to N if set-union can be done in constant time (here set-union stands both for the binary operation and for the union of a set of sets of numbers). Use the theory "programs" to prove the correctness. Assignment R. In the decimal system, a natural number n is represented by a finite sequence s of digits (numbers < B) such that n = (sum i: s(i)*B^i) where the base B = 10. At school we have learned to add two numbers given in this representation by repeatedly adding digits in the same position modulo B and transferring the carry to the next position. Formulate this recipe as a while loop and prove that the value represented by the resulting sequence equals the sum of the two values of the argument sequences. Note that the "digits" s(i) must be in the range 0 \leq s(i) < B, both for the summands and for the resulting sum. Do this for an arbitrary base B > 1. You may use the theory sum in the user guide and the finite_sequences in the pvs-prelude. You will have to develop some elementary arithmetic. Use the theory "programs" to prove the correctness. Assignment S. The value of a polynomial expression S = (sum i: i < n : a(i)*m^i) for a real argument m can be calculated by s := 0 ; k := n-1 ; while k >= 0 do s := s * m + a(k) ; k := k-1 ; end . Prove the correctness using the theory "programs". The hint and remark in the previous assignment are applicable here as well. Assignment T. We consider finite sets of integers. Such a set is represented by an increasing array of integers with some length. Given are such sets X and Y, an integer m, and a boolean function p: int * int -> bool. Construct a program to determine the subset Z of X of the elements x in X such that m+x is in Y and that p(m, x) holds. Z must again be represented as an increasing array. The time complexity of the program must be linear in #X+#Y (where # means "number of elements of"). [NB. Binary search in Y per element of X has complexity (#X)*log(#Y) and is therefore not efficient enough] Assignment U. We use the same setting as assignment T. Construct a program to determine the subset Z of X of the elements x in X such that there is an element y in Y for which x+y = m and p(x, y) holds. Z must again be represented as an increasing array. The time complexity of the program must again be linear in #X+#Y (where # means "number of elements of"). Assignment V. The mutual exclusion algorithm of Burns-Lamport for N concurrent threads. It uses a single shared variable: bool cc[N] ; // initially all false thread(p: 0 <= p < N) { int j ; // private while (true) { 10: NCS ; 11: cc[p] := true ; 12: for (j := 0 ; j < p ; j++) { 13: if (cc[j]) { 14: cc[p] := false ; 15: await ! cc[j] ; goto 11 ; } } 16: for (j := p+1 ; j < N ; j++) { await ! cc[j] ; } 17: CS ; 18: cc[p] := false ; } } To prove: mutual exclusion: q at CS and r at CS implies q = r. Optionally: prove progress in the form box((exists q in [11..17]) implies diamond(exists r at CS)). You may use weak fairness to prove this. Version 18 November 2010, Wim H. Hesselink