rust_igraph/algorithms/paths/
distances_from.rs1use std::collections::VecDeque;
10
11use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum DistancesFromMode {
16 Out,
18 In,
20 All,
22}
23
24pub fn distances_from(graph: &Graph, sources: &[VertexId]) -> IgraphResult<Vec<Option<u32>>> {
57 let n = graph.vcount();
58 validate_sources(sources, n)?;
59
60 if sources.is_empty() {
61 return Ok(Vec::new());
62 }
63
64 let n_us = n as usize;
65 let total = sources.len().checked_mul(n_us).ok_or_else(|| {
66 IgraphError::InvalidArgument("distances_from: result size overflows".into())
67 })?;
68 let mut result = vec![None; total];
69
70 if graph.is_directed() {
71 let adj = build_out_adj(graph, n_us)?;
72 for (row, &src) in sources.iter().enumerate() {
73 bfs_with_adj(&adj, src, n_us, row, &mut result);
74 }
75 } else {
76 for (row, &src) in sources.iter().enumerate() {
77 bfs_undirected(graph, src, n_us, row, &mut result)?;
78 }
79 }
80
81 Ok(result)
82}
83
84pub fn distances_from_with_mode(
110 graph: &Graph,
111 sources: &[VertexId],
112 mode: DistancesFromMode,
113) -> IgraphResult<Vec<Option<u32>>> {
114 let n = graph.vcount();
115 validate_sources(sources, n)?;
116
117 if sources.is_empty() {
118 return Ok(Vec::new());
119 }
120
121 let n_us = n as usize;
122 let total = sources.len().checked_mul(n_us).ok_or_else(|| {
123 IgraphError::InvalidArgument("distances_from: result size overflows".into())
124 })?;
125 let mut result = vec![None; total];
126
127 if !graph.is_directed() {
128 for (row, &src) in sources.iter().enumerate() {
129 bfs_undirected(graph, src, n_us, row, &mut result)?;
130 }
131 return Ok(result);
132 }
133
134 let adj = match mode {
135 DistancesFromMode::Out => build_out_adj(graph, n_us)?,
136 DistancesFromMode::In => build_in_adj(graph, n_us)?,
137 DistancesFromMode::All => build_all_adj(graph, n_us)?,
138 };
139
140 for (row, &src) in sources.iter().enumerate() {
141 bfs_with_adj(&adj, src, n_us, row, &mut result);
142 }
143
144 Ok(result)
145}
146
147fn validate_sources(sources: &[VertexId], n: u32) -> IgraphResult<()> {
148 for &s in sources {
149 if s >= n {
150 return Err(IgraphError::InvalidArgument(format!(
151 "distances_from: source {s} out of range (vcount={n})"
152 )));
153 }
154 }
155 Ok(())
156}
157
158fn bfs_undirected(
159 graph: &Graph,
160 source: VertexId,
161 n_us: usize,
162 row: usize,
163 result: &mut [Option<u32>],
164) -> IgraphResult<()> {
165 let offset = row * n_us;
166 let mut visited = vec![false; n_us];
167 let mut queue = VecDeque::new();
168
169 visited[source as usize] = true;
170 result[offset + source as usize] = Some(0);
171 queue.push_back((source, 0u32));
172
173 while let Some((cur, dist)) = queue.pop_front() {
174 let next_dist = dist + 1;
175 for nb in graph.neighbors_iter(cur)? {
176 let nb_idx = nb as usize;
177 if !visited[nb_idx] {
178 visited[nb_idx] = true;
179 result[offset + nb_idx] = Some(next_dist);
180 queue.push_back((nb, next_dist));
181 }
182 }
183 }
184
185 Ok(())
186}
187
188fn bfs_with_adj(
189 adj: &[Vec<VertexId>],
190 source: VertexId,
191 n_us: usize,
192 row: usize,
193 result: &mut [Option<u32>],
194) {
195 let offset = row * n_us;
196 let mut visited = vec![false; n_us];
197 let mut queue = VecDeque::new();
198
199 visited[source as usize] = true;
200 result[offset + source as usize] = Some(0);
201 queue.push_back((source, 0u32));
202
203 while let Some((cur, dist)) = queue.pop_front() {
204 let next_dist = dist + 1;
205 for &nb in &adj[cur as usize] {
206 let nb_idx = nb as usize;
207 if !visited[nb_idx] {
208 visited[nb_idx] = true;
209 result[offset + nb_idx] = Some(next_dist);
210 queue.push_back((nb, next_dist));
211 }
212 }
213 }
214}
215
216fn build_out_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
217 let m =
218 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
219 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
220 for eid in 0..m {
221 let (from, to) = graph.edge(eid)?;
222 adj[from as usize].push(to);
223 }
224 Ok(adj)
225}
226
227fn build_in_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
228 let m =
229 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
230 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
231 for eid in 0..m {
232 let (from, to) = graph.edge(eid)?;
233 adj[to as usize].push(from);
234 }
235 Ok(adj)
236}
237
238fn build_all_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
239 let m =
240 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
241 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
242 for eid in 0..m {
243 let (from, to) = graph.edge(eid)?;
244 adj[from as usize].push(to);
245 if from != to {
246 adj[to as usize].push(from);
247 }
248 }
249 Ok(adj)
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn empty_sources() {
258 let g = Graph::with_vertices(3);
259 let d = distances_from(&g, &[]).unwrap();
260 assert!(d.is_empty());
261 }
262
263 #[test]
264 fn single_source_matches_distances() {
265 let mut g = Graph::with_vertices(4);
266 g.add_edge(0, 1).unwrap();
267 g.add_edge(1, 2).unwrap();
268 g.add_edge(2, 3).unwrap();
269 let d = distances_from(&g, &[0]).unwrap();
270 assert_eq!(d, vec![Some(0), Some(1), Some(2), Some(3)]);
271 }
272
273 #[test]
274 fn multiple_sources() {
275 let mut g = Graph::with_vertices(4);
276 g.add_edge(0, 1).unwrap();
277 g.add_edge(1, 2).unwrap();
278 g.add_edge(2, 3).unwrap();
279 let d = distances_from(&g, &[0, 3]).unwrap();
280 let n = 4;
281 assert_eq!(d[0], Some(0));
283 assert_eq!(d[1], Some(1));
284 assert_eq!(d[2], Some(2));
285 assert_eq!(d[3], Some(3));
286 assert_eq!(d[n], Some(3));
288 assert_eq!(d[n + 1], Some(2));
289 assert_eq!(d[n + 2], Some(1));
290 assert_eq!(d[n + 3], Some(0));
291 }
292
293 #[test]
294 fn disconnected() {
295 let mut g = Graph::with_vertices(4);
296 g.add_edge(0, 1).unwrap();
297 g.add_edge(2, 3).unwrap();
298 let d = distances_from(&g, &[0]).unwrap();
299 assert_eq!(d[0], Some(0));
300 assert_eq!(d[1], Some(1));
301 assert_eq!(d[2], None);
302 assert_eq!(d[3], None);
303 }
304
305 #[test]
306 fn source_out_of_range() {
307 let g = Graph::with_vertices(3);
308 assert!(distances_from(&g, &[5]).is_err());
309 }
310
311 #[test]
312 fn directed_out() {
313 let mut g = Graph::new(3, true).unwrap();
314 g.add_edge(0, 1).unwrap();
315 g.add_edge(1, 2).unwrap();
316 let d = distances_from(&g, &[0, 2]).unwrap();
317 let n = 3;
318 assert_eq!(d[0], Some(0));
320 assert_eq!(d[1], Some(1));
321 assert_eq!(d[2], Some(2));
322 assert_eq!(d[n], None);
324 assert_eq!(d[n + 1], None);
325 assert_eq!(d[n + 2], Some(0));
326 }
327
328 #[test]
329 fn directed_in_mode() {
330 let mut g = Graph::new(3, true).unwrap();
331 g.add_edge(0, 1).unwrap();
332 g.add_edge(1, 2).unwrap();
333 let d = distances_from_with_mode(&g, &[2], DistancesFromMode::In).unwrap();
334 assert_eq!(d[0], Some(2));
336 assert_eq!(d[1], Some(1));
337 assert_eq!(d[2], Some(0));
338 }
339
340 #[test]
341 fn directed_all_mode() {
342 let mut g = Graph::new(3, true).unwrap();
343 g.add_edge(0, 1).unwrap();
344 g.add_edge(1, 2).unwrap();
345 let d = distances_from_with_mode(&g, &[2], DistancesFromMode::All).unwrap();
346 assert_eq!(d[0], Some(2));
348 assert_eq!(d[1], Some(1));
349 assert_eq!(d[2], Some(0));
350 }
351
352 #[test]
353 fn duplicate_sources() {
354 let mut g = Graph::with_vertices(3);
355 g.add_edge(0, 1).unwrap();
356 g.add_edge(1, 2).unwrap();
357 let d = distances_from(&g, &[0, 0]).unwrap();
358 let n = 3;
359 assert_eq!(&d[..n], &d[n..]);
361 }
362
363 #[test]
364 fn star_graph() {
365 let mut g = Graph::with_vertices(5);
366 g.add_edge(0, 1).unwrap();
367 g.add_edge(0, 2).unwrap();
368 g.add_edge(0, 3).unwrap();
369 g.add_edge(0, 4).unwrap();
370 let d = distances_from(&g, &[1, 2]).unwrap();
371 let n = 5;
372 assert_eq!(d[0], Some(1));
374 assert_eq!(d[1], Some(0));
375 assert_eq!(d[2], Some(2));
376 assert_eq!(d[3], Some(2));
377 assert_eq!(d[4], Some(2));
378 assert_eq!(d[n], Some(1));
380 assert_eq!(d[n + 1], Some(2));
381 assert_eq!(d[n + 2], Some(0));
382 }
383}