use rustc_hash::{FxBuildHasher, FxHashSet};
use crate::{
adapt::Transpose,
algo::Cycle,
core::{Neighbors, VertexSet, id::UseVertexId, marker::Directed},
visit::{
VisitAll, VisitSet, Visitor,
raw::{RawDfsExtra, RawDfsExtraEvent, RawEvent, RawVisit, RawVisitMulti},
},
};
use super::Error;
pub fn dfs_visit<'a, G>(graph: &'a G) -> DfsVisit<'a, G>
where
G: VertexSet + 'a,
{
DfsVisit {
raw: RawVisit::new(Some(graph.vertex_count())),
multi: RawVisitMulti::new(VisitAll::new(graph)),
closed: FxHashSet::with_capacity_and_hasher(graph.vertex_count(), FxBuildHasher),
cycle: None,
}
}
pub struct DfsVisit<'a, G>
where
G: VertexSet + 'a,
{
raw: RawVisit<G, UseVertexId, RawDfsExtra>,
multi: RawVisitMulti<G, UseVertexId, RawDfsExtra, VisitAll<'a, G>>,
closed: FxHashSet<G::VertexId>,
cycle: Option<G::EdgeId>,
}
impl<'a, G> Visitor<G> for DfsVisit<'a, G>
where
G: Neighbors<EdgeType = Directed> + VertexSet,
{
type Item = Result<G::VertexId, Error<G>>;
fn visit_next(&mut self, graph: &G) -> Option<Self::Item> {
if self.cycle.is_some() {
return None;
}
let graph = &Transpose::new(graph);
loop {
let raw_extra_event = self.multi.next_multi(
&mut self.raw,
|raw| {
raw.next(graph, |_, raw_event| match raw_event {
RawEvent::Skip { vertex, from } if !self.closed.is_visited(&vertex) => {
self.cycle = from.map(|(_, edge)| edge);
false
}
_ => true,
})
},
|vertex| graph.contains_vertex(vertex),
)?;
if let Some(ref cycle) = self.cycle {
return Some(Err(Error::Cycle(Cycle::new(cycle.clone(), false))));
}
if let RawDfsExtraEvent::Close(vertex) = raw_extra_event {
self.closed.visit(vertex.clone());
return Some(Ok(vertex));
}
}
}
}