···1122-22+ - [Eulerian]: Eulerian paths (new module)
33+ currently limited to undirected graphs
34 - [Components]: strong articulation points (see functors [Connectivity]
45 and [BiConnectivity]) (Timothy Bourke)
56 - [Dominator]: non-trivial dominators (Timothy Bourke)
+212
src/eulerian.ml
···11+(**************************************************************************)
22+(* *)
33+(* Ocamlgraph: a generic graph library for OCaml *)
44+(* Copyright (C) 2004-2010 *)
55+(* Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles *)
66+(* *)
77+(* This software is free software; you can redistribute it and/or *)
88+(* modify it under the terms of the GNU Library General Public *)
99+(* License version 2.1, with the special exception on linking *)
1010+(* described in file LICENSE. *)
1111+(* *)
1212+(* This software is distributed in the hope that it will be useful, *)
1313+(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
1414+(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *)
1515+(* *)
1616+(**************************************************************************)
1717+1818+module type G = sig
1919+ type t
2020+ val is_directed : bool
2121+ module V : Sig.COMPARABLE
2222+ module E : Sig.EDGE with type vertex = V.t
2323+ val iter_edges_e : (E.t -> unit) -> t -> unit
2424+end
2525+2626+module Make(G: G) = struct
2727+2828+ open G
2929+3030+ let rev e =
3131+ E.create (E.dst e) (E.label e) (E.src e)
3232+3333+ module H = Hashtbl.Make(V)
3434+3535+ let setup g =
3636+ let nbe = ref 0 in
3737+ let out = H.create 16 in
3838+ let add h x y e =
3939+ let s = try H.find h x
4040+ with Not_found -> let s = H.create 4 in H.add h x s; s in
4141+ if H.mem s y then invalid_arg "Eulerian.path (multigraphs not allowed)";
4242+ H.add s y e in
4343+ let add e =
4444+ incr nbe;
4545+ let x = E.src e and y = E.dst e in
4646+ add out x y e;
4747+ if not is_directed && not (V.equal x y) then add out y x (rev e) in
4848+ iter_edges_e add g;
4949+ !nbe, out
5050+5151+ exception Found of V.t
5252+ let any h =
5353+ try H.iter (fun v _ -> raise (Found v)) h; assert false
5454+ with Found v -> v, H.find h v
5555+5656+ (** in order to achieve optimal complexity, paths are built as
5757+ doubly-linked lists, so that we can merge two cycles with a
5858+ common vertex in constant time *)
5959+ type dll = { mutable prev: dll; edge: E.t; mutable next: dll }
6060+6161+ let remove_edge out e =
6262+ (* Format.eprintf "remove_edge %a@." print_edge e; *)
6363+ let remove h x y =
6464+ let s = H.find h x in
6565+ assert (H.mem s y);
6666+ H.remove s y;
6767+ if H.length s = 0 then H.remove h x in
6868+ let v = E.src e and w = E.dst e in
6969+ remove out v w
7070+7171+ let self e = V.equal (E.src e) (E.dst e)
7272+7373+ let remove_edge edges e =
7474+ remove_edge edges e;
7575+ if not is_directed && not (self e) then remove_edge edges (rev e)
7676+7777+ let any_out_edge out v =
7878+ assert (H.mem out v);
7979+ let s = H.find out v in
8080+ assert (H.length s > 0);
8181+ let _, e = any s in
8282+ remove_edge out e;
8383+ e
8484+8585+ (** builds an arbitrary cycle from [start] *)
8686+ let round_trip edges start =
8787+ let e = any_out_edge edges start in
8888+ let rec path = { prev = path; edge = e; next = path } in
8989+ let rec tour e =
9090+ let v = E.dst e.edge in
9191+ if V.equal v start then (
9292+ path.prev <- e;
9393+ path
9494+ ) else (
9595+ let e' = { prev = e; edge = any_out_edge edges v; next = path } in
9696+ e.next <- e';
9797+ tour e'
9898+ ) in
9999+ tour path
100100+101101+ let connect e e' =
102102+ e.next <- e';
103103+ e'.prev <- e
104104+105105+ (** builds an Eulerian cycle from [v] *)
106106+ let eulerian_cycle out start =
107107+ (* Format.eprintf "eulerian_cycle from start=%a@." print_vertex start; *)
108108+ let todos = H.create 8 in (* vertex on cycle with out edges -> cycle edge *)
109109+ let todo e =
110110+ let v = E.src e.edge in
111111+ if H.mem out v then H.replace todos v e else H.remove todos v in
112112+ let rec update start e =
113113+ todo e;
114114+ if not (V.equal (E.dst e.edge) start) then update start e.next in
115115+ let path = round_trip out start in
116116+ update start path;
117117+ (* H.iter (fun v s -> eprintf " out %a = %d@." print_vertex v (H.length s)) out;
118118+ * eprintf " %d todos@." (H.length todos); *)
119119+ while H.length todos > 0 do
120120+ let v, e = any todos in
121121+ (* Format.eprintf " add cycle from %a@." print_vertex v; *)
122122+ H.remove todos v;
123123+ assert (H.mem out v);
124124+ let e' = round_trip out v in
125125+ update v e';
126126+ (* H.iter (fun v s -> eprintf " out %a = %d@." print_vertex v (H.length s)) out;
127127+ * eprintf " %d todos@." (H.length todos); *)
128128+ let p = e.prev in
129129+ assert (p.next == e);
130130+ let p' = e'.prev in
131131+ assert (p'.next == e');
132132+ connect p e';
133133+ connect p' e;
134134+ done;
135135+ path
136136+137137+ let list_of path =
138138+ let rec convert acc e =
139139+ if e == path then List.rev acc else convert (e.edge :: acc) e.next in
140140+ convert [path.edge] path.next
141141+142142+ let mem_edge out x y =
143143+ try H.mem (H.find out x) y with Not_found -> false
144144+145145+ let out_degree out x =
146146+ try H.length (H.find out x) with Not_found -> 0
147147+148148+ let undirected g =
149149+ let nbe, out = setup g in
150150+ let odds = H.create 2 in
151151+ let check v s =
152152+ let d = H.length s in
153153+ let d = if H.mem s v then d - 1 else d in
154154+ if d mod 2 = 1 then H.add odds v () in
155155+ H.iter check out;
156156+ let n = H.length odds in
157157+ if n <> 0 && n <> 2 then invalid_arg "Eulerian.path (bad degrees)";
158158+ let cycle = n = 0 in
159159+ let path =
160160+ if cycle then
161161+ if nbe = 0 then []
162162+ else let v, _ = any out in list_of (eulerian_cycle out v)
163163+ else (
164164+ (* we have two vertices x and y with odd degrees *)
165165+ let x, _ = any odds in
166166+ H.remove odds x;
167167+ let y, _ = any odds in
168168+ if mem_edge out x y then (
169169+ (* there is an edge x--y => it connects two Eulerian cycles *)
170170+ let xy = H.find (H.find out x) y in
171171+ remove_edge out xy;
172172+ match out_degree out x, out_degree out y with
173173+ | 0, 0 -> [xy]
174174+ | _, 0 -> rev xy :: list_of (eulerian_cycle out x)
175175+ | 0, _ -> xy :: list_of (eulerian_cycle out y)
176176+ | _ ->
177177+ (* a bit of a pity to use list concatenation, but this
178178+ does not change the complexity *)
179179+ list_of (eulerian_cycle out x) @
180180+ xy :: list_of (eulerian_cycle out y)
181181+ ) else (
182182+ (* no edge x--y => add one, build a cycle, then remove it *)
183183+ let dummy = E.label (snd (any (H.find out x))) in
184184+ let xy = E.create x dummy y in
185185+ H.add (H.find out x) y xy;
186186+ H.add (H.find out y) x (rev xy);
187187+ let p = eulerian_cycle out x in
188188+ let rec find e =
189189+ let v = E.src e.edge and w = E.dst e.edge in
190190+ if V.equal v x && V.equal w y ||
191191+ V.equal v y && V.equal w x then e else find e.next in
192192+ let start = find p in
193193+ List.tl (list_of start)
194194+ )
195195+ )
196196+ in
197197+ (* check that all edges have been consumed *)
198198+ if H.length out > 0 then invalid_arg "Eulerian.path (not connected)";
199199+ path, cycle
200200+201201+ let directed _g =
202202+ invalid_arg "Eulerian.path (directed graphs not yet supported)"
203203+204204+ let path g =
205205+ if is_directed then directed g else undirected g
206206+207207+ let cycle g =
208208+ let p, c = path g in
209209+ if not c then invalid_arg "Eulerian.cycle";
210210+ p
211211+212212+end
+47
src/eulerian.mli
···11+(**************************************************************************)
22+(* *)
33+(* Ocamlgraph: a generic graph library for OCaml *)
44+(* Copyright (C) 2004-2010 *)
55+(* Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles *)
66+(* *)
77+(* This software is free software; you can redistribute it and/or *)
88+(* modify it under the terms of the GNU Library General Public *)
99+(* License version 2.1, with the special exception on linking *)
1010+(* described in file LICENSE. *)
1111+(* *)
1212+(* This software is distributed in the hope that it will be useful, *)
1313+(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
1414+(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *)
1515+(* *)
1616+(**************************************************************************)
1717+1818+(** Eulerian path
1919+2020+ This module implements Hierholzer's algorithm, with O(E) complexity
2121+ where E is the number of edges.
2222+2323+ Limitations:
2424+ - multigraphs are not supported
2525+ - directed graphs not yet supported
2626+ *)
2727+2828+module type G = sig
2929+ type t
3030+ val is_directed : bool
3131+ module V : Sig.COMPARABLE
3232+ module E : Sig.EDGE with type vertex = V.t
3333+ val iter_edges_e : (E.t -> unit) -> t -> unit
3434+end
3535+3636+module Make(G: G) : sig
3737+3838+ val path: G.t -> G.E.t list * bool
3939+ (** [path g] returns an Eulerian path of [g]. The Boolean indicates
4040+ whether the path is a cycle. Raises [Invalid_argument] if there
4141+ is no Eulerian path. *)
4242+4343+ val cycle: G.t -> G.E.t list
4444+ (** [cycle g] returns an Eulerian cycle of [g].
4545+ Raises [Invalid_argument] if there is no Eulerian cycle. *)
4646+4747+end
+4
src/pack.ml
···9393 let iter_stable = S.iter
9494 end
95959696+ module Eulerian = struct
9797+ include Eulerian.Make(G)
9898+ end
9999+96100 module Int = struct
97101 type t = int
98102 let compare : t -> t -> int = Stdlib.compare
+10
src/sig_pack.mli
···409409 val iter_stable : (V.t -> unit) -> t -> unit
410410 end
411411412412+ (** Eulerian path *)
413413+ module Eulerian : sig
414414+ val path: t -> E.t list * bool
415415+ (** [path g] returns an Eulerian path of g. The Boolean indicates
416416+ whether the path is a cycle. Raises [Invalid_argument] if there is
417417+ no Eulerian path. *)
418418+419419+ val cycle: t -> E.t list
420420+ end
421421+412422 val spanningtree : t -> E.t list
413423 (** Kruskal algorithm *)
414424