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 let neighbors = graph.neighbors(cur)?;
176 for &nb in &neighbors {
177 let nb_idx = nb as usize;
178 if !visited[nb_idx] {
179 visited[nb_idx] = true;
180 result[offset + nb_idx] = Some(next_dist);
181 queue.push_back((nb, next_dist));
182 }
183 }
184 }
185
186 Ok(())
187}
188
189fn bfs_with_adj(
190 adj: &[Vec<VertexId>],
191 source: VertexId,
192 n_us: usize,
193 row: usize,
194 result: &mut [Option<u32>],
195) {
196 let offset = row * n_us;
197 let mut visited = vec![false; n_us];
198 let mut queue = VecDeque::new();
199
200 visited[source as usize] = true;
201 result[offset + source as usize] = Some(0);
202 queue.push_back((source, 0u32));
203
204 while let Some((cur, dist)) = queue.pop_front() {
205 let next_dist = dist + 1;
206 for &nb in &adj[cur as usize] {
207 let nb_idx = nb as usize;
208 if !visited[nb_idx] {
209 visited[nb_idx] = true;
210 result[offset + nb_idx] = Some(next_dist);
211 queue.push_back((nb, next_dist));
212 }
213 }
214 }
215}
216
217fn build_out_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
218 let m =
219 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
220 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
221 for eid in 0..m {
222 let (from, to) = graph.edge(eid)?;
223 adj[from as usize].push(to);
224 }
225 Ok(adj)
226}
227
228fn build_in_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
229 let m =
230 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
231 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
232 for eid in 0..m {
233 let (from, to) = graph.edge(eid)?;
234 adj[to as usize].push(from);
235 }
236 Ok(adj)
237}
238
239fn build_all_adj(graph: &Graph, n_us: usize) -> IgraphResult<Vec<Vec<VertexId>>> {
240 let m =
241 u32::try_from(graph.ecount()).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
242 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n_us];
243 for eid in 0..m {
244 let (from, to) = graph.edge(eid)?;
245 adj[from as usize].push(to);
246 if from != to {
247 adj[to as usize].push(from);
248 }
249 }
250 Ok(adj)
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn empty_sources() {
259 let g = Graph::with_vertices(3);
260 let d = distances_from(&g, &[]).unwrap();
261 assert!(d.is_empty());
262 }
263
264 #[test]
265 fn single_source_matches_distances() {
266 let mut g = Graph::with_vertices(4);
267 g.add_edge(0, 1).unwrap();
268 g.add_edge(1, 2).unwrap();
269 g.add_edge(2, 3).unwrap();
270 let d = distances_from(&g, &[0]).unwrap();
271 assert_eq!(d, vec![Some(0), Some(1), Some(2), Some(3)]);
272 }
273
274 #[test]
275 fn multiple_sources() {
276 let mut g = Graph::with_vertices(4);
277 g.add_edge(0, 1).unwrap();
278 g.add_edge(1, 2).unwrap();
279 g.add_edge(2, 3).unwrap();
280 let d = distances_from(&g, &[0, 3]).unwrap();
281 let n = 4;
282 assert_eq!(d[0], Some(0));
284 assert_eq!(d[1], Some(1));
285 assert_eq!(d[2], Some(2));
286 assert_eq!(d[3], Some(3));
287 assert_eq!(d[n], Some(3));
289 assert_eq!(d[n + 1], Some(2));
290 assert_eq!(d[n + 2], Some(1));
291 assert_eq!(d[n + 3], Some(0));
292 }
293
294 #[test]
295 fn disconnected() {
296 let mut g = Graph::with_vertices(4);
297 g.add_edge(0, 1).unwrap();
298 g.add_edge(2, 3).unwrap();
299 let d = distances_from(&g, &[0]).unwrap();
300 assert_eq!(d[0], Some(0));
301 assert_eq!(d[1], Some(1));
302 assert_eq!(d[2], None);
303 assert_eq!(d[3], None);
304 }
305
306 #[test]
307 fn source_out_of_range() {
308 let g = Graph::with_vertices(3);
309 assert!(distances_from(&g, &[5]).is_err());
310 }
311
312 #[test]
313 fn directed_out() {
314 let mut g = Graph::new(3, true).unwrap();
315 g.add_edge(0, 1).unwrap();
316 g.add_edge(1, 2).unwrap();
317 let d = distances_from(&g, &[0, 2]).unwrap();
318 let n = 3;
319 assert_eq!(d[0], Some(0));
321 assert_eq!(d[1], Some(1));
322 assert_eq!(d[2], Some(2));
323 assert_eq!(d[n], None);
325 assert_eq!(d[n + 1], None);
326 assert_eq!(d[n + 2], Some(0));
327 }
328
329 #[test]
330 fn directed_in_mode() {
331 let mut g = Graph::new(3, true).unwrap();
332 g.add_edge(0, 1).unwrap();
333 g.add_edge(1, 2).unwrap();
334 let d = distances_from_with_mode(&g, &[2], DistancesFromMode::In).unwrap();
335 assert_eq!(d[0], Some(2));
337 assert_eq!(d[1], Some(1));
338 assert_eq!(d[2], Some(0));
339 }
340
341 #[test]
342 fn directed_all_mode() {
343 let mut g = Graph::new(3, true).unwrap();
344 g.add_edge(0, 1).unwrap();
345 g.add_edge(1, 2).unwrap();
346 let d = distances_from_with_mode(&g, &[2], DistancesFromMode::All).unwrap();
347 assert_eq!(d[0], Some(2));
349 assert_eq!(d[1], Some(1));
350 assert_eq!(d[2], Some(0));
351 }
352
353 #[test]
354 fn duplicate_sources() {
355 let mut g = Graph::with_vertices(3);
356 g.add_edge(0, 1).unwrap();
357 g.add_edge(1, 2).unwrap();
358 let d = distances_from(&g, &[0, 0]).unwrap();
359 let n = 3;
360 assert_eq!(&d[..n], &d[n..]);
362 }
363
364 #[test]
365 fn star_graph() {
366 let mut g = Graph::with_vertices(5);
367 g.add_edge(0, 1).unwrap();
368 g.add_edge(0, 2).unwrap();
369 g.add_edge(0, 3).unwrap();
370 g.add_edge(0, 4).unwrap();
371 let d = distances_from(&g, &[1, 2]).unwrap();
372 let n = 5;
373 assert_eq!(d[0], Some(1));
375 assert_eq!(d[1], Some(0));
376 assert_eq!(d[2], Some(2));
377 assert_eq!(d[3], Some(2));
378 assert_eq!(d[4], Some(2));
379 assert_eq!(d[n], Some(1));
381 assert_eq!(d[n + 1], Some(2));
382 assert_eq!(d[n + 2], Some(0));
383 }
384}