···1122 - [Classic]: new functions [cycle] and [grid]
33 - [Eulerian]: Eulerian paths (new module)
44- currently limited to undirected graphs
54 - [Components]: strong articulation points (see functors [Connectivity]
65 and [BiConnectivity]) (Timothy Bourke)
76 - [Dominator]: non-trivial dominators (Timothy Bourke)
+50-10
src/eulerian.ml
···56565757 type out = E.t H.t H.t
58585959+ let add_out_edge out x y e =
6060+ let s = try H.find out x
6161+ with Not_found -> let s = H.create 4 in H.add out x s; s in
6262+ H.add s y e
6363+5964 (** compute the table of outgoing edges *)
6065 let setup g : int * out =
6166 let nbe = ref 0 in
6267 let out = H.create 16 in
6363- let add h x y e =
6464- let s = try H.find h x
6565- with Not_found -> let s = H.create 4 in H.add h x s; s in
6666- if H.mem s y then invalid_arg "Eulerian.path (multigraphs not allowed)";
6767- H.add s y e in
6868 let add e =
6969 incr nbe;
7070 let x = E.src e and y = E.dst e in
7171- add out x y e;
7272- if not is_directed && not (V.equal x y) then add out y x (rev e) in
7171+ add_out_edge out x y e;
7272+ if not is_directed && not (V.equal x y) then
7373+ add_out_edge out y x (rev e) in
7374 iter_edges_e add g;
7475 !nbe, out
7576···180181 let x, _ = any odds in
181182 H.remove odds x;
182183 let y, _ = any odds in
184184+183185 if mem_edge out x y then (
184184- (* there is an edge x--y => it connects two Eulerian cycles *)
186186+ (* there is an edge x--y => it connects 1 or 2 Eulerian cycles *)
185187 let xy = H.find (H.find out x) y in
186188 remove_edge out xy;
187189 match out_degree out x, out_degree out y with
···215217 if H.length out > 0 then invalid_arg "Eulerian.path (not connected)";
216218 path, cycle
217219218218- let directed _g =
219219- invalid_arg "Eulerian.path (directed graphs not yet supported)"
220220+ let directed g =
221221+ let delta = H.create 16 in (* out - in *)
222222+ let add v d =
223223+ H.replace delta v (d + try H.find delta v with Not_found -> 0) in
224224+ let add e =
225225+ add (E.src e) 1; add (E.dst e) (-1) in
226226+ iter_edges_e add g;
227227+ let start = ref None and finish = ref None in
228228+ let check v = function
229229+ | 1 when !start = None -> start := Some v
230230+ | -1 when !finish = None -> finish := Some v
231231+ | 0 -> ()
232232+ | _ -> invalid_arg "Eulerian.path (bad degrees)" in
233233+ H.iter check delta;
234234+ let nbe, out = setup g in
235235+ let path, cycle = match !start, !finish with
236236+ | None, None when nbe = 0 ->
237237+ [], true
238238+ | None, None ->
239239+ let v, _ = any out in list_of (eulerian_cycle out v), true
240240+ | Some s, Some f ->
241241+ (* add one edge f->s, build a cycle, then remove it
242242+ note: there may be already an edge f->s
243243+ if so, we are adding *a second one* and we are careful
244244+ about removing this one, not the other *)
245245+ let dummy = E.label (snd (any (H.find out s))) in
246246+ let fs = E.create f dummy s in
247247+ add_out_edge out f s fs;
248248+ let p = eulerian_cycle out s in
249249+ let rec find e = (* lookup for f->s, to break the cycle there *)
250250+ if e.edge == fs then e else find e.next in
251251+ let start = find p in
252252+ List.tl (list_of start), false
253253+ | Some _, None
254254+ | None, Some _ ->
255255+ assert false (* since the sum of all deltas is zero *)
256256+ in
257257+ (* check that all edges have been consumed *)
258258+ if H.length out > 0 then invalid_arg "Eulerian.path (not connected)";
259259+ path, cycle
220260221261 let path =
222262 if is_directed then directed else undirected
-1
src/eulerian.mli
···22222323 Limitations:
2424 - multigraphs are not supported
2525- - directed graphs not yet supported
2625 *)
27262827module type G = sig
+41-1
tests/test_eulerian.ml
···4242let () = assert (path_length g = 2)
43434444let () = add_edge g v2 v0
4545-let p, c = Eulerian.path g
4645let () = assert (exists_path g)
4746let () = assert (exists_cycle g)
4847let () = assert (path_length g = 3)
···9897 assert (not c);
9998 assert (List.length p = 7)
10099100100+open Pack.Digraph
101101+102102+let exists_path g =
103103+ try ignore (Eulerian.path g); true with Invalid_argument _ -> false
104104+let exists_cycle g =
105105+ try ignore (Eulerian.cycle g); true with Invalid_argument _ -> false
106106+107107+let () =
108108+ for n = 0 to 4 do
109109+ let g, v = Classic.cycle n in
110110+ let p, c = Eulerian.path g in
111111+ assert c;
112112+ assert (List.length p = n);
113113+ if n > 1 then (
114114+ remove_edge g v.(0) v.(1);
115115+ let p, c = Eulerian.path g in
116116+ assert (not c);
117117+ assert (List.length p = n - 1);
118118+ )
119119+ done
120120+121121+let g, v = Classic.cycle 5
122122+let () = add_edge g v.(1) v.(4)
123123+let () = assert (not (exists_cycle g))
124124+let () = assert (exists_path g)
125125+let () = add_edge g v.(4) v.(1)
126126+let () = assert (exists_cycle g)
127127+128128+(* +------- 2 <----+
129129+ v |
130130+ 0(finish) ------> 1(start)
131131+ ^ |
132132+ +------- 3 <----+ *)
133133+134134+let g, v = Classic.cycle 3
135135+let v3 = V.create 3
136136+let () = add_vertex g v3; add_edge g v.(1) v3; add_edge g v3 v.(0)
137137+let _, c = Eulerian.path g
138138+let () = assert (not c)
139139+140140+