3 {1 The VerifyThis competition at FM2012: Problem 2}
5 see {h <a href="http://fm2012.verifythis.org/challenges">this web page</a>}
12 use int.ComputerDivision
19 (** {2 Preliminary lemmas on division by 2 and power of 2} *)
21 (** Needed for the proof of phase1_frame and phase1_frame2 *)
23 forall x:int. x >= 0 -> x >= 2 * div x 2 >= x-1
25 (** The step must be a power of 2 *)
26 predicate is_power_of_2 (x:int) = exists k:int. (k >= 0 /\ x = power 2 k)
29 lemma is_power_of_2_1:
30 forall x:int. is_power_of_2 x -> x > 1 -> 2 * div x 2 = x
34 {2 Modeling the "upsweep" phase }
39 function go_left (left right:int) : int =
40 let space = right - left in left - div space 2
42 function go_right (left right:int) : int =
43 let space = right - left in right - div space 2
46 Description in a purely logic way the effect of the first phase
47 "upsweep" of the algorithm. The second phase "downsweep" then
48 traverses the array in the same way than the first phase. Hence,
49 the inductive nature of this definition is not an issue. *)
51 inductive phase1 (left:int) (right:int) (a0: array int) (a: array int) =
52 | Leaf : forall left right:int, a0 a : array int.
55 phase1 left right a0 a
56 | Node : forall left right:int, a0 a : array int.
58 phase1 (go_left left right) left a0 a ->
59 phase1 (go_right left right) right a0 a ->
60 a[left] = sum a0 (left-(right-left)+1) (left+1) ->
61 phase1 left right a0 a
63 (** Frame properties of the "phase1" inductive *)
65 (** frame lemma for "phase1" on fourth argument.
66 needed to prove both upsweep, downsweep and compute_sums
68 let rec lemma phase1_frame (left right:int) (a0 a a' : array int) : unit
69 requires { forall i:int. left-(right-left) < i < right ->
71 requires { phase1 left right a0 a }
72 variant { right-left }
73 ensures { phase1 left right a0 a' } =
74 if right > left + 1 then begin
75 phase1_frame (go_left left right) left a0 a a';
76 phase1_frame (go_right left right) right a0 a a'
79 (** frame lemma for "phase1" on third argument.
80 needed to prove upsweep and compute_sums
82 let rec lemma phase1_frame2 (left right:int) (a0 a0' a : array int) : unit
83 requires { forall i:int. left-(right-left) < i < right ->
85 requires { phase1 left right a0 a }
86 variant { right-left }
87 ensures { phase1 left right a0' a } =
88 if right > left + 1 then begin
89 phase1_frame2 (go_left left right) left a0 a0' a;
90 phase1_frame2 (go_right left right) right a0 a0' a
94 (** {2 The upsweep phase}
96 First function: modify partially the table and compute some
97 intermediate partial sums
101 let rec upsweep (left right: int) (a: array int)
102 requires { 0 <= left < right < a.length }
103 requires { -1 <= left - (right - left) }
104 requires { is_power_of_2 (right - left) }
105 variant { right - left }
106 ensures { phase1 left right (old a) a }
107 ensures { let space = right - left in
108 a[right] = sum (old a) (left-space+1) (right+1) /\
109 (forall i: int. i <= left-space -> a[i] = (old a)[i]) /\
110 (forall i: int. i > right -> a[i] = (old a)[i]) }
111 = let space = right - left in
112 if right > left+1 then begin
113 upsweep (left - div space 2) left a;
114 upsweep (right - div space 2) right a;
115 assert { phase1 (left - div space 2) left (old a) a };
116 assert { phase1 (right - div space 2) right (old a) a };
117 assert { a[left] = sum (old a) (left-(right-left)+1) (left+1) };
118 assert { a[right] = sum (old a) (left+1) (right+1) }
120 a[right] <- a[left] + a[right];
122 right > left+1 -> phase1 (left - div space 2) left (old a) a };
124 right > left+1 -> phase1 (right - div space 2) right (old a) a }
126 (** {2 The downsweep phase} *)
128 (** The property we ultimately want to prove *)
129 predicate partial_sum (left:int) (right:int) (a0 a : array int) =
130 forall i : int. (left-(right-left)) < i <= right -> a[i] = sum a0 0 i
132 (** Second function: complete the partial using the remaining intial
133 value and the partial sum already computed *)
134 let rec downsweep (left right: int) (ghost a0 : array int) (a : array int)
135 requires { 0 <= left < right < a.length }
136 requires { -1 <= left - (right - left) }
137 requires { is_power_of_2 (right - left) }
138 requires { a[right] = sum a0 0 (left-(right-left) + 1) }
139 requires { phase1 left right a0 a }
140 variant { right - left }
141 ensures { partial_sum left right a0 a }
142 ensures { forall i: int. i <= left-(right-left) -> a[i] = (old a)[i] }
143 ensures { forall i: int. i > right -> a[i] = (old a)[i] }
144 = let tmp = a[right] in
145 assert { a[right] = sum a0 0 (left-(right-left) + 1) };
146 assert { a[left] = sum a0 (left-(right-left)+1) (left+1) };
147 a[right] <- a[right] + a[left];
149 assert { a[right] = sum a0 0 (left + 1) };
150 if right > left+1 then
151 let space = right - left in
152 assert { phase1 (go_left left right) left a0 (old a) };
153 assert { phase1 (go_right left right) right a0 (old a) };
154 assert { phase1 (go_right left right) right a0 a };
155 downsweep (left - div space 2) left a0 a;
156 assert { phase1 (go_right left right) right a0 a };
157 downsweep (right - div space 2) right a0 a;
158 assert { partial_sum (left - div space 2) left a0 a };
159 assert { partial_sum (right - div space 2) right a0 a }
161 (** {2 The main procedure} *)
164 requires { a.length >= 2 }
165 requires { is_power_of_2 a.length }
166 ensures { forall i : int. 0 <= i < a.length -> a[i] = sum (old a) 0 i }
167 = let a0 = ghost (copy a) in
169 let left = div l 2 - 1 in
171 upsweep left right a;
172 (* needed for the precondition of downsweep *)
173 assert { phase1 left right a0 a };
175 downsweep left right a0 a;
176 (* needed to prove the post-condition *)
177 assert { forall i : int.
178 left-(right-left) < i <= right -> a[i] = sum a0 0 i }
181 (** {2 A simple test} *)
184 let test_harness () =
186 (* needed for the precondition of compute_sums *)
187 assert { power 2 3 = a.length };
188 a[0] <- 3; a[1] <- 1; a[2] <- 7; a[3] <- 0;
189 a[4] <- 4; a[5] <- 1; a[6] <- 6; a[7] <- 3;
194 assert { a[3] = 11 };
195 assert { a[4] = 11 };
196 assert { a[5] = 15 };
197 assert { a[6] = 16 };
201 exception BenchFailure
203 let bench () raises { BenchFailure -> true } =
205 (* needed for the precondition of compute_sums *)
206 assert { power 2 3 = a.length };
207 a[0] <- 3; a[1] <- 1; a[2] <- 7; a[3] <- 0;
208 a[4] <- 4; a[5] <- 1; a[6] <- 6; a[7] <- 3;
210 if a[0] <> 0 then raise BenchFailure;
211 if a[1] <> 3 then raise BenchFailure;
212 if a[2] <> 4 then raise BenchFailure;
213 if a[3] <> 11 then raise BenchFailure;
214 if a[4] <> 11 then raise BenchFailure;
215 if a[5] <> 15 then raise BenchFailure;
216 if a[6] <> 16 then raise BenchFailure;
217 if a[7] <> 22 then raise BenchFailure;