remove_duplicate.mlw 1.99 KB
Newer Older
1

MARCHE Claude's avatar
MARCHE Claude committed
2 3 4 5 6 7 8 9 10 11 12

(** {1 Removing duplicate elements in an array}

    Given an array [a] of size [n], removes its duplicate elements,
    in-place, as follows: return [r] such that [a[0..r-1]] contains the same
    elements as [a[0..n-1]] and no duplicate

*)


(** {2 Specification} *)
13 14 15 16 17 18

module Spec

  use export int.Int
  use export array.Array

MARCHE Claude's avatar
MARCHE Claude committed
19
  (** v appears in [a[0..s-1]] *)
20 21 22
  predicate appears (v: 'a) (a: array 'a) (s: int) =
    exists i: int. 0 <= i < s /\ a[i] = v

MARCHE Claude's avatar
MARCHE Claude committed
23
  (** [a[0..s-1]] contains no duplicate element *)
24 25 26 27 28
  predicate nodup (a: array 'a) (s: int) =
    forall i: int. 0 <= i < s -> not (appears a[i] a i)

end

MARCHE Claude's avatar
MARCHE Claude committed
29
(** {2 Quadratic implementation, without extra space} *)
30 31 32 33 34 35

module RemoveDuplicateQuadratic

  use import Spec
  use import ref.Refint

36
  let rec test_appears (ghost w: ref int) (v: 'a) (a: array 'a) (s: int) : bool
37
    requires { 0 <= s <= length a }
38
    ensures  { result=true <-> appears v a s }
39
    ensures  { result=true -> 0 <= !w < s && a[!w] = v }
40
    variant  { s }
41 42
  = s > 0 &&
    if a[s-1] = v then begin w := s - 1; true end else test_appears w v a (s-1)
43 44 45 46 47 48 49

  let remove_duplicate (a: array 'a) : int
    ensures { 0 <= result <= length a }
    ensures { nodup a result }
    ensures { forall v:'a. appears v (old a) (length a) <-> appears v a result }
  = let n = length a in
    let r = ref 0 in
50
    let ghost from = make n 0 in
51
    let ghost to_  = make n 0 in
52 53 54 55
    'L:
    for i = 0 to n - 1 do
      invariant { 0 <= !r <= i }
      invariant { nodup a !r }
Jean-Christophe Filliatre's avatar
Jean-Christophe Filliatre committed
56 57 58 59
      invariant { forall j: int. 0 <= j < !r ->
                  0 <= to_[j] < i /\ a[j] = (at a 'L)[to_[j]] }
      invariant { forall j: int. 0 <= j < i ->
                  0 <= from[j] < !r /\ (at a 'L)[j] = a[from[j]] }
60
      invariant { forall j: int. i <= j < n -> a[j] = (at a 'L)[j] }
61 62 63 64 65 66 67 68 69 70
      let ghost w = ref 0 in
      if not (test_appears w a[i] a !r) then begin
        a[!r] <- a[i];
        from[i] <- !r;
        to_[!r] <- i;
        incr r
      end else begin
        from[i] <- !w;
        ()
      end
71 72 73 74
    done;
    !r

end