1use std::collections::VecDeque;
11
12use crate::core::{Graph, IgraphResult, VertexId};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct DfsTree {
18 pub order: Vec<VertexId>,
20 pub order_out: Vec<VertexId>,
22 pub parents: Vec<Option<VertexId>>,
25 pub dist: Vec<Option<u32>>,
28}
29
30pub fn dfs_tree(graph: &Graph, root: VertexId) -> IgraphResult<DfsTree> {
63 graph.neighbors_iter(root)?;
64
65 let n = graph.vcount();
66 let n_us = n as usize;
67 let mut visited = vec![false; n_us];
68 let mut order: Vec<VertexId> = Vec::with_capacity(n_us);
69 let mut order_out: Vec<VertexId> = Vec::with_capacity(n_us);
70 let mut parents: Vec<Option<VertexId>> = vec![None; n_us];
71 let mut dist: Vec<Option<u32>> = vec![None; n_us];
72
73 let mut stack: VecDeque<(VertexId, usize, Vec<VertexId>)> = VecDeque::new();
74
75 visited[root as usize] = true;
76 order.push(root);
77 dist[root as usize] = Some(0);
78 let mut root_neis: Vec<VertexId> = graph.neighbors_iter(root)?.collect();
79 root_neis.reverse();
80 stack.push_back((root, 0, root_neis));
81
82 while let Some(&(cur, cursor, ref neis)) = stack.back() {
83 let mut next_cursor = cursor;
84 let mut found: Option<VertexId> = None;
85 while next_cursor < neis.len() {
86 let nei = neis[next_cursor];
87 next_cursor += 1;
88 if !visited[nei as usize] {
89 found = Some(nei);
90 break;
91 }
92 }
93
94 if let Some(nei) = found {
95 let last = stack.len() - 1;
96 stack[last].1 = next_cursor;
97 visited[nei as usize] = true;
98 order.push(nei);
99 parents[nei as usize] = Some(cur);
100 #[allow(clippy::cast_possible_truncation)]
101 let depth = (stack.len()) as u32; dist[nei as usize] = Some(depth);
103 let mut nei_neis: Vec<VertexId> = graph.neighbors_iter(nei)?.collect();
104 nei_neis.reverse();
105 stack.push_back((nei, 0, nei_neis));
106 } else {
107 order_out.push(cur);
108 stack.pop_back();
109 }
110 }
111
112 Ok(DfsTree {
113 order,
114 order_out,
115 parents,
116 dist,
117 })
118}
119
120pub fn dfs(graph: &Graph, root: VertexId) -> IgraphResult<Vec<VertexId>> {
145 graph.neighbors_iter(root)?;
147
148 let n = graph.vcount();
149 let mut visited = vec![false; n as usize];
150 let mut order: Vec<VertexId> = Vec::with_capacity(n as usize);
151
152 let mut stack: VecDeque<(VertexId, usize, Vec<VertexId>)> = VecDeque::new();
156
157 visited[root as usize] = true;
158 order.push(root);
159 let mut root_neis: Vec<VertexId> = graph.neighbors_iter(root)?.collect();
165 root_neis.reverse();
166 stack.push_back((root, 0, root_neis));
167
168 while let Some(&(actvect, cursor, ref neis)) = stack.back() {
169 let mut next_cursor = cursor;
172 let mut found: Option<VertexId> = None;
173 while next_cursor < neis.len() {
174 let nei = neis[next_cursor];
175 next_cursor += 1;
176 if !visited[nei as usize] {
177 found = Some(nei);
178 break;
179 }
180 }
181
182 if let Some(nei) = found {
183 let last = stack.len() - 1;
185 stack[last].1 = next_cursor;
186 visited[nei as usize] = true;
187 order.push(nei);
188 let mut nei_neis: Vec<VertexId> = graph.neighbors_iter(nei)?.collect();
189 nei_neis.reverse();
190 stack.push_back((nei, 0, nei_neis));
191 } else {
192 let _ = actvect;
194 stack.pop_back();
195 }
196 }
197
198 Ok(order)
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum DfsMode {
204 Out,
206 In,
208 All,
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct DfsSimple {
215 pub order: Vec<VertexId>,
217 pub order_out: Vec<VertexId>,
219 pub parents: Vec<Option<VertexId>>,
222 pub dist: Vec<Option<u32>>,
225}
226
227pub fn dfs_simple(graph: &Graph, root: VertexId, mode: DfsMode) -> IgraphResult<DfsSimple> {
253 graph.neighbors_iter(root)?; let n = graph.vcount();
256 let n_us = n as usize;
257 let use_mode = if graph.is_directed() {
258 mode
259 } else {
260 DfsMode::All
261 };
262
263 let mut visited = vec![false; n_us];
264 let mut order: Vec<VertexId> = Vec::with_capacity(n_us);
265 let mut order_out: Vec<VertexId> = Vec::with_capacity(n_us);
266 let mut parents: Vec<Option<VertexId>> = vec![None; n_us];
267 let mut dist: Vec<Option<u32>> = vec![None; n_us];
268
269 let neighbors = |v: VertexId| -> IgraphResult<Vec<VertexId>> {
270 match use_mode {
271 DfsMode::Out => graph.out_neighbors_vec(v),
272 DfsMode::In => graph.in_neighbors_vec(v),
273 DfsMode::All => {
274 if graph.is_directed() {
275 let mut combined = graph.out_neighbors_vec(v)?;
276 combined.extend(graph.in_neighbors_vec(v)?);
277 Ok(combined)
278 } else {
279 Ok(graph.neighbors_iter(v)?.collect())
280 }
281 }
282 }
283 };
284
285 let mut stack: VecDeque<(VertexId, usize, Vec<VertexId>)> = VecDeque::new();
286
287 visited[root as usize] = true;
288 order.push(root);
289 dist[root as usize] = Some(0);
290 let mut root_neis = neighbors(root)?;
291 root_neis.reverse();
292 stack.push_back((root, 0, root_neis));
293
294 while let Some(&(cur, cursor, ref neis)) = stack.back() {
295 let mut next_cursor = cursor;
296 let mut found: Option<VertexId> = None;
297 while next_cursor < neis.len() {
298 let nei = neis[next_cursor];
299 next_cursor += 1;
300 if !visited[nei as usize] {
301 found = Some(nei);
302 break;
303 }
304 }
305
306 if let Some(nei) = found {
307 let last = stack.len() - 1;
308 stack[last].1 = next_cursor;
309 visited[nei as usize] = true;
310 order.push(nei);
311 parents[nei as usize] = Some(cur);
312 #[allow(clippy::cast_possible_truncation)]
313 let depth = stack.len() as u32;
314 dist[nei as usize] = Some(depth);
315 let mut nei_neis = neighbors(nei)?;
316 nei_neis.reverse();
317 stack.push_back((nei, 0, nei_neis));
318 } else {
319 order_out.push(cur);
320 stack.pop_back();
321 }
322 }
323
324 Ok(DfsSimple {
325 order,
326 order_out,
327 parents,
328 dist,
329 })
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 fn path_graph(n: u32) -> Graph {
337 let mut g = Graph::with_vertices(n);
338 for i in 0..n.saturating_sub(1) {
339 g.add_edge(i, i + 1).unwrap();
340 }
341 g
342 }
343
344 #[test]
345 fn empty_graph_errors_on_any_root() {
346 let g = Graph::with_vertices(0);
347 let err = dfs(&g, 0).unwrap_err();
348 assert!(matches!(
349 err,
350 crate::core::IgraphError::VertexOutOfRange { id: 0, n: 0 }
351 ));
352 }
353
354 #[test]
355 fn single_vertex_visits_self() {
356 let g = Graph::with_vertices(1);
357 assert_eq!(dfs(&g, 0).unwrap(), vec![0]);
358 }
359
360 #[test]
361 fn path_visits_in_order() {
362 let g = path_graph(5);
363 assert_eq!(dfs(&g, 0).unwrap(), vec![0, 1, 2, 3, 4]);
365 }
366
367 #[test]
368 fn dfs_goes_deep_before_wide() {
369 let mut g = Graph::with_vertices(4);
376 g.add_edge(0, 1).unwrap();
377 g.add_edge(0, 2).unwrap();
378 g.add_edge(1, 3).unwrap();
379 let order = dfs(&g, 0).unwrap();
380 assert_eq!(order[0], 0);
381 assert_eq!(order.len(), 4);
382 let pos = |v: u32| order.iter().position(|&x| x == v).unwrap();
383 let p1 = pos(1);
388 let p3 = pos(3);
389 assert!(p3 == p1 + 1, "3 should follow 1 directly in DFS pre-order");
390 }
391
392 #[test]
393 fn unreachable_vertex_excluded() {
394 let mut g = Graph::with_vertices(3);
395 g.add_edge(0, 1).unwrap();
396 let order = dfs(&g, 0).unwrap();
398 assert_eq!(order.len(), 2);
399 assert!(order.contains(&0));
400 assert!(order.contains(&1));
401 }
402
403 #[test]
404 fn invalid_root_errors() {
405 let g = Graph::with_vertices(2);
406 let err = dfs(&g, 5).unwrap_err();
407 match err {
408 crate::core::IgraphError::VertexOutOfRange { id, n } => {
409 assert_eq!(id, 5);
410 assert_eq!(n, 2);
411 }
412 other => panic!("unexpected error: {other:?}"),
413 }
414 }
415
416 #[test]
417 fn complete_k4_visits_all_four() {
418 let mut g = Graph::with_vertices(4);
419 for u in 0..4u32 {
420 for v in (u + 1)..4 {
421 g.add_edge(u, v).unwrap();
422 }
423 }
424 let order = dfs(&g, 0).unwrap();
425 let mut sorted = order.clone();
426 sorted.sort_unstable();
427 assert_eq!(sorted, vec![0, 1, 2, 3]);
428 assert_eq!(order[0], 0);
429 }
430
431 #[test]
434 fn dfs_tree_singleton() {
435 let g = Graph::with_vertices(1);
436 let r = dfs_tree(&g, 0).unwrap();
437 assert_eq!(r.order, vec![0]);
438 assert_eq!(r.order_out, vec![0]);
439 assert_eq!(r.parents, vec![None]);
440 assert_eq!(r.dist, vec![Some(0)]);
441 }
442
443 #[test]
444 fn dfs_tree_path() {
445 let g = path_graph(4);
446 let r = dfs_tree(&g, 0).unwrap();
447 assert_eq!(r.order, vec![0, 1, 2, 3]);
448 assert_eq!(r.order_out, vec![3, 2, 1, 0]);
449 assert_eq!(r.parents, vec![None, Some(0), Some(1), Some(2)]);
450 assert_eq!(r.dist, vec![Some(0), Some(1), Some(2), Some(3)]);
451 }
452
453 #[test]
454 fn dfs_tree_branching() {
455 let mut g = Graph::with_vertices(4);
457 g.add_edge(0, 1).unwrap();
458 g.add_edge(0, 2).unwrap();
459 g.add_edge(1, 3).unwrap();
460 let r = dfs_tree(&g, 0).unwrap();
461 assert_eq!(r.order[0], 0);
462 assert_eq!(r.parents[0], None);
463 assert_eq!(r.dist[0], Some(0));
464 assert_eq!(r.parents[3], Some(1));
466 assert_eq!(r.dist[3], Some(2));
467 assert_eq!(*r.order_out.last().unwrap(), 0);
469 }
470
471 #[test]
472 fn dfs_tree_unreachable() {
473 let mut g = Graph::with_vertices(3);
474 g.add_edge(0, 1).unwrap();
475 let r = dfs_tree(&g, 0).unwrap();
476 assert_eq!(r.order.len(), 2);
477 assert_eq!(r.order_out.len(), 2);
478 assert_eq!(r.parents[2], None);
479 assert_eq!(r.dist[2], None);
480 }
481
482 #[test]
483 fn dfs_tree_order_out_reverse_invariant() {
484 let mut g = Graph::with_vertices(5);
488 g.add_edge(0, 1).unwrap();
489 g.add_edge(0, 2).unwrap();
490 g.add_edge(1, 3).unwrap();
491 g.add_edge(1, 4).unwrap();
492 let r = dfs_tree(&g, 0).unwrap();
493 let finish_pos = |v: u32| r.order_out.iter().position(|&x| x == v).unwrap();
494 assert!(finish_pos(0) > finish_pos(1));
496 assert!(finish_pos(0) > finish_pos(2));
497 assert!(finish_pos(1) > finish_pos(3));
499 assert!(finish_pos(1) > finish_pos(4));
500 }
501
502 #[test]
503 fn dfs_tree_invalid_root() {
504 let g = Graph::with_vertices(2);
505 assert!(dfs_tree(&g, 5).is_err());
506 }
507
508 #[test]
511 fn dfs_simple_undirected_tree() {
512 let mut g = Graph::with_vertices(4);
513 g.add_edge(0, 1).unwrap();
514 g.add_edge(0, 2).unwrap();
515 g.add_edge(1, 3).unwrap();
516
517 let r = dfs_simple(&g, 0, DfsMode::Out).unwrap();
518 assert_eq!(r.order[0], 0);
519 assert_eq!(r.order.len(), 4);
520 assert_eq!(r.parents[0], None);
521 assert_eq!(r.dist[0], Some(0));
522 assert_eq!(*r.order_out.last().unwrap(), 0);
523 }
524
525 #[test]
526 fn dfs_simple_single_vertex() {
527 let g = Graph::with_vertices(1);
528 let r = dfs_simple(&g, 0, DfsMode::All).unwrap();
529 assert_eq!(r.order, vec![0]);
530 assert_eq!(r.order_out, vec![0]);
531 assert_eq!(r.dist, vec![Some(0)]);
532 }
533
534 #[test]
535 fn dfs_simple_invalid_root() {
536 let g = Graph::with_vertices(2);
537 assert!(dfs_simple(&g, 5, DfsMode::Out).is_err());
538 }
539
540 #[test]
541 fn dfs_simple_directed_out() {
542 let mut g = Graph::new(4, true).unwrap();
544 g.add_edge(0, 1).unwrap();
545 g.add_edge(1, 2).unwrap();
546 g.add_edge(0, 3).unwrap();
547
548 let r = dfs_simple(&g, 0, DfsMode::Out).unwrap();
549 assert_eq!(r.order[0], 0);
550 assert_eq!(r.order.len(), 4);
551 assert_eq!(r.parents[1], Some(0));
552 assert_eq!(r.parents[2], Some(1));
553 assert_eq!(r.dist[2], Some(2));
554 }
555
556 #[test]
557 fn dfs_simple_directed_in() {
558 let mut g = Graph::new(4, true).unwrap();
560 g.add_edge(1, 0).unwrap();
561 g.add_edge(2, 1).unwrap();
562 g.add_edge(3, 0).unwrap();
563
564 let r = dfs_simple(&g, 0, DfsMode::In).unwrap();
565 assert_eq!(r.order[0], 0);
566 assert_eq!(r.order.len(), 4);
567 assert_eq!(r.parents[1], Some(0));
568 assert_eq!(r.parents[2], Some(1));
569 assert_eq!(r.parents[3], Some(0));
570 }
571
572 #[test]
573 fn dfs_simple_directed_all() {
574 let mut g = Graph::new(3, true).unwrap();
576 g.add_edge(0, 1).unwrap();
577 g.add_edge(2, 0).unwrap();
578
579 let r = dfs_simple(&g, 0, DfsMode::All).unwrap();
580 assert_eq!(r.order.len(), 3);
581 assert_eq!(r.order[0], 0);
582 }
583
584 #[test]
585 fn dfs_simple_unreachable() {
586 let mut g = Graph::with_vertices(4);
587 g.add_edge(0, 1).unwrap();
588 let r = dfs_simple(&g, 0, DfsMode::All).unwrap();
589 assert_eq!(r.order.len(), 2);
590 assert_eq!(r.order_out.len(), 2);
591 assert_eq!(r.parents[2], None);
592 assert_eq!(r.dist[2], None);
593 }
594
595 #[test]
596 fn dfs_simple_mode_ignored_for_undirected() {
597 let mut g = Graph::with_vertices(3);
598 g.add_edge(0, 1).unwrap();
599 g.add_edge(1, 2).unwrap();
600 let r = dfs_simple(&g, 0, DfsMode::In).unwrap();
601 assert_eq!(r.order.len(), 3);
602 }
603
604 #[test]
605 fn dfs_simple_directed_out_no_reach() {
606 let mut g = Graph::new(3, true).unwrap();
608 g.add_edge(0, 1).unwrap();
609 g.add_edge(2, 0).unwrap();
610 let r = dfs_simple(&g, 0, DfsMode::Out).unwrap();
611 assert_eq!(r.order.len(), 2);
612 assert!(r.order.contains(&0));
613 assert!(r.order.contains(&1));
614 assert_eq!(r.dist[2], None);
615 }
616}