1use crate::core::graph::EdgeId;
16use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum SimpleCycleMode {
21 Out,
23 In,
25 All,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct SimpleCycle {
32 pub vertices: Vec<VertexId>,
34 pub edges: Vec<EdgeId>,
36}
37
38pub fn simple_cycles(
68 graph: &Graph,
69 mode: SimpleCycleMode,
70 min_length: u32,
71 max_length: Option<u32>,
72 max_results: Option<usize>,
73) -> IgraphResult<Vec<SimpleCycle>> {
74 if min_length < 1 {
75 return Err(IgraphError::InvalidArgument(
76 "simple_cycles: min_length must be >= 1".to_string(),
77 ));
78 }
79
80 let n = graph.vcount();
81 if n == 0 {
82 return Ok(Vec::new());
83 }
84
85 let max_len = max_length.unwrap_or(n);
86 if max_len == 0 {
87 return Ok(Vec::new());
88 }
89
90 let directed = graph.is_directed() && mode != SimpleCycleMode::All;
91
92 let (adj, inc) = build_adj_inc(graph, mode)?;
94
95 let n_usize = n as usize;
97 let mut adj_work: Vec<Vec<VertexId>> = adj.clone();
98 let mut inc_work: Vec<Vec<EdgeId>> = inc.clone();
99
100 let mut blocked: Vec<bool> = vec![false; n_usize];
101 let mut b_lists: Vec<Vec<VertexId>> = vec![Vec::new(); n_usize];
102
103 let mut vertex_stack: Vec<VertexId> = Vec::new();
104 let mut edge_stack: Vec<EdgeId> = Vec::new();
105
106 let mut results: Vec<SimpleCycle> = Vec::new();
107 let mut stop = false;
108
109 for s in 0..n {
110 if stop {
111 break;
112 }
113
114 for i in (s as usize)..n_usize {
116 blocked[i] = false;
117 b_lists[i].clear();
118 }
119
120 if adj_work[s as usize].is_empty() {
123 continue;
124 }
125
126 johnson_circuit(
128 &adj_work,
129 &inc_work,
130 s,
131 s,
132 directed,
133 min_length,
134 max_len,
135 max_results,
136 &mut blocked,
137 &mut b_lists,
138 &mut vertex_stack,
139 &mut edge_stack,
140 &mut results,
141 &mut stop,
142 );
143
144 for i in 0..n_usize {
146 if let Some(pos) = adj_work[i].iter().position(|&v| v == s) {
147 adj_work[i].remove(pos);
148 inc_work[i].remove(pos);
149 }
150 }
151 adj_work[s as usize].clear();
152 inc_work[s as usize].clear();
153 }
154
155 Ok(results)
156}
157
158type AdjInc = (Vec<Vec<VertexId>>, Vec<Vec<EdgeId>>);
160
161fn build_adj_inc(graph: &Graph, mode: SimpleCycleMode) -> IgraphResult<AdjInc> {
163 let n = graph.vcount() as usize;
164 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
165 let mut inc: Vec<Vec<EdgeId>> = vec![Vec::new(); n];
166
167 let m = graph.ecount();
168 for eid in 0..m {
169 let eid32 = u32::try_from(eid)
170 .map_err(|_| IgraphError::InvalidArgument("edge count exceeds u32".to_string()))?;
171 let (from, to) = graph.edge(eid32)?;
172
173 if graph.is_directed() {
174 match mode {
175 SimpleCycleMode::Out => {
176 adj[from as usize].push(to);
177 inc[from as usize].push(eid32);
178 }
179 SimpleCycleMode::In => {
180 adj[to as usize].push(from);
181 inc[to as usize].push(eid32);
182 }
183 SimpleCycleMode::All => {
184 adj[from as usize].push(to);
185 inc[from as usize].push(eid32);
186 adj[to as usize].push(from);
187 inc[to as usize].push(eid32);
188 }
189 }
190 } else {
191 adj[from as usize].push(to);
192 inc[from as usize].push(eid32);
193 adj[to as usize].push(from);
194 inc[to as usize].push(eid32);
195 }
196 }
197
198 for v in 0..n {
200 let mut pairs: Vec<(VertexId, EdgeId)> =
201 adj[v].iter().copied().zip(inc[v].iter().copied()).collect();
202 pairs.sort_unstable();
203 adj[v] = pairs.iter().map(|&(a, _)| a).collect();
204 inc[v] = pairs.iter().map(|&(_, e)| e).collect();
205 }
206
207 Ok((adj, inc))
208}
209
210#[allow(clippy::too_many_arguments)]
212fn johnson_circuit(
213 adj: &[Vec<VertexId>],
214 inc: &[Vec<EdgeId>],
215 s: VertexId,
216 start_v: VertexId,
217 directed: bool,
218 min_length: u32,
219 max_length: u32,
220 max_results: Option<usize>,
221 blocked: &mut [bool],
222 b_lists: &mut [Vec<VertexId>],
223 vertex_stack: &mut Vec<VertexId>,
224 edge_stack: &mut Vec<EdgeId>,
225 results: &mut Vec<SimpleCycle>,
226 stop: &mut bool,
227) {
228 let mut frame_v: Vec<VertexId> = Vec::new();
232 let mut frame_idx: Vec<usize> = Vec::new();
233 let mut frame_found: Vec<bool> = Vec::new();
234 let mut frame_length_stop: Vec<bool> = Vec::new();
235
236 vertex_stack.push(start_v);
238 blocked[start_v as usize] = true;
239 frame_v.push(start_v);
240 frame_idx.push(0);
241 frame_found.push(false);
242 frame_length_stop.push(false);
243
244 loop {
245 if *stop || frame_v.is_empty() {
246 break;
247 }
248
249 let depth = frame_v.len() - 1;
250 let v = frame_v[depth];
251 let v_idx = v as usize;
252 let neighbors = &adj[v_idx];
253 let incidents = &inc[v_idx];
254
255 let mut advanced = false;
256
257 while frame_idx[depth] < neighbors.len() {
258 let i = frame_idx[depth];
259 frame_idx[depth] += 1;
260
261 let w = neighbors[i];
262 let we = incidents[i];
263
264 if w == s {
265 frame_found[depth] = true;
266
267 if !directed {
269 if edge_stack.len() == 1 && edge_stack[0] == we {
270 continue;
271 }
272 if !edge_stack.is_empty() && edge_stack[0] > we {
273 continue;
274 }
275 }
276
277 let cycle_len = vertex_stack.len();
278 let min_l = min_length as usize;
279 let max_l = max_length as usize;
280 if cycle_len >= min_l && cycle_len <= max_l {
281 let mut cycle_edges = edge_stack.clone();
282 cycle_edges.push(we);
283 results.push(SimpleCycle {
284 vertices: vertex_stack.clone(),
285 edges: cycle_edges,
286 });
287
288 if let Some(max) = max_results {
289 if results.len() >= max {
290 *stop = true;
291 break;
292 }
293 }
294 }
295 } else if !blocked[w as usize] {
296 let can_recurse = vertex_stack.len() < max_length as usize;
297 if can_recurse {
298 vertex_stack.push(w);
299 edge_stack.push(we);
300 blocked[w as usize] = true;
301
302 frame_v.push(w);
303 frame_idx.push(0);
304 frame_found.push(false);
305 frame_length_stop.push(false);
306 advanced = true;
307 break;
308 }
309 frame_length_stop[depth] = true;
310 }
311 }
312
313 if *stop {
314 break;
315 }
316
317 if !advanced {
318 let found = frame_found[depth];
319 let length_stop = frame_length_stop[depth];
320 let cur_v = frame_v[depth];
321
322 frame_v.pop();
323 frame_idx.pop();
324 frame_found.pop();
325 frame_length_stop.pop();
326
327 if found || length_stop {
328 unblock(cur_v, blocked, b_lists);
329 } else {
330 for &w in &adj[cur_v as usize] {
331 let w_b = &mut b_lists[w as usize];
332 if !w_b.contains(&cur_v) {
333 w_b.push(cur_v);
334 }
335 }
336 }
337
338 vertex_stack.pop();
339 edge_stack.pop();
340
341 if !frame_v.is_empty() && (found || length_stop) {
343 let parent = frame_v.len() - 1;
344 frame_found[parent] = true;
345 }
346 }
347 }
348
349 vertex_stack.clear();
350 edge_stack.clear();
351}
352
353fn unblock(u: VertexId, blocked: &mut [bool], b_lists: &mut [Vec<VertexId>]) {
355 let mut stack: Vec<VertexId> = vec![u];
356
357 while let Some(v) = stack.pop() {
358 if blocked[v as usize] {
359 blocked[v as usize] = false;
360 while let Some(w) = b_lists[v as usize].pop() {
361 if blocked[w as usize] {
362 stack.push(w);
363 }
364 }
365 }
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn empty_graph() {
375 let g = Graph::with_vertices(0);
376 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
377 assert!(cycles.is_empty());
378 }
379
380 #[test]
381 fn no_edges() {
382 let g = Graph::with_vertices(5);
383 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
384 assert!(cycles.is_empty());
385 }
386
387 #[test]
388 fn directed_triangle() {
389 let mut g = Graph::new(3, true).unwrap();
390 g.add_edge(0, 1).unwrap();
391 g.add_edge(1, 2).unwrap();
392 g.add_edge(2, 0).unwrap();
393 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
394 assert_eq!(cycles.len(), 1);
395 assert_eq!(cycles[0].vertices, vec![0, 1, 2]);
396 assert_eq!(cycles[0].edges, vec![0, 1, 2]);
397 }
398
399 #[test]
400 fn directed_two_cycles() {
401 let mut g = Graph::new(3, true).unwrap();
403 g.add_edge(0, 1).unwrap(); g.add_edge(1, 0).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 0).unwrap(); let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
408 assert_eq!(cycles.len(), 2);
409 }
410
411 #[test]
412 fn undirected_triangle() {
413 let mut g = Graph::with_vertices(3);
414 g.add_edge(0, 1).unwrap();
415 g.add_edge(1, 2).unwrap();
416 g.add_edge(0, 2).unwrap();
417 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, None, None).unwrap();
418 assert_eq!(cycles.len(), 1);
420 assert_eq!(cycles[0].vertices.len(), 3);
421 }
422
423 #[test]
424 fn undirected_square() {
425 let mut g = Graph::with_vertices(4);
427 g.add_edge(0, 1).unwrap();
428 g.add_edge(1, 2).unwrap();
429 g.add_edge(2, 3).unwrap();
430 g.add_edge(3, 0).unwrap();
431 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, None, None).unwrap();
432 assert_eq!(cycles.len(), 1);
433 assert_eq!(cycles[0].vertices.len(), 4);
434 }
435
436 #[test]
437 fn undirected_complete_4() {
438 let mut g = Graph::with_vertices(4);
440 g.add_edge(0, 1).unwrap();
441 g.add_edge(0, 2).unwrap();
442 g.add_edge(0, 3).unwrap();
443 g.add_edge(1, 2).unwrap();
444 g.add_edge(1, 3).unwrap();
445 g.add_edge(2, 3).unwrap();
446 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, None, None).unwrap();
447 assert_eq!(cycles.len(), 7);
448 }
449
450 #[test]
451 fn min_length_filter() {
452 let mut g = Graph::with_vertices(4);
454 g.add_edge(0, 1).unwrap();
455 g.add_edge(0, 2).unwrap();
456 g.add_edge(0, 3).unwrap();
457 g.add_edge(1, 2).unwrap();
458 g.add_edge(1, 3).unwrap();
459 g.add_edge(2, 3).unwrap();
460 let cycles = simple_cycles(&g, SimpleCycleMode::All, 4, None, None).unwrap();
461 assert_eq!(cycles.len(), 3);
462 for c in &cycles {
463 assert_eq!(c.vertices.len(), 4);
464 }
465 }
466
467 #[test]
468 fn max_length_filter() {
469 let mut g = Graph::with_vertices(4);
471 g.add_edge(0, 1).unwrap();
472 g.add_edge(0, 2).unwrap();
473 g.add_edge(0, 3).unwrap();
474 g.add_edge(1, 2).unwrap();
475 g.add_edge(1, 3).unwrap();
476 g.add_edge(2, 3).unwrap();
477 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, Some(3), None).unwrap();
478 assert_eq!(cycles.len(), 4);
479 for c in &cycles {
480 assert_eq!(c.vertices.len(), 3);
481 }
482 }
483
484 #[test]
485 fn max_results() {
486 let mut g = Graph::with_vertices(4);
488 g.add_edge(0, 1).unwrap();
489 g.add_edge(0, 2).unwrap();
490 g.add_edge(0, 3).unwrap();
491 g.add_edge(1, 2).unwrap();
492 g.add_edge(1, 3).unwrap();
493 g.add_edge(2, 3).unwrap();
494 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, None, Some(3)).unwrap();
495 assert_eq!(cycles.len(), 3);
496 }
497
498 #[test]
499 fn self_loop_directed() {
500 let mut g = Graph::new(2, true).unwrap();
501 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
503 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
504 assert_eq!(cycles.len(), 1);
505 assert_eq!(cycles[0].vertices, vec![0]);
506 assert_eq!(cycles[0].edges, vec![0]);
507 }
508
509 #[test]
510 fn directed_in_mode() {
511 let mut g = Graph::new(3, true).unwrap();
513 g.add_edge(0, 1).unwrap();
514 g.add_edge(1, 2).unwrap();
515 g.add_edge(2, 0).unwrap();
516 let cycles = simple_cycles(&g, SimpleCycleMode::In, 1, None, None).unwrap();
517 assert_eq!(cycles.len(), 1);
518 assert_eq!(cycles[0].vertices.len(), 3);
520 }
521
522 #[test]
523 fn dag_no_cycles() {
524 let mut g = Graph::new(4, true).unwrap();
525 g.add_edge(0, 1).unwrap();
526 g.add_edge(0, 2).unwrap();
527 g.add_edge(1, 3).unwrap();
528 g.add_edge(2, 3).unwrap();
529 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
530 assert!(cycles.is_empty());
531 }
532
533 #[test]
534 fn tree_no_cycles() {
535 let mut g = Graph::with_vertices(5);
536 g.add_edge(0, 1).unwrap();
537 g.add_edge(0, 2).unwrap();
538 g.add_edge(1, 3).unwrap();
539 g.add_edge(1, 4).unwrap();
540 let cycles = simple_cycles(&g, SimpleCycleMode::All, 1, None, None).unwrap();
541 assert!(cycles.is_empty());
542 }
543
544 #[test]
545 fn directed_k3_all_edges() {
546 let mut g = Graph::new(3, true).unwrap();
554 g.add_edge(0, 1).unwrap();
555 g.add_edge(1, 0).unwrap();
556 g.add_edge(0, 2).unwrap();
557 g.add_edge(2, 0).unwrap();
558 g.add_edge(1, 2).unwrap();
559 g.add_edge(2, 1).unwrap();
560 let cycles = simple_cycles(&g, SimpleCycleMode::Out, 1, None, None).unwrap();
561 assert_eq!(cycles.len(), 5);
562 }
563}