use crate::graph::Graph;
use crate::iterators::VertexIter;
use crate::vertex_id::VertexId;
use hashbrown::HashSet;
use std::iter::{Chain, Cloned, Peekable};
#[derive(Debug)]
pub struct Dfs<'a, T> {
unchecked: Peekable<Cloned<Chain<VertexIter<'a>, VertexIter<'a>>>>,
black: HashSet<VertexId>,
grey: HashSet<VertexId>,
pending_stack: Vec<VertexId>,
iterable: &'a Graph<T>,
cached_cyclic: bool,
}
impl<'a, T> Dfs<'a, T> {
pub fn new(graph: &'a Graph<T>) -> Dfs<'_, T> {
let unchecked = graph.roots().chain(graph.vertices()).cloned().peekable();
Dfs {
unchecked,
iterable: graph,
cached_cyclic: false,
grey: HashSet::new(),
black: HashSet::new(),
pending_stack: Vec::new(),
}
}
pub fn is_cyclic(&mut self) -> bool {
if self.cached_cyclic {
return self.cached_cyclic;
}
while self.process_vertex().is_some() {}
self.cached_cyclic
}
fn process_vertex(&mut self) -> Option<&'a VertexId> {
if self.pending_stack.is_empty() {
self.black.extend(self.grey.drain());
let unchecked = &mut self.unchecked;
let black = &self.black;
let next = unchecked.find(move |v| !black.contains(v));
if let Some(v) = next {
self.pending_stack.push(v);
}
}
self.pending_stack
.pop()
.iter()
.filter_map(|v| {
if !self.grey.insert(*v) {
self.cached_cyclic = true;
return None;
}
for v in self.iterable.out_neighbors(v) {
if self.grey.contains(v) {
self.cached_cyclic = true
} else {
self.pending_stack.push(*v)
}
}
self.iterable.fetch_id_ref(v)
})
.next()
}
}
impl<'a, T> Iterator for Dfs<'a, T> {
type Item = &'a VertexId;
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.iterable.vertex_count() - self.black.len();
(0, Some(remaining))
}
fn next(&mut self) -> Option<Self::Item> {
(0..self.size_hint().1.unwrap())
.filter_map(move |_| self.process_vertex())
.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_cyclic() {
for _ in 0..100 {
let mut graph = Graph::new();
let v = graph.add_vertex(0);
assert!(graph.add_edge(&v, &v).is_ok(), "Failed to create cycle");
for _ in 0..100 {
graph.add_vertex(0);
}
let mut dfs = graph.dfs();
for _ in 0..99 {
dfs.next();
}
assert!(dfs.is_cyclic());
}
}
#[test]
fn not_cyclic() {
let mut graph = Graph::new();
let v1 = graph.add_vertex(());
let v2 = graph.add_vertex(());
let v3 = graph.add_vertex(());
graph.add_edge(&v1, &v2);
graph.add_edge(&v3, &v2);
graph.add_vertex(());
assert!(graph.is_cyclic() == false,);
}
}