use std::collections::VecDeque;
use crate::core::{Graph, IgraphResult, VertexId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BfsTree {
pub order: Vec<VertexId>,
pub distances: Vec<Option<u32>>,
pub parents: Vec<Option<VertexId>>,
}
pub fn bfs_tree(graph: &Graph, root: VertexId) -> IgraphResult<BfsTree> {
graph.neighbors(root)?;
let n = graph.vcount();
let n_us = n as usize;
let mut distances: Vec<Option<u32>> = vec![None; n_us];
let mut parents: Vec<Option<VertexId>> = vec![None; n_us];
let mut order: Vec<VertexId> = Vec::with_capacity(n_us);
let mut queue: VecDeque<VertexId> = VecDeque::new();
distances[root as usize] = Some(0);
order.push(root);
queue.push_back(root);
while let Some(v) = queue.pop_front() {
let v_dist = distances[v as usize].expect("dequeued vertex is visited");
let next_dist = v_dist
.checked_add(1)
.ok_or(crate::core::IgraphError::Internal(
"BFS distance overflow (graph diameter exceeds u32::MAX)",
))?;
for w in graph.neighbors(v)? {
if distances[w as usize].is_none() {
distances[w as usize] = Some(next_dist);
parents[w as usize] = Some(v);
order.push(w);
queue.push_back(w);
}
}
}
Ok(BfsTree {
order,
distances,
parents,
})
}
pub fn bfs(graph: &Graph, root: VertexId) -> IgraphResult<Vec<VertexId>> {
graph.neighbors(root)?;
let n = graph.vcount();
let mut visited = vec![false; n as usize];
let mut order: Vec<VertexId> = Vec::with_capacity(n as usize);
let mut queue: VecDeque<VertexId> = VecDeque::new();
visited[root as usize] = true;
order.push(root);
queue.push_back(root);
while let Some(v) = queue.pop_front() {
for w in graph.neighbors(v)? {
if !visited[w as usize] {
visited[w as usize] = true;
order.push(w);
queue.push_back(w);
}
}
}
Ok(order)
}
#[cfg(test)]
mod tests {
use super::*;
fn path_graph(n: u32) -> Graph {
let mut g = Graph::with_vertices(n);
for i in 0..n.saturating_sub(1) {
g.add_edge(i, i + 1).unwrap();
}
g
}
#[test]
fn empty_graph_errors_on_any_root() {
let g = Graph::with_vertices(0);
let err = bfs(&g, 0).unwrap_err();
assert!(matches!(
err,
crate::core::IgraphError::VertexOutOfRange { id: 0, n: 0 }
));
}
#[test]
fn single_vertex_visits_self() {
let g = Graph::with_vertices(1);
assert_eq!(bfs(&g, 0).unwrap(), vec![0]);
}
#[test]
fn path_visits_in_order() {
let g = path_graph(5);
assert_eq!(bfs(&g, 0).unwrap(), vec![0, 1, 2, 3, 4]);
}
#[test]
fn bfs_layers_breadth_first_not_depth_first() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(1, 3).unwrap();
let order = bfs(&g, 0).unwrap();
assert_eq!(order[0], 0);
let pos_3 = order.iter().position(|&x| x == 3).unwrap();
let pos_1 = order.iter().position(|&x| x == 1).unwrap();
let pos_2 = order.iter().position(|&x| x == 2).unwrap();
assert!(pos_3 > pos_1 && pos_3 > pos_2);
}
#[test]
fn unreachable_vertex_excluded() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
let order = bfs(&g, 0).unwrap();
assert_eq!(order, vec![0, 1]);
}
#[test]
fn invalid_root_errors() {
let g = Graph::with_vertices(2);
let err = bfs(&g, 5).unwrap_err();
match err {
crate::core::IgraphError::VertexOutOfRange { id, n } => {
assert_eq!(id, 5);
assert_eq!(n, 2);
}
other => panic!("unexpected error: {other:?}"),
}
}
#[test]
fn bfs_tree_returns_full_state_for_a_tree() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
let r = bfs_tree(&g, 0).unwrap();
assert_eq!(r.order, vec![0, 1, 2, 3]);
assert_eq!(r.distances, vec![Some(0), Some(1), Some(1), Some(1)]);
assert_eq!(r.parents, vec![None, Some(0), Some(0), Some(0)]);
}
#[test]
fn bfs_tree_path_5_is_chain() {
let g = path_graph(5);
let r = bfs_tree(&g, 0).unwrap();
assert_eq!(r.order, vec![0, 1, 2, 3, 4]);
assert_eq!(
r.distances,
vec![Some(0), Some(1), Some(2), Some(3), Some(4)]
);
assert_eq!(r.parents, vec![None, Some(0), Some(1), Some(2), Some(3)]);
}
#[test]
fn bfs_tree_unreachable_vertices_have_none() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
let r = bfs_tree(&g, 0).unwrap();
assert_eq!(r.order, vec![0, 1]);
assert_eq!(r.distances, vec![Some(0), Some(1), None, None]);
assert_eq!(r.parents, vec![None, Some(0), None, None]);
}
#[test]
fn bfs_tree_invalid_root_errors() {
let g = Graph::with_vertices(3);
assert!(bfs_tree(&g, 7).is_err());
}
#[test]
fn bfs_tree_directed_uses_out_edges() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(1, 3).unwrap();
let r = bfs_tree(&g, 0).unwrap();
assert_eq!(r.distances, vec![Some(0), Some(1), Some(2), Some(2)]);
assert_eq!(r.parents[0], None);
assert_eq!(r.parents[1], Some(0));
assert_eq!(r.parents[2], Some(1));
assert_eq!(r.parents[3], Some(1));
let r2 = bfs_tree(&g, 2).unwrap();
assert_eq!(r2.order, vec![2]);
}
}