use std::collections::HashSet;
use std::hash::Hash;
use std::iter::FusedIterator;
use rustc_hash::{FxHashMap, FxHashSet};
pub fn dfs<N, FN, IN, FS>(start: N, mut successors: FN, mut success: FS) -> Option<Vec<N>>
where
N: Clone + Eq + Hash,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = N>,
FS: FnMut(&N) -> bool,
{
let mut to_visit = vec![start];
let mut visited = FxHashSet::default();
let mut parents = FxHashMap::default();
while let Some(node) = to_visit.pop() {
if visited.insert(node.clone()) {
if success(&node) {
return Some(build_path(node, &parents));
}
for next in successors(&node)
.into_iter()
.collect::<Vec<_>>()
.into_iter()
.rev()
{
if !visited.contains(&next) {
parents.insert(next.clone(), node.clone());
to_visit.push(next);
}
}
}
}
None
}
fn build_path<N>(mut node: N, parents: &FxHashMap<N, N>) -> Vec<N>
where
N: Clone + Eq + Hash,
{
let mut path = vec![node.clone()];
while let Some(parent) = parents.get(&node).cloned() {
path.push(parent.clone());
node = parent;
}
path.into_iter().rev().collect()
}
pub fn dfs_reach<N, FN, IN>(start: N, successors: FN) -> DfsReachable<N, FN>
where
N: Eq + Hash + Clone,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = N>,
{
DfsReachable {
to_see: vec![start],
visited: HashSet::new(),
successors,
}
}
pub struct DfsReachable<N, FN> {
to_see: Vec<N>,
visited: HashSet<N>,
successors: FN,
}
impl<N, FN, IN> Iterator for DfsReachable<N, FN>
where
N: Eq + Hash + Clone,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = N>,
{
type Item = N;
fn next(&mut self) -> Option<Self::Item> {
let n = self.to_see.pop()?;
if self.visited.contains(&n) {
return self.next();
}
self.visited.insert(n.clone());
let mut to_insert = Vec::new();
for s in (self.successors)(&n) {
if !self.visited.contains(&s) {
to_insert.push(s.clone());
}
}
self.to_see.extend(to_insert.into_iter().rev());
Some(n)
}
}
impl<N, FN, IN> FusedIterator for DfsReachable<N, FN>
where
N: Eq + Hash + Clone,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = N>,
{
}