rust_igraph/algorithms/paths/
distances.rs1use std::collections::VecDeque;
18
19use crate::core::{Graph, IgraphResult, VertexId};
20
21pub fn distances(graph: &Graph, source: VertexId) -> IgraphResult<Vec<Option<u32>>> {
48 let n = graph.vcount();
49 if n == 0 {
50 return Ok(Vec::new());
51 }
52 graph.neighbors(source)?;
54
55 let n_us = n as usize;
56 let mut dist: Vec<Option<u32>> = vec![None; n_us];
57 let mut queue: VecDeque<VertexId> = VecDeque::new();
58
59 dist[source as usize] = Some(0);
60 queue.push_back(source);
61
62 while let Some(v) = queue.pop_front() {
63 let next = dist[v as usize]
64 .ok_or(crate::core::IgraphError::Internal(
65 "dequeued vertex has no distance",
66 ))?
67 .checked_add(1)
68 .ok_or(crate::core::IgraphError::Internal(
69 "distance overflow (graph diameter exceeds u32::MAX)",
70 ))?;
71 for w in graph.neighbors(v)? {
72 if dist[w as usize].is_none() {
73 dist[w as usize] = Some(next);
74 queue.push_back(w);
75 }
76 }
77 }
78
79 Ok(dist)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn empty_graph_yields_empty_distances() {
88 let g = Graph::with_vertices(0);
89 let d = distances(&g, 0);
90 assert_eq!(d.unwrap(), Vec::<Option<u32>>::new());
94 }
95
96 #[test]
97 fn single_vertex_distance_to_self_is_zero() {
98 let g = Graph::with_vertices(1);
99 let d = distances(&g, 0).unwrap();
100 assert_eq!(d, vec![Some(0)]);
101 }
102
103 #[test]
104 fn invalid_source_errors() {
105 let g = Graph::with_vertices(3);
106 let err = distances(&g, 5);
107 assert!(err.is_err(), "expected error for source >= vcount");
108 }
109
110 #[test]
111 fn path_distances_increase_by_one() {
112 let mut g = Graph::with_vertices(5);
114 for i in 0..4 {
115 g.add_edge(i, i + 1).unwrap();
116 }
117 let d = distances(&g, 0).unwrap();
118 assert_eq!(d, vec![Some(0), Some(1), Some(2), Some(3), Some(4)]);
119 }
120
121 #[test]
122 fn isolated_components_are_unreachable() {
123 let mut g = Graph::with_vertices(5);
125 g.add_edge(0, 1).unwrap();
126 g.add_edge(1, 2).unwrap();
127 g.add_edge(3, 4).unwrap();
128 let d = distances(&g, 0).unwrap();
129 assert_eq!(d, vec![Some(0), Some(1), Some(2), None, None]);
130 }
131
132 #[test]
133 fn self_loop_does_not_affect_distance() {
134 let mut g = Graph::with_vertices(2);
136 g.add_edge(0, 0).unwrap();
137 g.add_edge(0, 1).unwrap();
138 let d = distances(&g, 0).unwrap();
139 assert_eq!(d, vec![Some(0), Some(1)]);
140 }
141
142 #[test]
143 fn parallel_edges_do_not_affect_distance() {
144 let mut g = Graph::with_vertices(2);
146 g.add_edge(0, 1).unwrap();
147 g.add_edge(0, 1).unwrap();
148 g.add_edge(0, 1).unwrap();
149 let d = distances(&g, 0).unwrap();
150 assert_eq!(d, vec![Some(0), Some(1)]);
151 }
152
153 #[test]
154 fn directed_graph_uses_out_edges() {
155 let mut g = Graph::new(3, true).unwrap();
157 g.add_edge(0, 1).unwrap();
158 g.add_edge(1, 2).unwrap();
159 let d_from_zero = distances(&g, 0).unwrap();
160 assert_eq!(d_from_zero, vec![Some(0), Some(1), Some(2)]);
161 let d_from_two = distances(&g, 2).unwrap();
162 assert_eq!(d_from_two, vec![None, None, Some(0)]);
163 }
164
165 #[test]
166 fn cycle_distances_are_minimum_of_two_directions() {
167 let mut g = Graph::with_vertices(6);
170 for i in 0..5 {
171 g.add_edge(i, i + 1).unwrap();
172 }
173 g.add_edge(5, 0).unwrap();
174 let d = distances(&g, 0).unwrap();
175 assert_eq!(
176 d,
177 vec![Some(0), Some(1), Some(2), Some(3), Some(2), Some(1)]
178 );
179 }
180}