1use crate::core::{Graph, IgraphResult};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SimplePathMode {
11 Out,
13 In,
15 All,
17}
18
19pub fn all_simple_paths(
51 graph: &Graph,
52 from: u32,
53 to: Option<&[u32]>,
54 mode: SimplePathMode,
55 min_len: i32,
56 max_len: i32,
57 max_results: i64,
58) -> IgraphResult<Vec<Vec<u32>>> {
59 let vcount = graph.vcount();
60 if from >= vcount {
61 return Err(crate::core::IgraphError::InvalidArgument(
62 "source vertex out of range".to_string(),
63 ));
64 }
65
66 let mut result: Vec<Vec<u32>> = Vec::new();
67
68 if max_results == 0 {
69 return Ok(result);
70 }
71
72 let target_set: Option<Vec<bool>> = to.map(|targets| {
73 let mut set = vec![false; vcount as usize];
74 for &t in targets {
75 if (t as usize) < set.len() {
76 set[t as usize] = true;
77 }
78 }
79 set
80 });
81
82 let adj = build_adjlist(graph, mode)?;
83
84 let mut stack: Vec<u32> = vec![from];
85 let mut dist: Vec<i32> = vec![0];
86 let mut added = vec![false; vcount as usize];
87 let mut nptr = vec![0usize; vcount as usize];
88 added[from as usize] = true;
89
90 while let Some(&act) = stack.last() {
91 let cur_dist = *dist.last().unwrap_or(&0);
92 let neis = &adj[act as usize];
93 let n = neis.len();
94 let ptr = &mut nptr[act as usize];
95
96 let within_dist = max_len < 0 || cur_dist < max_len;
97 let mut found_nei = false;
98 let mut nei = 0u32;
99
100 if within_dist {
101 while !found_nei && *ptr < n {
102 nei = neis[*ptr];
103 found_nei = !added[nei as usize];
104 *ptr += 1;
105 }
106 }
107
108 if within_dist && found_nei {
109 stack.push(nei);
110 dist.push(cur_dist + 1);
111 added[nei as usize] = true;
112
113 let is_target = match &target_set {
114 None => true,
115 Some(set) => set[nei as usize],
116 };
117 if is_target && (min_len <= 0 || cur_dist + 1 >= min_len) {
118 result.push(stack.clone());
119 if max_results >= 0
120 && i64::try_from(result.len()).unwrap_or(i64::MAX) >= max_results
121 {
122 break;
123 }
124 }
125 } else {
126 let up = stack.pop().unwrap_or(0);
127 dist.pop();
128 added[up as usize] = false;
129 nptr[up as usize] = 0;
130 }
131 }
132
133 Ok(result)
134}
135
136fn build_adjlist(graph: &Graph, mode: SimplePathMode) -> IgraphResult<Vec<Vec<u32>>> {
137 let n = graph.vcount();
138 let mut adj = Vec::with_capacity(n as usize);
139
140 for v in 0..n {
141 let raw = match mode {
142 SimplePathMode::Out => {
143 if graph.is_directed() {
144 graph.out_neighbors_vec(v)?
145 } else {
146 graph.neighbors(v)?
147 }
148 }
149 SimplePathMode::In => {
150 if graph.is_directed() {
151 graph.in_neighbors_vec(v)?
152 } else {
153 graph.neighbors(v)?
154 }
155 }
156 SimplePathMode::All => {
157 if graph.is_directed() {
158 let mut all = graph.out_neighbors_vec(v)?;
159 all.extend(graph.in_neighbors_vec(v)?);
160 all
161 } else {
162 graph.neighbors(v)?
163 }
164 }
165 };
166
167 let mut deduped: Vec<u32> = Vec::with_capacity(raw.len());
168 for &nei in &raw {
169 if nei != v && !deduped.contains(&nei) {
170 deduped.push(nei);
171 }
172 }
173 adj.push(deduped);
174 }
175
176 Ok(adj)
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::create;
183
184 #[test]
185 fn empty_graph() {
186 let g = Graph::with_vertices(0);
187 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1);
188 assert!(r.is_err());
189 }
190
191 #[test]
192 fn single_vertex() {
193 let g = Graph::with_vertices(1);
194 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
195 assert!(r.is_empty());
196 }
197
198 #[test]
199 fn single_edge() {
200 let g = create(&[(0, 1)], 2, false).expect("ok");
201 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
202 assert_eq!(r.len(), 1);
203 assert_eq!(r[0], vec![0, 1]);
204 }
205
206 #[test]
207 fn path_3() {
208 let g = create(&[(0, 1), (1, 2)], 3, false).expect("ok");
209 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
210 assert_eq!(r.len(), 2);
211 assert_eq!(r[0], vec![0, 1]);
212 assert_eq!(r[1], vec![0, 1, 2]);
213 }
214
215 #[test]
216 fn triangle_from_0() {
217 let g = create(&[(0, 1), (1, 2), (0, 2)], 3, false).expect("ok");
218 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
219 assert_eq!(r.len(), 4);
221 }
222
223 #[test]
224 fn target_filter() {
225 let g = create(&[(0, 1), (1, 2), (2, 3)], 4, false).expect("ok");
226 let r = all_simple_paths(&g, 0, Some(&[3]), SimplePathMode::Out, 0, -1, -1).expect("ok");
227 assert_eq!(r.len(), 1);
228 assert_eq!(r[0], vec![0, 1, 2, 3]);
229 }
230
231 #[test]
232 fn max_len_filter() {
233 let g = create(&[(0, 1), (1, 2), (2, 3)], 4, false).expect("ok");
234 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, 2, -1).expect("ok");
235 assert_eq!(r.len(), 2);
237 }
238
239 #[test]
240 fn min_len_filter() {
241 let g = create(&[(0, 1), (1, 2), (2, 3)], 4, false).expect("ok");
242 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 2, -1, -1).expect("ok");
243 assert_eq!(r.len(), 2);
245 }
246
247 #[test]
248 fn max_results() {
249 let g = create(&[(0, 1), (1, 2), (2, 3)], 4, false).expect("ok");
250 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, 2).expect("ok");
251 assert_eq!(r.len(), 2);
252 }
253
254 #[test]
255 fn directed_out() {
256 let g = create(&[(0, 1), (1, 2), (2, 0)], 3, true).expect("ok");
257 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
258 assert_eq!(r.len(), 2);
260 assert_eq!(r[0], vec![0, 1]);
261 assert_eq!(r[1], vec![0, 1, 2]);
262 }
263
264 #[test]
265 fn directed_in() {
266 let g = create(&[(0, 1), (1, 2), (2, 0)], 3, true).expect("ok");
267 let r = all_simple_paths(&g, 0, None, SimplePathMode::In, 0, -1, -1).expect("ok");
268 assert_eq!(r.len(), 2);
270 assert_eq!(r[0], vec![0, 2]);
271 assert_eq!(r[1], vec![0, 2, 1]);
272 }
273
274 #[test]
275 fn directed_all() {
276 let g = create(&[(0, 1), (1, 2)], 3, true).expect("ok");
277 let r = all_simple_paths(&g, 1, None, SimplePathMode::All, 0, -1, -1).expect("ok");
278 assert_eq!(r.len(), 2);
281 }
282
283 #[test]
284 fn self_loop_ignored() {
285 let g = create(&[(0, 0), (0, 1)], 2, false).expect("ok");
286 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
287 assert_eq!(r.len(), 1);
288 assert_eq!(r[0], vec![0, 1]);
289 }
290
291 #[test]
292 fn multi_edge_ignored() {
293 let g = create(&[(0, 1), (0, 1), (1, 2)], 3, false).expect("ok");
294 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
295 assert_eq!(r.len(), 2);
296 assert_eq!(r[0], vec![0, 1]);
297 assert_eq!(r[1], vec![0, 1, 2]);
298 }
299
300 #[test]
301 fn disconnected() {
302 let g = create(&[(0, 1), (2, 3)], 4, false).expect("ok");
303 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
304 assert_eq!(r.len(), 1);
305 assert_eq!(r[0], vec![0, 1]);
306 }
307
308 #[test]
309 fn source_out_of_range() {
310 let g = Graph::with_vertices(3);
311 let r = all_simple_paths(&g, 5, None, SimplePathMode::Out, 0, -1, -1);
312 assert!(r.is_err());
313 }
314
315 #[test]
316 fn max_results_zero() {
317 let g = create(&[(0, 1)], 2, false).expect("ok");
318 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, 0).expect("ok");
319 assert!(r.is_empty());
320 }
321
322 #[test]
323 fn k4_complete() {
324 let g = create(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false).expect("ok");
325 let r = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, -1).expect("ok");
326 assert!(r.len() >= 9, "got {} paths", r.len());
334 }
335}
336
337#[cfg(all(test, feature = "proptest-harness"))]
338mod proptests {
339 use super::*;
340 use crate::create;
341 use proptest::prelude::*;
342
343 fn arb_graph(max_v: u32) -> impl Strategy<Value = Graph> {
344 (2..=max_v).prop_flat_map(|n| {
345 let max_e = (n as usize)
346 .saturating_mul(n.saturating_sub(1) as usize)
347 .min(20);
348 proptest::collection::vec((0..n, 0..n), 0..=max_e).prop_map(move |edges| {
349 let edge_tuples: Vec<(u32, u32)> = edges.into_iter().collect();
350 create(&edge_tuples, n, false).expect("arb graph")
351 })
352 })
353 }
354
355 proptest! {
356 #[test]
357 fn all_paths_are_simple(g in arb_graph(6)) {
358 let n = g.vcount();
359 if n > 0 {
360 let paths = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, 100)
361 .expect("ok");
362 for path in &paths {
363 let mut seen = vec![false; n as usize];
364 for &v in path {
365 prop_assert!(
366 !seen[v as usize],
367 "vertex {v} repeated in path {:?}",
368 path
369 );
370 seen[v as usize] = true;
371 }
372 }
373 }
374 }
375
376 #[test]
377 fn all_paths_start_with_source(g in arb_graph(6)) {
378 let n = g.vcount();
379 if n > 0 {
380 let paths = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, 100)
381 .expect("ok");
382 for path in &paths {
383 prop_assert_eq!(
384 path[0], 0,
385 "path {:?} does not start with source 0",
386 path
387 );
388 }
389 }
390 }
391
392 #[test]
393 fn path_edges_exist(g in arb_graph(6)) {
394 let n = g.vcount();
395 if n > 0 {
396 let paths = all_simple_paths(&g, 0, None, SimplePathMode::Out, 0, -1, 100)
397 .expect("ok");
398 for path in &paths {
399 for w in path.windows(2) {
400 let neis = g.neighbors(w[0]).expect("ok");
401 prop_assert!(
402 neis.contains(&w[1]),
403 "edge {}->{} not in graph but in path {:?}",
404 w[0], w[1], path
405 );
406 }
407 }
408 }
409 }
410 }
411}