3 {1 VerifyThis @ ETAPS 2017 competition
4 Challenge 4: Tree Buffer}
6 See https://formal.iti.kit.edu/ulbrich/verifythis2017/
8 Author: Jean-Christophe FilliĆ¢tre (CNRS)
11 (* default implementation *)
17 type buf 'a = { h: int; xs: list 'a }
19 let rec function take (n: int) (l: list 'a) : list 'a =
22 | Cons x xs -> if n = 0 then Nil
23 else Cons x (take (n-1) xs) end
25 let function empty (h: int) : buf 'a =
28 let function add (x: 'a) (b: buf 'a) : buf 'a =
29 { b with xs = Cons x b.xs }
31 let function get (b: buf 'a) : list 'a =
34 (* the following lemma is useful to verify both Caterpillar and
35 RealTime implementations below *)
40 let rec lemma take_lemma (l1 l2 l3: list 'a) (n: int)
41 requires { 0 <= n <= length l1 }
42 ensures { take n (l1 ++ l2) = take n (l1 ++ l3) }
44 = match l1 with Nil -> ()
45 | Cons _ ll1 -> if n > 0 then take_lemma ll1 l2 l3 (n-1) end
61 ghost b: buf 'a; (* the model is the default implementation *)
64 xs_len = length xs < ch /\
65 forall len. 0 <= len <= ch -> take len (xs ++ ys) = take len b.xs
67 ch = 1; xs = Nil; xs_len = 0; ys = Nil; b = empty 1
70 (* for the three operations, the postcondition uses the default
73 let cat_empty (h: int) : cat 'a
75 ensures { result.b = empty h }
76 = { ch = h; xs = Nil; xs_len = 0; ys = Nil;
79 let cat_add (x: 'a) (c: cat 'a) : cat 'a
80 ensures { result.b = add x c.b }
81 = if c.xs_len = c.ch - 1 then
82 { c with xs = Nil; xs_len = 0; ys = Cons x c.xs;
85 { c with xs = Cons x c.xs; xs_len = 1 + c.xs_len;
88 let cat_get (c: cat 'a) : list 'a
89 ensures { result = get c.b }
90 = take c.ch (c.xs ++ c.ys)
96 (* important note: Why3 assumes a garbage collector and so it makes
97 little sense to implement the real time solution in Why3.
99 Yet I stayed close to the C++ code, with a queue to_delete where
100 lists are added when discarded and then destroyed progressively
101 (at most two conses at a time) in process_queue.
103 The C++ code seems to be missing the insertion into to_delete,
104 which I added to rt_add; see my comment below.
113 (* For technical reasons, the global queue cannot contain
114 polymorphic values, to we assume values to be of some
115 abstract type "elt". Anyway, the C++ code assumes integer
119 (* not different from the Caterpillar implementation
120 replacing 'a with elt everywhere *)
126 ghost b: buf elt; (* the model is the default implementation *)
129 xs_len = length xs < ch /\
130 forall len. 0 <= len <= ch -> take len (xs ++ ys) = take len b.xs
132 ch = 1; xs = Nil; xs_len = 0; ys = Nil; b = empty 1
135 (* garbage collection *)
137 (* note: when translating Why3 to OCaml, this module is mapped
138 to OCaml's Queue module, where push and pop are O(1) *)
140 val to_delete: Q.t (list elt)
142 let de_allocate (l: list elt)
143 = match l with Nil -> () | Cons _ xs -> Q.push xs to_delete end
147 if not (Q.is_empty to_delete) then de_allocate (Q.pop to_delete);
148 if not (Q.is_empty to_delete) then de_allocate (Q.pop to_delete)
149 with Q.Empty -> absurd end
151 (* no difference wrt Caterpillar *)
152 let rt_empty (h: int) : rt
154 ensures { result.b = empty h }
155 = { ch = h; xs = Nil; xs_len = 0; ys = Nil;
158 (* no difference wrt Caterpillar *)
159 let rt_get (c: rt) : list elt
160 ensures { result = get c.b }
161 = take c.ch (c.xs ++ c.ys)
163 (* this is where we introduce explicit garbage collection
164 1. process_queue is called first (as in the C++ code)
165 2. when ys is discarded, it is added to the queue (which
166 seems to be missing in the C++ code) *)
167 let rt_add (x: elt) (c: rt) : rt
168 ensures { result.b = add x c.b }
170 if c.xs_len = c.ch - 1 then begin
171 Q.push c.ys to_delete;
172 { c with xs = Nil; xs_len = 0; ys = Cons x c.xs;
175 { c with xs = Cons x c.xs; xs_len = 1 + c.xs_len;