mirror of
https://github.com/AdaCore/why3.git
synced 2026-02-12 12:34:55 -08:00
152 lines
4.3 KiB
Plaintext
152 lines
4.3 KiB
Plaintext
|
|
(* Counting Sort.
|
|
|
|
We sort an array of integers, assuming all elements are in the range 0..k-1
|
|
|
|
We simply count the elements equal to x, for each
|
|
x in 0..k-1, and then (re)fill the array with two nested loops.
|
|
|
|
TODO: Implement and prove a *stable* variant of counting sort,
|
|
as proposed in
|
|
|
|
Introduction to Algorithms
|
|
Cormen, Leiserson, Rivest
|
|
The MIT Press (2nd edition)
|
|
Section 9.2, page 175
|
|
*)
|
|
|
|
module Spec
|
|
|
|
use export int.Int
|
|
use int.NumOf as N
|
|
use export array.Array
|
|
use export array.IntArraySorted
|
|
|
|
(* values of the array are in the range 0..k-1 *)
|
|
val constant k: int
|
|
ensures { 0 < result }
|
|
|
|
predicate k_values (a: array int) =
|
|
forall i: int. 0 <= i < length a -> 0 <= a[i] < k
|
|
|
|
(* we introduce two predicates:
|
|
- [numeq a v l u] is the number of values in a[l..u[ equal to v
|
|
- [numlt a v l u] is the number of values in a[l..u[ less than v *)
|
|
function numeq (a: array int) (v i j : int) : int =
|
|
N.numof (fun k -> a[k] = v) i j
|
|
|
|
function numlt (a: array int) (v i j : int) : int =
|
|
N.numof (fun k -> a[k] < v) i j
|
|
|
|
(* an ovious lemma relates numeq and numlt *)
|
|
let rec lemma eqlt (a: array int) (v: int) (l u: int)
|
|
requires { k_values a }
|
|
requires { 0 <= v < k }
|
|
requires { 0 <= l < u <= length a }
|
|
ensures { numlt a v l u + numeq a v l u = numlt a (v+1) l u }
|
|
variant { u - l }
|
|
= if l < u-1 then eqlt a v (l+1) u
|
|
|
|
(* permutation of two arrays is here conveniently defined using [numeq]
|
|
i.e. as the equality of the two multi-sets *)
|
|
predicate permut (a b: array int) =
|
|
length a = length b /\
|
|
forall v: int. 0 <= v < k -> numeq a v 0 (length a) = numeq b v 0 (length b)
|
|
|
|
end
|
|
|
|
module CountingSort
|
|
|
|
use Spec
|
|
use ref.Refint
|
|
|
|
(* sorts array a into array b *)
|
|
let counting_sort (a: array int) (b: array int)
|
|
requires { k_values a /\ length a = length b }
|
|
ensures { sorted b /\ permut a b }
|
|
= let c = Array.make k 0 in
|
|
for i = 0 to length a - 1 do
|
|
invariant { forall v: int. 0 <= v < k -> c[v] = numeq a v 0 i }
|
|
let v = a[i] in
|
|
c[v] <- c[v] + 1
|
|
done;
|
|
let j = ref 0 in
|
|
for v = 0 to k-1 do
|
|
invariant { !j = numlt a v 0 (length a) }
|
|
invariant { sorted_sub b 0 !j }
|
|
invariant { forall e: int. 0 <= e < !j -> 0 <= b[e] < v }
|
|
invariant { forall f: int.
|
|
0 <= f < v -> numeq b f 0 !j = numeq a f 0 (length a) }
|
|
for i = 1 to c[v] do
|
|
invariant { !j -i+1 = numlt a v 0 (length a) }
|
|
invariant { sorted_sub b 0 !j }
|
|
invariant { forall e: int. 0 <= e < !j -> 0 <= b[e] <= v }
|
|
invariant { forall f: int.
|
|
0 <= f < v -> numeq b f 0 !j = numeq a f 0 (length a) }
|
|
invariant { numeq b v 0 !j = i-1 }
|
|
b[!j] <- v;
|
|
incr j
|
|
done
|
|
done;
|
|
assert { !j = length b }
|
|
|
|
end
|
|
|
|
module InPlaceCountingSort
|
|
|
|
use Spec
|
|
use ref.Refint
|
|
|
|
(* sorts array a in place *)
|
|
let in_place_counting_sort (a: array int)
|
|
requires { k_values a }
|
|
ensures { sorted a /\ permut (old a) a }
|
|
= let c = make k 0 in
|
|
for i = 0 to length a - 1 do
|
|
invariant { forall v: int. 0 <= v < k -> c[v] = numeq a v 0 i }
|
|
let v = a[i] in
|
|
c[v] <- c[v] + 1
|
|
done;
|
|
let j = ref 0 in
|
|
for v = 0 to k-1 do
|
|
invariant { !j = numlt (old a) v 0 (length a) }
|
|
invariant { sorted_sub a 0 !j }
|
|
invariant { forall e: int. 0 <= e < !j -> 0 <= a[e] < v }
|
|
invariant { forall f: int.
|
|
0 <= f < v -> numeq a f 0 !j = numeq (old a) f 0 (length a) }
|
|
for i = 1 to c[v] do
|
|
invariant { !j -i+1 = numlt (old a) v 0 (length a) }
|
|
invariant { sorted_sub a 0 !j }
|
|
invariant { forall e: int. 0 <= e < !j -> 0 <= a[e] <= v }
|
|
invariant { forall f: int.
|
|
0 <= f < v -> numeq a f 0 !j = numeq (old a) f 0 (length a) }
|
|
invariant { numeq a v 0 !j = i-1 }
|
|
a[!j] <- v;
|
|
incr j;
|
|
assert {forall f. 0 <= f < v -> numeq a f 0 !j = numeq a f 0 (!j - 1)}
|
|
done
|
|
done;
|
|
assert { !j = length a }
|
|
|
|
end
|
|
|
|
module Harness
|
|
|
|
use Spec
|
|
use InPlaceCountingSort
|
|
|
|
let harness ()
|
|
requires { k = 2 }
|
|
= (* a is [0;1;0] *)
|
|
let a = make 3 0 in
|
|
a[1] <- 1;
|
|
in_place_counting_sort a;
|
|
(* a is now [0;0;1] *)
|
|
assert { numeq a 0 0 3 = 2 };
|
|
assert { numeq a 1 0 3 = 1 };
|
|
assert { a[0] = 0 };
|
|
assert { a[1] = 0 };
|
|
assert { a[2] = 1 }
|
|
|
|
end
|