use std::collections::VecDeque;
use crate::core::{Graph, IgraphResult, VertexId};
pub fn distances(graph: &Graph, source: VertexId) -> IgraphResult<Vec<Option<u32>>> {
let n = graph.vcount();
if n == 0 {
return Ok(Vec::new());
}
graph.neighbors(source)?;
let n_us = n as usize;
let mut dist: Vec<Option<u32>> = vec![None; n_us];
let mut queue: VecDeque<VertexId> = VecDeque::new();
dist[source as usize] = Some(0);
queue.push_back(source);
while let Some(v) = queue.pop_front() {
let next = dist[v as usize]
.expect("dequeued vertex has been visited")
.checked_add(1)
.ok_or(crate::core::IgraphError::Internal(
"distance overflow (graph diameter exceeds u32::MAX)",
))?;
for w in graph.neighbors(v)? {
if dist[w as usize].is_none() {
dist[w as usize] = Some(next);
queue.push_back(w);
}
}
}
Ok(dist)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_yields_empty_distances() {
let g = Graph::with_vertices(0);
let d = distances(&g, 0);
assert_eq!(d.unwrap(), Vec::<Option<u32>>::new());
}
#[test]
fn single_vertex_distance_to_self_is_zero() {
let g = Graph::with_vertices(1);
let d = distances(&g, 0).unwrap();
assert_eq!(d, vec![Some(0)]);
}
#[test]
fn invalid_source_errors() {
let g = Graph::with_vertices(3);
let err = distances(&g, 5);
assert!(err.is_err(), "expected error for source >= vcount");
}
#[test]
fn path_distances_increase_by_one() {
let mut g = Graph::with_vertices(5);
for i in 0..4 {
g.add_edge(i, i + 1).unwrap();
}
let d = distances(&g, 0).unwrap();
assert_eq!(d, vec![Some(0), Some(1), Some(2), Some(3), Some(4)]);
}
#[test]
fn isolated_components_are_unreachable() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(3, 4).unwrap();
let d = distances(&g, 0).unwrap();
assert_eq!(d, vec![Some(0), Some(1), Some(2), None, None]);
}
#[test]
fn self_loop_does_not_affect_distance() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let d = distances(&g, 0).unwrap();
assert_eq!(d, vec![Some(0), Some(1)]);
}
#[test]
fn parallel_edges_do_not_affect_distance() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
let d = distances(&g, 0).unwrap();
assert_eq!(d, vec![Some(0), Some(1)]);
}
#[test]
fn directed_graph_uses_out_edges() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let d_from_zero = distances(&g, 0).unwrap();
assert_eq!(d_from_zero, vec![Some(0), Some(1), Some(2)]);
let d_from_two = distances(&g, 2).unwrap();
assert_eq!(d_from_two, vec![None, None, Some(0)]);
}
#[test]
fn cycle_distances_are_minimum_of_two_directions() {
let mut g = Graph::with_vertices(6);
for i in 0..5 {
g.add_edge(i, i + 1).unwrap();
}
g.add_edge(5, 0).unwrap();
let d = distances(&g, 0).unwrap();
assert_eq!(
d,
vec![Some(0), Some(1), Some(2), Some(3), Some(2), Some(1)]
);
}
}