(* Union-Find

   Authors: Martin Clochard (Univ Paris Sud)
            Jean-Christophe Filliâtre (CNRS)
	    Mário Pereira (Univ Paris Sud)
	    Simão Melo de Sousa (University Beira Interior)
*)

use int.Int
use int.MinMax
use map.Map
use array.Array
use ref.Ref

type uf = {
          size: int;
          link: array int;
          rank: array int;
  mutable ghost rep : int -> int;
  mutable ghost dst : array int; (* fields dst and maxd are used to prove *)
  mutable ghost maxd: int;       (* termination; see find's variant below *)
}
  invariant { 0 <= size = length link = length rank = length dst }
  invariant { forall x. 0 <= x < size -> 0 <= link[x] < size }
  invariant { forall x. 0 <= x < size -> 0 <= rep x < size }
  invariant { forall x. 0 <= x < size -> rep (rep x) = rep x }
  invariant { forall x. 0 <= x < size -> x = link[x] <-> rep x = x }
  invariant { forall x. 0 <= x < size -> let y = link[x] in
              x <> y -> rep x = rep y /\ dst[x] < dst[y] }
  invariant { 0 <= maxd }
  invariant { forall x. 0 <= x < size -> dst[x] <= maxd }
  by { size = 0; link = make 0 0; rank = make 0 0;
       rep  = (fun x -> x); dst = make 0 0; maxd = 0; }

let create (n: int) : uf
  requires { 0 <= n }
= let l = Array.make n 0 in
  for i = 0 to n - 1 do invariant { forall j. 0 <= j < i -> l[j] = j }
    l[i] <- i
  done;
  { size = n;
    link = l;
    rank = Array.make n 0;
    rep  = (fun x -> x);
    dst  = Array.make n 0;
    maxd = 0; }

(* with path compression *)
let rec find (uf: uf) (x: int) : int
  requires { 0 <= x < uf.size }
  variant  { uf.maxd - uf.dst[x] }
  ensures  { result = uf.rep x }
  ensures  { uf.dst[result] >= uf.dst[x] }
= let y = uf.link[x] in
  if y = x then x
  else begin let rx = find uf y in uf.link[x] <- rx; rx end

let same (uf: uf) (x: int) (y: int) : bool
  requires { 0 <= x < uf.size }
  requires { 0 <= y < uf.size }
  ensures  { result <-> uf.rep x = uf.rep y }
= let a = find uf x in
  let b = find uf y in
  a = b

let ghost inc_dst (dst: int -> int) (x y: int) : int -> int
  ensures { result y = 1 + max (dst x) (dst y) }
  ensures { forall z. z <> y -> result z = dst z }
= Map.set dst y (1 + max (dst x) (dst y))

let union (uf: uf) (x y: int) : ghost int
  requires { 0 <= x < uf.size }
  requires { 0 <= y < uf.size }
  ensures  { result = old (uf.rep x) || result = old (uf.rep y) }
  ensures  { forall z. 0 <= z < uf.size ->
             uf.rep z = if old (uf.rep z = uf.rep x || uf.rep z = uf.rep y)
                        then result else old (uf.rep z) }
= let x = find uf x in
  let y = find uf y in
  if x = y then x else
  let rkx = uf.rank[x] in
  let rky = uf.rank[y] in
  if rkx < rky then begin
    uf.link[x] <- y;
    uf.rep <- pure { fun z -> if uf.rep z = uf.rep x then y else uf.rep z };
    uf.dst[y] <- 1 + max uf.dst[x] uf.dst[y];
    uf.maxd <- uf.maxd + 1;
    y
  end else begin
    uf.link[y] <- x;
    uf.rep <- pure { fun z -> if uf.rep z = uf.rep y then x else uf.rep z };
    uf.dst[x] <- 1 + max uf.dst[x] uf.dst[y];
    uf.maxd <- uf.maxd + 1;
    if rkx = rky then uf.rank[x] <- rkx + 1;
    x
  end

(* Though this is not needed, one can recover from above a global
   invariant that states the existence of a path from each element to
   its representative. *)

inductive path (link: array int) (x y: int) =
  | Path0: forall link x  . path link x x
  | Path1: forall link x y. path link link[x] y -> path link x y

let rec lemma path_rep (uf: uf) (x: int)
  requires { 0 <= x < uf.size }
  ensures  { path uf.link x (uf.rep x) }
  variant  { uf.maxd - uf.dst[x] }
= if uf.link[x] <> x then path_rep uf uf.link[x]


Generated by why3doc 1.3.3+git