2019-09-27 16:55:09 +04:00
|
|
|
(* Persistent implementation of Union/Find with height-balanced
|
2020-01-06 21:26:24 +04:00
|
|
|
forests and no path compression: O(n*log(n)).
|
2019-09-27 16:55:09 +04:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-01-06 21:26:24 +04:00
|
|
|
let rec seek (i: item) (p: partition) : item * height =
|
2019-09-27 16:55:09 +04:00
|
|
|
let j, _ as i' = ItemMap.find i p in
|
|
|
|
if equal i j then i' else seek j p
|
|
|
|
|
2020-01-06 21:26:24 +04:00
|
|
|
let repr i p = fst (seek i p)
|
2019-09-27 16:55:09 +04:00
|
|
|
|
2020-01-06 21:26:24 +04:00
|
|
|
let is_equiv (i: item) (j: item) (p: partition) : bool =
|
|
|
|
try equal (repr i p) (repr j p) with Not_found -> false
|
2019-09-27 16:55:09 +04:00
|
|
|
|
|
|
|
let get_or_set (i: item) (p: partition) =
|
|
|
|
try seek i p, p with
|
2020-01-06 21:26:24 +04:00
|
|
|
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
|
2019-09-27 16:55:09 +04:00
|
|
|
|
|
|
|
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) =
|
2020-01-06 21:26:24 +04:00
|
|
|
let buffer = Buffer.create 80 in
|
2019-09-27 16:55:09 +04:00
|
|
|
let print i (j,hi) =
|
|
|
|
let _,hj = ItemMap.find j p in
|
2020-01-06 21:26:24 +04:00
|
|
|
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
|
2019-09-27 16:55:09 +04:00
|
|
|
|
|
|
|
end
|