ligo/vendors/UnionFind/Partition1.ml

76 lines
2.2 KiB
OCaml

(* Persistent implementation of Union/Find with height-balanced
forests and no path compression: O(n*log(n)).
In the definition of type [t], the height component is that of the
source, that is, if [ItemMap.find i m = (j,h)], then [h] is the
height of [i] (_not_ [j]).
*)
module Make (Item: Partition.Item) =
struct
type item = Item.t
let equal i j = Item.compare i j = 0
module ItemMap = Map.Make (Item)
type height = int
type partition = (item * height) ItemMap.t
type t = partition
let empty = ItemMap.empty
let rec seek (i: item) (p: partition) : item * height =
let j, _ as i' = ItemMap.find i p in
if equal i j then i' else seek j p
let repr i p = fst (seek i p)
let is_equiv (i: item) (j: item) (p: partition) : bool =
try equal (repr i p) (repr j p) with Not_found -> false
let get_or_set (i: item) (p: partition) =
try seek i p, p with
Not_found -> let i' = i,0 in i', ItemMap.add i i' p
let mem i p = try Some (repr i p) with Not_found -> None
let repr i p = try repr i p with Not_found -> i
let equiv (i: item) (j: item) (p: partition) : partition =
let (ri,hi), p = get_or_set i p in
let (rj,hj), p = get_or_set j p in
let add = ItemMap.add in
if equal ri rj
then p
else if hi > hj
then add rj (ri,hj) p
else add ri (rj,hi) (if hi < hj then p else add rj (rj,hj+1) p)
let alias (i: item) (j: item) (p: partition) : partition =
let (ri,hi), p = get_or_set i p in
let (rj,hj), p = get_or_set j p in
let add = ItemMap.add in
if equal ri rj
then p
else if hi = hj || equal ri i
then add ri (rj,hi) @@ add rj (rj, max hj (hi+1)) p
else if hi < hj then add ri (rj,hi) p
else add rj (ri,hj) p
(* Printing *)
let print (p: partition) =
let buffer = Buffer.create 80 in
let print i (j,hi) =
let _,hj = ItemMap.find j p in
let link =
Printf.sprintf "%s,%d -> %s,%d\n"
(Item.to_string i) hi (Item.to_string j) hj
in Buffer.add_string buffer link
in ItemMap.iter print p; buffer
end