use std::collections::{VecDeque, hash_map::Entry};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use crate::{
core::{Neighbors, base::NeighborReference, marker::Direction, weight::Weight},
visit::VisitSet,
};
use super::{Error, ShortestPaths};
pub fn bfs<G, W>(
graph: &G,
source: G::VertexId,
goal: Option<G::VertexId>,
edge_dist: W,
) -> Result<ShortestPaths<W, G>, Error>
where
G: Neighbors,
W: Weight,
{
if !W::is_unsigned() && edge_dist < W::zero() {
return Err(Error::NegativeWeight);
}
let mut visited =
FxHashSet::with_capacity_and_hasher(graph.vertex_count_hint().unwrap_or(32), FxBuildHasher);
let mut dist = FxHashMap::default();
let mut pred = FxHashMap::default();
let mut queue = VecDeque::new();
dist.insert(source.clone(), W::zero());
queue.push_back((source.clone(), W::zero()));
while let Some((vertex, vertex_dist)) = queue.pop_front() {
if goal.as_ref() == Some(&vertex) {
visited.visit(vertex);
break;
}
for neighbor in graph.neighbors_directed(&vertex, Direction::Outgoing) {
let next = neighbor.id();
if visited.is_visited(&*next) {
continue;
}
let next = next.into_owned();
let next_dist = vertex_dist.clone() + edge_dist.clone();
if let Entry::Vacant(slot) = dist.entry(next.clone()) {
slot.insert(next_dist.clone());
pred.insert(next.clone(), vertex.clone());
}
queue.push_back((next, next_dist));
}
visited.visit(vertex.clone());
}
if let Some(ref goal) = goal
&& !visited.is_visited(goal)
{
return Err(Error::GoalNotReached);
}
Ok(ShortestPaths { source, dist, pred })
}