1use alloc::vec;
2use core::{
3 hash::{BuildHasher, Hash},
4 iter::{from_fn, FromIterator},
5};
6
7use indexmap::IndexSet;
8
9use crate::{
10 visit::{IntoNeighborsDirected, NodeCount},
11 Direction::Outgoing,
12};
13
14pub fn all_simple_paths<TargetColl, G, S>(
56 graph: G,
57 from: G::NodeId,
58 to: G::NodeId,
59 min_intermediate_nodes: usize,
60 max_intermediate_nodes: Option<usize>,
61) -> impl Iterator<Item = TargetColl>
62where
63 G: NodeCount,
64 G: IntoNeighborsDirected,
65 G::NodeId: Eq + Hash,
66 TargetColl: FromIterator<G::NodeId>,
67 S: BuildHasher + Default,
68{
69 let max_length = if let Some(l) = max_intermediate_nodes {
73 l + 1
74 } else {
75 graph.node_count() - 1
76 };
77
78 let min_length = min_intermediate_nodes + 1;
79
80 let mut visited: IndexSet<G::NodeId, S> = IndexSet::from_iter(Some(from));
82 let mut stack = vec![graph.neighbors_directed(from, Outgoing)];
85
86 from_fn(move || {
87 while let Some(children) = stack.last_mut() {
88 if let Some(child) = children.next() {
89 if visited.len() < max_length {
90 if child == to {
91 if visited.len() >= min_length {
92 let path = visited
93 .iter()
94 .cloned()
95 .chain(Some(to))
96 .collect::<TargetColl>();
97 return Some(path);
98 }
99 } else if !visited.contains(&child) {
100 visited.insert(child);
101 stack.push(graph.neighbors_directed(child, Outgoing));
102 }
103 } else {
104 if (child == to || children.any(|v| v == to)) && visited.len() >= min_length {
105 let path = visited
106 .iter()
107 .cloned()
108 .chain(Some(to))
109 .collect::<TargetColl>();
110 return Some(path);
111 }
112 stack.pop();
113 visited.pop();
114 }
115 } else {
116 stack.pop();
117 visited.pop();
118 }
119 }
120 None
121 })
122}
123
124#[cfg(test)]
125mod test {
126 use alloc::{vec, vec::Vec};
127 use core::iter::FromIterator;
128 use std::{collections::hash_map::RandomState, println};
129
130 use hashbrown::HashSet;
131 use itertools::assert_equal;
132
133 use super::all_simple_paths;
134 use crate::{dot::Dot, prelude::DiGraph};
135
136 #[test]
137 fn test_all_simple_paths() {
138 let graph = DiGraph::<i32, i32, _>::from_edges([
139 (0, 1),
140 (0, 2),
141 (0, 3),
142 (1, 2),
143 (1, 3),
144 (2, 3),
145 (2, 4),
146 (3, 2),
147 (3, 4),
148 (4, 2),
149 (4, 5),
150 (5, 2),
151 (5, 3),
152 ]);
153
154 let expexted_simple_paths_0_to_5 = vec![
155 vec![0usize, 1, 2, 3, 4, 5],
156 vec![0, 1, 2, 4, 5],
157 vec![0, 1, 3, 2, 4, 5],
158 vec![0, 1, 3, 4, 5],
159 vec![0, 2, 3, 4, 5],
160 vec![0, 2, 4, 5],
161 vec![0, 3, 2, 4, 5],
162 vec![0, 3, 4, 5],
163 ];
164
165 println!("{}", Dot::new(&graph));
166 let actual_simple_paths_0_to_5: HashSet<Vec<_>> =
167 all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 5u32.into(), 0, None)
168 .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
169 .collect();
170 assert_eq!(actual_simple_paths_0_to_5.len(), 8);
171 assert_eq!(
172 HashSet::from_iter(expexted_simple_paths_0_to_5),
173 actual_simple_paths_0_to_5
174 );
175 }
176
177 #[test]
178 fn test_one_simple_path() {
179 let graph = DiGraph::<i32, i32, _>::from_edges([(0, 1), (2, 1)]);
180
181 let expexted_simple_paths_0_to_1 = &[vec![0usize, 1]];
182 println!("{}", Dot::new(&graph));
183 let actual_simple_paths_0_to_1: Vec<Vec<_>> =
184 all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 1u32.into(), 0, None)
185 .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
186 .collect();
187
188 assert_eq!(actual_simple_paths_0_to_1.len(), 1);
189 assert_equal(expexted_simple_paths_0_to_1, &actual_simple_paths_0_to_1);
190 }
191
192 #[test]
193 fn test_no_simple_paths() {
194 let graph = DiGraph::<i32, i32, _>::from_edges([(0, 1), (2, 1)]);
195
196 println!("{}", Dot::new(&graph));
197 let actual_simple_paths_0_to_2: Vec<Vec<_>> =
198 all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 2u32.into(), 0, None)
199 .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
200 .collect();
201
202 assert_eq!(actual_simple_paths_0_to_2.len(), 0);
203 }
204}