···2323 val iter_edges_e : (E.t -> unit) -> t -> unit
2424end
25252626+(** The following implements Hierholzer's algorithm.
2727+2828+ It is sketched as follows:
2929+3030+ 1. make a round trip from a random vertex, by following random
3131+ edges until we get back to the starting point (it will, as we
3232+ first check that all vertices have even degrees).
3333+3434+ 2. if any vertex along this cycle still has outgoing edges, pick one
3535+ and make another round trip from it, and then join the two cycles
3636+ into a single one. Repeat step 2 until all edges are exhausted.
3737+3838+ The implementation makes use of the following:
3939+4040+ - A table, called `out` in the following, that maps each vertex to
4141+ outgoing edges not yet used in the Eulerian path.
4242+4343+ - In order to achieve optimal complexity, paths are built as
4444+ doubly-linked lists, so that we can merge two cycles with a common
4545+ vertex in constant time. This is type `dll` below.
4646+*)
4747+2648module Make(G: G) = struct
27492850 open G
···32543355 module H = Hashtbl.Make(V)
34563535- let setup g =
5757+ type out = E.t H.t H.t
5858+5959+ (** compute the table of outgoing edges *)
6060+ let setup g : int * out =
3661 let nbe = ref 0 in
3762 let out = H.create 16 in
3863 let add h x y e =
···5378 try H.iter (fun v _ -> raise (Found v)) h; assert false
5479 with Found v -> v, H.find h v
55805656- (** 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 *)
5981 type dll = { mutable prev: dll; edge: E.t; mutable next: dll }
60826183 let remove_edge out e =
6262- (* Format.eprintf "remove_edge %a@." print_edge e; *)
6384 let remove h x y =
6485 let s = H.find h x in
6586 assert (H.mem s y);
···82103 remove_edge out e;
83104 e
841058585- (** builds an arbitrary cycle from [start] *)
106106+ (** build an arbitrary cycle from vertex [start] *)
86107 let round_trip edges start =
87108 let e = any_out_edge edges start in
88109 let rec path = { prev = path; edge = e; next = path } in
···102123 e.next <- e';
103124 e'.prev <- e
104125105105- (** builds an Eulerian cycle from [v] *)
126126+ (** build an Eulerian cycle from vertex [start] *)
106127 let eulerian_cycle out start =
107107- (* Format.eprintf "eulerian_cycle from start=%a@." print_vertex start; *)
108128 let todos = H.create 8 in (* vertex on cycle with out edges -> cycle edge *)
109129 let todo e =
110130 let v = E.src e.edge in
···114134 if not (V.equal (E.dst e.edge) start) then update start e.next in
115135 let path = round_trip out start in
116136 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); *)
119137 while H.length todos > 0 do
120138 let v, e = any todos in
121121- (* Format.eprintf " add cycle from %a@." print_vertex v; *)
122139 H.remove todos v;
123140 assert (H.mem out v);
124141 let e' = round_trip out v in
125142 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); *)
128143 let p = e.prev in
129144 assert (p.next == e);
130145 let p' = e'.prev in
···174189 | _, 0 -> rev xy :: list_of (eulerian_cycle out x)
175190 | 0, _ -> xy :: list_of (eulerian_cycle out y)
176191 | _ ->
177177- (* a bit of a pity to use list concatenation, but this
178178- does not change the complexity *)
192192+ (* a bit of a pity to use list concatenation here,
193193+ but this does not change the complexity *)
179194 list_of (eulerian_cycle out x) @
180195 xy :: list_of (eulerian_cycle out y)
181196 ) else (
···185200 H.add (H.find out x) y xy;
186201 H.add (H.find out y) x (rev xy);
187202 let p = eulerian_cycle out x in
188188- let rec find e =
203203+ let rec find e = (* lookup for x--y, to break the cycle there *)
189204 let v = E.src e.edge and w = E.dst e.edge in
190205 if V.equal v x && V.equal w y ||
191206 V.equal v y && V.equal w x then e else find e.next in