/src/igraph/src/paths/eulerian.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | IGraph library. |
3 | | Copyright (C) 2005-2020 The igraph development team |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program; if not, write to the Free Software |
17 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | | 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include "igraph_eulerian.h" |
22 | | |
23 | | #include "igraph_adjlist.h" |
24 | | #include "igraph_bitset.h" |
25 | | #include "igraph_interface.h" |
26 | | #include "igraph_components.h" |
27 | | #include "igraph_stack.h" |
28 | | |
29 | | /** |
30 | | * \section about_eulerian |
31 | | * |
32 | | * <para>These functions calculate whether an Eulerian path or cycle exists |
33 | | * and if so, can find them.</para> |
34 | | */ |
35 | | |
36 | | |
37 | | /* solution adapted from https://www.geeksforgeeks.org/eulerian-path-and-circuit/ |
38 | | The function returns one of the following values |
39 | | has_path is set to 1 if a path exists, 0 otherwise |
40 | | has_cycle is set to 1 if a cycle exists, 0 otherwise |
41 | | */ |
42 | | static igraph_error_t igraph_i_is_eulerian_undirected( |
43 | 3.70k | const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { |
44 | 3.70k | igraph_integer_t odd; |
45 | 3.70k | igraph_vector_int_t degree; |
46 | 3.70k | igraph_vector_int_t csize; |
47 | | /* boolean vector to mark singletons: */ |
48 | 3.70k | igraph_vector_int_t nonsingleton; |
49 | 3.70k | igraph_integer_t i, n, vsize; |
50 | 3.70k | igraph_integer_t cluster_count; |
51 | | /* number of self-looping singletons: */ |
52 | 3.70k | igraph_integer_t es; |
53 | | /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ |
54 | 3.70k | igraph_integer_t ens; |
55 | | |
56 | 3.70k | n = igraph_vcount(graph); |
57 | | |
58 | 3.70k | if (igraph_ecount(graph) == 0 || n <= 1) { |
59 | 366 | start_of_path = 0; /* in case the graph has one vertex with self-loops */ |
60 | 366 | *has_path = true; |
61 | 366 | *has_cycle = true; |
62 | 366 | return IGRAPH_SUCCESS; |
63 | 366 | } |
64 | | |
65 | | /* check for connectedness, but singletons are special since they affect |
66 | | * the Eulerian nature only if there is a self-loop AND another edge |
67 | | * somewhere else in the graph */ |
68 | 3.34k | IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); |
69 | 3.34k | IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); |
70 | 3.34k | cluster_count = 0; |
71 | 3.34k | vsize = igraph_vector_int_size(&csize); |
72 | 384k | for (i = 0; i < vsize; i++) { |
73 | 381k | if (VECTOR(csize)[i] > 1) { |
74 | 3.43k | cluster_count++; |
75 | 3.43k | if (cluster_count > 1) { |
76 | | /* disconnected edges, they'll never reach each other */ |
77 | 526 | *has_path = false; |
78 | 526 | *has_cycle = false; |
79 | 526 | igraph_vector_int_destroy(&csize); |
80 | 526 | IGRAPH_FINALLY_CLEAN(1); |
81 | | |
82 | 526 | return IGRAPH_SUCCESS; |
83 | 526 | } |
84 | 3.43k | } |
85 | 381k | } |
86 | | |
87 | 2.81k | igraph_vector_int_destroy(&csize); |
88 | 2.81k | IGRAPH_FINALLY_CLEAN(1); |
89 | | |
90 | | /* the graph is connected except for singletons */ |
91 | | /* find singletons (including those with self-loops) */ |
92 | 2.81k | IGRAPH_VECTOR_INT_INIT_FINALLY(&nonsingleton, 0); |
93 | 2.81k | IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); |
94 | | |
95 | | /* check the degrees for odd/even: |
96 | | * - >= 2 odd means no cycle (1 odd is impossible) |
97 | | * - > 2 odd means no path |
98 | | * plus there are a few corner cases with singletons |
99 | | */ |
100 | 2.81k | IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); |
101 | 2.81k | IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); |
102 | 2.81k | odd = 0; |
103 | 2.81k | es = 0; |
104 | 2.81k | ens = 0; |
105 | 393k | for (i = 0; i < n; i++) { |
106 | 390k | igraph_integer_t deg = VECTOR(degree)[i]; |
107 | | /* Eulerian is about edges, so skip free vertices */ |
108 | 390k | if (deg == 0) continue; |
109 | | |
110 | 36.8k | if (!VECTOR(nonsingleton)[i]) { |
111 | | /* singleton with self loops */ |
112 | 520 | es++; |
113 | 36.3k | } else { |
114 | | /* at least one non-singleton */ |
115 | 36.3k | ens = 1; |
116 | | /* note: self-loops count for two (in and out) */ |
117 | 36.3k | if (deg % 2) odd++; |
118 | 36.3k | } |
119 | | |
120 | 36.8k | if (es + ens > 1) { |
121 | | /* 2+ singletons with self loops or singleton with self-loops and |
122 | | * 1+ edges in the non-singleton part of the graph. */ |
123 | 86 | *has_path = false; |
124 | 86 | *has_cycle = false; |
125 | 86 | igraph_vector_int_destroy(&nonsingleton); |
126 | 86 | igraph_vector_int_destroy(°ree); |
127 | 86 | IGRAPH_FINALLY_CLEAN(2); |
128 | | |
129 | 86 | return IGRAPH_SUCCESS; |
130 | 86 | } |
131 | 36.8k | } |
132 | | |
133 | 2.72k | igraph_vector_int_destroy(&nonsingleton); |
134 | 2.72k | IGRAPH_FINALLY_CLEAN(1); |
135 | | |
136 | | /* this is the usual algorithm on the connected part of the graph */ |
137 | 2.72k | if (odd > 2) { |
138 | 670 | *has_path = false; |
139 | 670 | *has_cycle = false; |
140 | 2.05k | } else if (odd == 2) { |
141 | 1.05k | *has_path = true; |
142 | 1.05k | *has_cycle = false; |
143 | 1.05k | } else { |
144 | 1.00k | *has_path = true; |
145 | 1.00k | *has_cycle = true; |
146 | 1.00k | } |
147 | | |
148 | | /* set start of path if there is one but there is no cycle */ |
149 | | /* note: we cannot do this in the previous loop because at that time we are |
150 | | * not sure yet if a path exists */ |
151 | 72.7k | for (i = 0; i < n; i++) { |
152 | 72.7k | if ((*has_cycle && VECTOR(degree)[i] > 0) || (!*has_cycle && VECTOR(degree)[i] %2 == 1)) { |
153 | 2.72k | *start_of_path = i; |
154 | 2.72k | break; |
155 | 2.72k | } |
156 | 72.7k | } |
157 | | |
158 | 2.72k | igraph_vector_int_destroy(°ree); |
159 | 2.72k | IGRAPH_FINALLY_CLEAN(1); |
160 | | |
161 | 2.72k | return IGRAPH_SUCCESS; |
162 | 2.81k | } |
163 | | |
164 | | |
165 | | static igraph_error_t igraph_i_is_eulerian_directed( |
166 | 3.39k | const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle, igraph_integer_t *start_of_path) { |
167 | 3.39k | igraph_integer_t incoming_excess, outgoing_excess, n; |
168 | 3.39k | igraph_integer_t i, vsize; |
169 | 3.39k | igraph_integer_t cluster_count; |
170 | 3.39k | igraph_vector_int_t out_degree, in_degree; |
171 | 3.39k | igraph_vector_int_t csize; |
172 | | /* boolean vector to mark singletons: */ |
173 | 3.39k | igraph_vector_int_t nonsingleton; |
174 | | /* number of self-looping singletons: */ |
175 | 3.39k | igraph_integer_t es; |
176 | | /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ |
177 | 3.39k | igraph_integer_t ens; |
178 | | |
179 | 3.39k | n = igraph_vcount(graph); |
180 | | |
181 | 3.39k | if (igraph_ecount(graph) == 0 || n <= 1) { |
182 | 427 | start_of_path = 0; /* in case the graph has one vertex with self-loops */ |
183 | 427 | *has_path = true; |
184 | 427 | *has_cycle = true; |
185 | 427 | return IGRAPH_SUCCESS; |
186 | 427 | } |
187 | | |
188 | 2.96k | incoming_excess = 0; |
189 | 2.96k | outgoing_excess = 0; |
190 | | |
191 | | /* check for weak connectedness, but singletons are special since they affect |
192 | | * the Eulerian nature only if there is a self-loop AND another edge |
193 | | * somewhere else in the graph */ |
194 | 2.96k | IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); |
195 | | |
196 | 2.96k | IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); |
197 | 2.96k | cluster_count = 0; |
198 | 2.96k | vsize = igraph_vector_int_size(&csize); |
199 | 351k | for (i = 0; i < vsize; i++) { |
200 | 349k | if (VECTOR(csize)[i] > 1) { |
201 | 3.28k | cluster_count++; |
202 | 3.28k | if (cluster_count > 1) { |
203 | | /* weakly disconnected edges, they'll never reach each other */ |
204 | 501 | *has_path = false; |
205 | 501 | *has_cycle = false; |
206 | 501 | igraph_vector_int_destroy(&csize); |
207 | 501 | IGRAPH_FINALLY_CLEAN(1); |
208 | | |
209 | 501 | return IGRAPH_SUCCESS; |
210 | 501 | } |
211 | 3.28k | } |
212 | 349k | } |
213 | | |
214 | 2.46k | igraph_vector_int_destroy(&csize); |
215 | 2.46k | IGRAPH_FINALLY_CLEAN(1); |
216 | | |
217 | | /* the graph is weakly connected except for singletons */ |
218 | | /* find the singletons (including those with self-loops) */ |
219 | 2.46k | IGRAPH_VECTOR_INT_INIT_FINALLY(&nonsingleton, 0); |
220 | 2.46k | IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); |
221 | | |
222 | | |
223 | | /* checking if no. of incoming edges == outgoing edges |
224 | | * plus there are a few corner cases with singletons */ |
225 | 2.46k | IGRAPH_VECTOR_INT_INIT_FINALLY(&out_degree, 0); |
226 | 2.46k | IGRAPH_CHECK(igraph_degree(graph, &out_degree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); |
227 | | |
228 | 2.46k | IGRAPH_VECTOR_INT_INIT_FINALLY(&in_degree, 0); |
229 | 2.46k | IGRAPH_CHECK(igraph_degree(graph, &in_degree, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); |
230 | 2.46k | es = 0; |
231 | 2.46k | ens = 0; |
232 | 2.46k | *start_of_path = -1; |
233 | 246k | for (i = 0; i < n; i++) { |
234 | 244k | igraph_integer_t degin = VECTOR(in_degree)[i]; |
235 | 244k | igraph_integer_t degout = VECTOR(out_degree)[i]; |
236 | | |
237 | | /* Eulerian is about edges, so skip free vertices */ |
238 | 244k | if (degin + degout == 0) continue; |
239 | | |
240 | 17.4k | if (!VECTOR(nonsingleton)[i]) { |
241 | | /* singleton with self loops */ |
242 | 233 | es++; |
243 | | /* if we ever want a path, it has to be this self-loop */ |
244 | 233 | *start_of_path = i; |
245 | 17.2k | } else { |
246 | | /* at least one non-singleton */ |
247 | 17.2k | ens = 1; |
248 | 17.2k | } |
249 | | |
250 | 17.4k | if (es + ens > 1) { |
251 | | /* 2+ singletons with self loops or singleton with self-loops and |
252 | | * 1+ edges in the non-singleton part of the graph. */ |
253 | 52 | *has_path = false; |
254 | 52 | *has_cycle = false; |
255 | 52 | igraph_vector_int_destroy(&nonsingleton); |
256 | 52 | igraph_vector_int_destroy(&in_degree); |
257 | 52 | igraph_vector_int_destroy(&out_degree); |
258 | 52 | IGRAPH_FINALLY_CLEAN(3); |
259 | | |
260 | 52 | return IGRAPH_SUCCESS; |
261 | 52 | } |
262 | | |
263 | | /* as long as we have perfect balance, you can start |
264 | | * anywhere with an edge */ |
265 | 17.4k | if (*start_of_path == -1 && incoming_excess == 0 && outgoing_excess == 0) { |
266 | 2.26k | *start_of_path = i; |
267 | 2.26k | } |
268 | | |
269 | | /* same in and out (including self-loops, even in singletons) */ |
270 | 17.4k | if (degin == degout) { |
271 | 14.7k | continue; |
272 | 14.7k | } |
273 | | |
274 | | /* non-singleton, in != out */ |
275 | 2.70k | if (degin > degout) { |
276 | 1.30k | incoming_excess += degin - degout; |
277 | 1.39k | } else { |
278 | 1.39k | outgoing_excess += degout - degin; |
279 | 1.39k | if (outgoing_excess == 1) { |
280 | 910 | *start_of_path = i; |
281 | 910 | } |
282 | 1.39k | } |
283 | | |
284 | | /* too much imbalance, either of the following: |
285 | | * 1. 1+ vertices have 2+ in/out |
286 | | * 2. 2+ nodes have 1+ in/out */ |
287 | 2.70k | if (incoming_excess > 1 || outgoing_excess > 1) { |
288 | 865 | *has_path = false; |
289 | 865 | *has_cycle = false; |
290 | 865 | igraph_vector_int_destroy(&nonsingleton); |
291 | 865 | igraph_vector_int_destroy(&in_degree); |
292 | 865 | igraph_vector_int_destroy(&out_degree); |
293 | 865 | IGRAPH_FINALLY_CLEAN(3); |
294 | | |
295 | 865 | return IGRAPH_SUCCESS; |
296 | 865 | } |
297 | 2.70k | } |
298 | | |
299 | 1.54k | *has_path = true; |
300 | | /* perfect edge balance -> strong connectivity */ |
301 | 1.54k | *has_cycle = (incoming_excess == 0) && (outgoing_excess == 0); |
302 | | /* either way, the start was set already */ |
303 | | |
304 | 1.54k | igraph_vector_int_destroy(&nonsingleton); |
305 | 1.54k | igraph_vector_int_destroy(&in_degree); |
306 | 1.54k | igraph_vector_int_destroy(&out_degree); |
307 | 1.54k | IGRAPH_FINALLY_CLEAN(3); |
308 | | |
309 | 1.54k | return IGRAPH_SUCCESS; |
310 | 2.46k | } |
311 | | |
312 | | |
313 | | /** |
314 | | * \ingroup Eulerian |
315 | | * \function igraph_is_eulerian |
316 | | * \brief Checks whether an Eulerian path or cycle exists. |
317 | | * |
318 | | * An Eulerian path traverses each edge of the graph precisely once. A closed |
319 | | * Eulerian path is referred to as an Eulerian cycle. |
320 | | * |
321 | | * \param graph The graph object. |
322 | | * \param has_path Pointer to a Boolean, will be set to true if an Eulerian path exists. |
323 | | * Must not be \c NULL. |
324 | | * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. |
325 | | * Must not be \c NULL. |
326 | | * \return Error code: |
327 | | * \c IGRAPH_ENOMEM, not enough memory for temporary data. |
328 | | * |
329 | | * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. |
330 | | * |
331 | | */ |
332 | | |
333 | 4.78k | igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle) { |
334 | 4.78k | igraph_integer_t start_of_path = 0; |
335 | | |
336 | 4.78k | if (igraph_is_directed(graph)) { |
337 | 2.32k | IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, has_path, has_cycle, &start_of_path)); |
338 | 2.45k | } else { |
339 | 2.45k | IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, has_path, has_cycle, &start_of_path)); |
340 | 2.45k | } |
341 | 4.78k | return IGRAPH_SUCCESS; |
342 | 4.78k | } |
343 | | |
344 | | |
345 | | static igraph_error_t igraph_i_eulerian_path_undirected( |
346 | | const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, |
347 | 1.25k | igraph_integer_t start_of_path) { |
348 | | |
349 | 1.25k | igraph_integer_t curr; |
350 | 1.25k | igraph_integer_t n, m; |
351 | 1.25k | igraph_inclist_t il; |
352 | 1.25k | igraph_stack_int_t path, tracker, edge_tracker, edge_path; |
353 | 1.25k | igraph_bitset_t visited_list; |
354 | 1.25k | igraph_vector_int_t degree; |
355 | | |
356 | 1.25k | n = igraph_vcount(graph); |
357 | 1.25k | m = igraph_ecount(graph); |
358 | | |
359 | 1.25k | if (edge_res) { |
360 | 1.25k | igraph_vector_int_clear(edge_res); |
361 | 1.25k | } |
362 | | |
363 | 1.25k | if (vertex_res) { |
364 | 1.25k | igraph_vector_int_clear(vertex_res); |
365 | 1.25k | } |
366 | | |
367 | 1.25k | if (m == 0 || n == 0) { |
368 | 182 | return IGRAPH_SUCCESS; |
369 | 182 | } |
370 | | |
371 | 1.06k | IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); |
372 | 1.06k | IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); |
373 | | |
374 | 1.06k | IGRAPH_STACK_INT_INIT_FINALLY(&path, n); |
375 | 1.06k | IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); |
376 | 1.06k | IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); |
377 | 1.06k | IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); |
378 | 1.06k | IGRAPH_BITSET_INIT_FINALLY(&visited_list, m); |
379 | | |
380 | 1.06k | IGRAPH_CHECK(igraph_stack_int_push(&tracker, start_of_path)); |
381 | | |
382 | 1.06k | IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); |
383 | 1.06k | IGRAPH_FINALLY(igraph_inclist_destroy, &il); |
384 | | |
385 | 1.06k | curr = start_of_path; |
386 | | |
387 | 53.9k | while (!igraph_stack_int_empty(&tracker)) { |
388 | | |
389 | 52.9k | if (VECTOR(degree)[curr] != 0) { |
390 | 25.9k | igraph_vector_int_t *incedges; |
391 | 25.9k | igraph_integer_t nc, edge = -1; |
392 | 25.9k | igraph_integer_t j, next; |
393 | 25.9k | IGRAPH_CHECK(igraph_stack_int_push(&tracker, curr)); |
394 | | |
395 | 25.9k | incedges = igraph_inclist_get(&il, curr); |
396 | 25.9k | nc = igraph_vector_int_size(incedges); |
397 | 25.9k | IGRAPH_ASSERT(nc > 0); |
398 | | |
399 | 1.24M | for (j = 0; j < nc; j++) { |
400 | 1.24M | edge = VECTOR(*incedges)[j]; |
401 | 1.24M | if (!IGRAPH_BIT_TEST(visited_list, edge)) { |
402 | 25.9k | break; |
403 | 25.9k | } |
404 | 1.24M | } |
405 | | |
406 | 25.9k | next = IGRAPH_OTHER(graph, edge, curr); |
407 | | |
408 | 25.9k | IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); |
409 | | |
410 | | /* remove edge here */ |
411 | 25.9k | VECTOR(degree)[curr]--; |
412 | 25.9k | VECTOR(degree)[next]--; |
413 | 25.9k | IGRAPH_BIT_SET(visited_list, edge); |
414 | | |
415 | 25.9k | curr = next; |
416 | 26.9k | } else { /* back track to find remaining circuit */ |
417 | 26.9k | igraph_integer_t curr_e; |
418 | 26.9k | IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); |
419 | 26.9k | curr = igraph_stack_int_pop(&tracker); |
420 | 26.9k | if (!igraph_stack_int_empty(&edge_tracker)) { |
421 | 25.9k | curr_e = igraph_stack_int_pop(&edge_tracker); |
422 | 25.9k | IGRAPH_CHECK(igraph_stack_int_push(&edge_path, curr_e)); |
423 | 25.9k | } |
424 | 26.9k | } |
425 | 52.9k | } |
426 | | |
427 | 1.06k | if (edge_res) { |
428 | 1.06k | IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); |
429 | 26.9k | while (!igraph_stack_int_empty(&edge_path)) { |
430 | 25.9k | IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); |
431 | 25.9k | } |
432 | 1.06k | } |
433 | 1.06k | if (vertex_res) { |
434 | 1.06k | IGRAPH_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); |
435 | 28.0k | while (!igraph_stack_int_empty(&path)) { |
436 | 26.9k | IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); |
437 | 26.9k | } |
438 | 1.06k | } |
439 | | |
440 | 1.06k | igraph_stack_int_destroy(&path); |
441 | 1.06k | igraph_stack_int_destroy(&tracker); |
442 | 1.06k | igraph_stack_int_destroy(&edge_path); |
443 | 1.06k | igraph_stack_int_destroy(&edge_tracker); |
444 | 1.06k | igraph_bitset_destroy(&visited_list); |
445 | 1.06k | igraph_inclist_destroy(&il); |
446 | 1.06k | igraph_vector_int_destroy(°ree); |
447 | 1.06k | IGRAPH_FINALLY_CLEAN(7); |
448 | | |
449 | 1.06k | return IGRAPH_SUCCESS; |
450 | 1.06k | } |
451 | | |
452 | | /* solution adapted from https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ */ |
453 | | static igraph_error_t igraph_i_eulerian_path_directed( |
454 | | const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, |
455 | 1.06k | igraph_integer_t start_of_path) { |
456 | | |
457 | 1.06k | igraph_integer_t curr; |
458 | 1.06k | igraph_integer_t n, m; |
459 | 1.06k | igraph_inclist_t il; |
460 | 1.06k | igraph_stack_int_t path, tracker, edge_tracker, edge_path; |
461 | 1.06k | igraph_bitset_t visited_list; |
462 | 1.06k | igraph_vector_int_t remaining_out_edges; |
463 | | |
464 | 1.06k | n = igraph_vcount(graph); |
465 | 1.06k | m = igraph_ecount(graph); |
466 | | |
467 | 1.06k | if (edge_res) { |
468 | 1.06k | igraph_vector_int_clear(edge_res); |
469 | 1.06k | } |
470 | | |
471 | 1.06k | if (vertex_res) { |
472 | 1.06k | igraph_vector_int_clear(vertex_res); |
473 | 1.06k | } |
474 | | |
475 | 1.06k | if (m == 0 || n == 0) { |
476 | 210 | return IGRAPH_SUCCESS; |
477 | 210 | } |
478 | | |
479 | 851 | IGRAPH_STACK_INT_INIT_FINALLY(&path, n); |
480 | 851 | IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); |
481 | 851 | IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); |
482 | 851 | IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); |
483 | 851 | IGRAPH_BITSET_INIT_FINALLY(&visited_list, m); |
484 | | |
485 | 851 | IGRAPH_CHECK(igraph_stack_int_push(&tracker, start_of_path)); |
486 | | |
487 | 851 | IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); |
488 | 851 | IGRAPH_FINALLY(igraph_inclist_destroy, &il); |
489 | | |
490 | 851 | IGRAPH_VECTOR_INT_INIT_FINALLY(&remaining_out_edges, 0); |
491 | 851 | IGRAPH_CHECK(igraph_degree(graph, &remaining_out_edges, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); |
492 | | |
493 | 851 | curr = start_of_path; |
494 | | |
495 | 43.0k | while (!igraph_stack_int_empty(&tracker)) { |
496 | | |
497 | 42.2k | if (VECTOR(remaining_out_edges)[curr] != 0) { |
498 | 20.6k | igraph_vector_int_t *incedges; |
499 | 20.6k | igraph_integer_t nc, edge = -1; |
500 | 20.6k | igraph_integer_t j, next; |
501 | 20.6k | IGRAPH_CHECK(igraph_stack_int_push(&tracker, curr)); |
502 | | |
503 | 20.6k | incedges = igraph_inclist_get(&il, curr); |
504 | 20.6k | nc = igraph_vector_int_size(incedges); |
505 | 20.6k | IGRAPH_ASSERT(nc > 0); |
506 | | |
507 | 791k | for (j = 0; j < nc; j++) { |
508 | 791k | edge = VECTOR(*incedges)[j]; |
509 | 791k | if (!IGRAPH_BIT_TEST(visited_list, edge)) { |
510 | 20.6k | break; |
511 | 20.6k | } |
512 | 791k | } |
513 | | |
514 | 20.6k | next = IGRAPH_TO(graph, edge); |
515 | | |
516 | 20.6k | IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); |
517 | | |
518 | | /* remove edge here */ |
519 | 20.6k | VECTOR(remaining_out_edges)[curr]--; |
520 | 20.6k | IGRAPH_BIT_SET(visited_list, edge); |
521 | | |
522 | 20.6k | curr = next; |
523 | 21.5k | } else { /* back track to find remaining circuit */ |
524 | 21.5k | igraph_integer_t curr_e; |
525 | 21.5k | IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); |
526 | 21.5k | curr = igraph_stack_int_pop(&tracker); |
527 | 21.5k | if (!igraph_stack_int_empty(&edge_tracker)) { |
528 | 20.6k | curr_e = igraph_stack_int_pop(&edge_tracker); |
529 | 20.6k | IGRAPH_CHECK(igraph_stack_int_push(&edge_path, curr_e)); |
530 | 20.6k | } |
531 | 21.5k | } |
532 | 42.2k | } |
533 | | |
534 | 851 | if (edge_res) { |
535 | 851 | IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); |
536 | 21.5k | while (!igraph_stack_int_empty(&edge_path)) { |
537 | 20.6k | IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); |
538 | 20.6k | } |
539 | 851 | } |
540 | 851 | if (vertex_res) { |
541 | 851 | IGRAPH_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); |
542 | 22.3k | while (!igraph_stack_int_empty(&path)) { |
543 | 21.5k | IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); |
544 | 21.5k | } |
545 | 851 | } |
546 | | |
547 | 851 | igraph_stack_int_destroy(&path); |
548 | 851 | igraph_stack_int_destroy(&tracker); |
549 | 851 | igraph_stack_int_destroy(&edge_path); |
550 | 851 | igraph_stack_int_destroy(&edge_tracker); |
551 | 851 | igraph_bitset_destroy(&visited_list); |
552 | 851 | igraph_inclist_destroy(&il); |
553 | 851 | igraph_vector_int_destroy(&remaining_out_edges); |
554 | 851 | IGRAPH_FINALLY_CLEAN(7); |
555 | | |
556 | 851 | return IGRAPH_SUCCESS; |
557 | 851 | } |
558 | | |
559 | | |
560 | | /** |
561 | | * \ingroup Eulerian |
562 | | * \function igraph_eulerian_cycle |
563 | | * \brief Finds an Eulerian cycle. |
564 | | * |
565 | | * Finds an Eulerian cycle, if it exists. An Eulerian cycle is a closed path |
566 | | * that traverses each edge precisely once. |
567 | | * |
568 | | * </para><para> |
569 | | * If the graph has no edges, a zero-length cycle is returned. |
570 | | * |
571 | | * </para><para> |
572 | | * This function uses Hierholzer's algorithm. |
573 | | * |
574 | | * \param graph The graph object. |
575 | | * \param edge_res Pointer to an initialised vector. The indices of edges |
576 | | * belonging to the cycle will be stored here. May be \c NULL |
577 | | * if it is not needed by the caller. |
578 | | * \param vertex_res Pointer to an initialised vector. The indices of vertices |
579 | | * belonging to the cycle will be stored here. The first and |
580 | | * last vertex in the vector will be the same. May be \c NULL |
581 | | * if it is not needed by the caller. |
582 | | * \return Error code: |
583 | | * \clist |
584 | | * \cli IGRAPH_ENOMEM |
585 | | * not enough memory for temporary data. |
586 | | * \cli IGRAPH_ENOSOL |
587 | | * graph does not have an Eulerian cycle. |
588 | | * \endclist |
589 | | * |
590 | | * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. |
591 | | * |
592 | | */ |
593 | | |
594 | | igraph_error_t igraph_eulerian_cycle( |
595 | 789 | const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { |
596 | | |
597 | 789 | igraph_bool_t has_cycle; |
598 | 789 | igraph_bool_t has_path; |
599 | 789 | igraph_integer_t start_of_path = 0; |
600 | | |
601 | 789 | if (igraph_is_directed(graph)) { |
602 | 381 | IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); |
603 | | |
604 | 381 | if (!has_cycle) { |
605 | 0 | IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_ENOSOL); |
606 | 0 | } |
607 | | |
608 | 381 | IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); |
609 | 408 | } else { |
610 | 408 | IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); |
611 | | |
612 | 408 | if (!has_cycle) { |
613 | 0 | IGRAPH_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_ENOSOL); |
614 | 0 | } |
615 | | |
616 | 408 | IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); |
617 | 408 | } |
618 | | |
619 | 789 | return IGRAPH_SUCCESS; |
620 | 789 | } |
621 | | |
622 | | |
623 | | /** |
624 | | * \ingroup Eulerian |
625 | | * \function igraph_eulerian_path |
626 | | * \brief Finds an Eulerian path. |
627 | | * |
628 | | * Finds an Eulerian path, if it exists. An Eulerian path traverses |
629 | | * each edge precisely once. |
630 | | * |
631 | | * </para><para> |
632 | | * If the graph has no edges, a zero-length path is returned. |
633 | | * |
634 | | * </para><para> |
635 | | * This function uses Hierholzer's algorithm. |
636 | | * |
637 | | * \param graph The graph object. |
638 | | * \param edge_res Pointer to an initialised vector. The indices of edges |
639 | | * belonging to the path will be stored here. May be \c NULL |
640 | | * if it is not needed by the caller. |
641 | | * \param vertex_res Pointer to an initialised vector. The indices of vertices |
642 | | * belonging to the path will be stored here. May be \c NULL |
643 | | * if it is not needed by the caller. |
644 | | * \return Error code: |
645 | | * \clist |
646 | | * \cli IGRAPH_ENOMEM |
647 | | * not enough memory for temporary data. |
648 | | * \cli IGRAPH_ENOSOL |
649 | | * graph does not have an Eulerian path. |
650 | | * \endclist |
651 | | * |
652 | | * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. |
653 | | * |
654 | | */ |
655 | | |
656 | | igraph_error_t igraph_eulerian_path( |
657 | 1.52k | const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { |
658 | | |
659 | 1.52k | igraph_bool_t has_cycle; |
660 | 1.52k | igraph_bool_t has_path; |
661 | 1.52k | igraph_integer_t start_of_path = 0; |
662 | | |
663 | 1.52k | if (igraph_is_directed(graph)) { |
664 | 680 | IGRAPH_CHECK(igraph_i_is_eulerian_directed(graph, &has_path, &has_cycle, &start_of_path)); |
665 | | |
666 | 680 | if (!has_path) { |
667 | 0 | IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_ENOSOL); |
668 | 0 | } |
669 | 680 | IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); |
670 | 842 | } else { |
671 | 842 | IGRAPH_CHECK(igraph_i_is_eulerian_undirected(graph, &has_path, &has_cycle, &start_of_path)); |
672 | | |
673 | 842 | if (!has_path) { |
674 | 0 | IGRAPH_ERROR("The graph does not have an Eulerian path.", IGRAPH_ENOSOL); |
675 | 0 | } |
676 | | |
677 | 842 | IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); |
678 | 842 | } |
679 | | |
680 | 1.52k | return IGRAPH_SUCCESS; |
681 | 1.52k | } |