3 A classical example. Searches a sorted array for a given value v. *)
8 use int.ComputerDivision
12 (* the code and its specification *)
14 exception Not_found (* raised to signal a search failure *)
16 let binary_search (a: array int) (v: int) : int
17 requires { forall i1 i2. 0 <= i1 <= i2 < length a -> a[i1] <= a[i2] }
18 ensures { 0 <= result < length a /\ a[result] = v }
19 raises { Not_found -> forall i. 0 <= i < length a -> a[i] <> v }
22 let ref u = length a - 1 in
24 invariant { 0 <= l /\ u < length a }
26 forall i. 0 <= i < length a -> a[i] = v -> l <= i <= u }
28 let m = l + div (u - l) 2 in
29 assert { l <= m <= u };
41 (* A generalization: the midpoint is computed by some abstract function.
42 The only requirement is that it lies between l and u. *)
44 module BinarySearchAnyMidPoint
50 exception Not_found (* raised to signal a search failure *)
52 val midpoint (l: int) (u: int) : int
53 requires { l <= u } ensures { l <= result <= u }
55 let binary_search (a: array int) (v: int) : int
56 requires { forall i1 i2. 0 <= i1 <= i2 < length a -> a[i1] <= a[i2] }
57 ensures { 0 <= result < length a /\ a[result] = v }
58 raises { Not_found -> forall i. 0 <= i < length a -> a[i] <> v }
61 let ref u = length a - 1 in
63 invariant { 0 <= l /\ u < length a }
64 invariant { forall i. 0 <= i < length a -> a[i] = v -> l <= i <= u }
66 let m = midpoint l u in
78 (* The following version of binary search is faster in practice, by being
79 friendlier with the branch predictor of most processors. It also happens
80 to be stable, since it always return the highest index. *)
82 module BinarySearchBranchless
85 use int.ComputerDivision
89 exception Not_found (* raised to signal a search failure *)
91 let binary_search (a: array int) (v: int) : int
92 requires { forall i1 i2. 0 <= i1 <= i2 < length a -> a[i1] <= a[i2] }
93 ensures { 0 <= result < length a /\ a[result] = v }
94 ensures { forall i. result < i < length a -> a[i] <> v }
95 raises { Not_found -> forall i. 0 <= i < length a -> a[i] <> v }
98 let ref s = length a in
99 if s = 0 then raise Not_found;
101 invariant { 0 <= l /\ l + s <= length a /\ s >= 1 }
103 forall i. 0 <= i < length a -> a[i] = v -> a[l] <= v /\ i < l + s }
107 l := if a[m] > v then l else m;
115 (* binary search using 32-bit integers *)
117 module BinarySearchInt32
122 use mach.array.Array32
124 exception Not_found (* raised to signal a search failure *)
126 let binary_search (a: array int32) (v: int32) : int32
127 requires { forall i1 i2. 0 <= i1 <= i2 < a.length -> a[i1] <= a[i2] }
128 ensures { 0 <= result < a.length /\ a[result] = v }
129 raises { Not_found -> forall i. 0 <= i < a.length -> a[i] <> v }
132 let ref u = length a - 1 in
134 invariant { 0 <= l /\ u < a.length }
135 invariant { forall i. 0 <= i < a.length -> a[i] = v -> l <= i <= u }
137 let m = l + (u - l) / 2 in
138 assert { l <= m <= u };
141 else if a[m] > v then
150 (* A particular case with Boolean values (0 or 1) and a sentinel 1 at the end.
151 We look for the first position containing a 1. *)
153 module BinarySearchBoolean
156 use int.ComputerDivision
160 let binary_search (a: array int) : int
161 requires { 0 < a.length }
162 requires { forall i j. 0 <= i <= j < a.length -> 0 <= a[i] <= a[j] <= 1 }
163 requires { a[a.length - 1] = 1 }
164 ensures { 0 <= result < a.length }
165 ensures { a[result] = 1 }
166 ensures { forall i. 0 <= i < result -> a[i] = 0 }
169 let ref hi = length a - 1 in
171 invariant { 0 <= lo <= hi < a.length }
172 invariant { a[hi] = 1 }
173 invariant { forall i. 0 <= i < lo -> a[i] = 0 }
175 let mid = lo + div (hi - lo) 2 in
188 use int.ComputerDivision
192 let rec function log2 (n: int) : int
194 = if n <= 1 then 0 else 1 + log2 (div n 2)
196 let rec lemma log2_monotone (x y: int)
198 ensures { log2 x <= log2 y }
200 = if y > 1 then log2_monotone (div x 2) (div y 2)
202 let function f (n: int) : int
203 = if n = 0 then 0 else 1 + log2 n
206 forall n. n >= 2 -> f n <= 2 * log2 n
210 let binary_search (a: array int) (v: int) : int
211 requires { forall i1 i2. 0 <= i1 <= i2 < length a -> a[i1] <= a[i2] }
212 requires { time = 0 }
213 ensures { 0 <= result < length a && a[result] = v
214 || result = -1 && forall i. 0 <= i < length a -> a[i] <> v }
215 ensures { time - old time <= f (length a) }
218 let ref hi = length a in
220 invariant { 0 <= lo <= hi <= length a }
221 invariant { forall i. 0 <= i < lo || hi <= i < length a -> a[i] <> v }
222 invariant { (time - old time) + f (hi - lo) <= f (length a) }
224 let mid = lo + div (hi - lo) 2 in
227 else if a[mid] > v then
237 (* Search in a two-dimensional grid where all rows and columns are
238 sorted. Here is an example:
241 +---+---+---+---+---+-->
242 | 1 | 3 | 4 | 4 | 7*|
243 +---+---+---+---+---+
244 | 1 | 4 | 6 | 6 | 8*|
245 +---+---+---+---+---+
246 | 2 | 5 | 7 | 9*|11*|
247 +---+---+---+---+---+
248 | 4 | 8 | 8*|12*|13 |
249 +---+---+---+---+---+
253 Algorithm: start from the upper right corner and then move left
254 (resp. down) when the value is greater (resp. smaller) than the
257 In the example above, the stars depict the elements that are
258 examined when we search for the value 10. Here we end up outside of
259 the grid and thus the search is unsuccessful.
262 module TwoDimensional
267 let search (m: matrix int) (v: int) : bool
268 requires { [@expl: rows are sorted] forall i. 0 <= i < rows m ->
269 forall j1 j2. 0 <= j1 <= j2 < columns m ->
270 get m i j1 <= get m i j2 }
271 requires { [@expl: columns are sorted] forall j. 0 <= j < columns m ->
272 forall i1 i2. 0 <= i1 <= i2 < rows m ->
273 get m i1 j <= get m i2 j }
275 exists i j. 0 <= i < rows m && 0 <= j < columns m && get m i j = v }
277 let ref j = columns m - 1 in
278 while i < rows m && 0 <= j do
279 invariant { 0 <= i <= rows m }
280 invariant { -1 <= j < columns m }
281 invariant { forall i' j'. 0 <= i' < rows m -> 0 <= j' < columns m ->
282 i' < i || j' > j -> get m i' j' <> v }
283 variant { rows m - i + j }
285 if x = v then return true;
286 if x < v then i <- i + 1 else j <- j - 1