use indexmap::map::Entry::{Occupied, Vacant};
use std::hash::Hash;
use std::iter::FusedIterator;
use super::reverse_path;
use crate::FxIndexMap;
use rustc_hash::FxHashSet;
#[expect(clippy::missing_panics_doc)]
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 parents: FxIndexMap<N, usize> = FxIndexMap::default();
parents.insert(start, usize::MAX);
let mut stack = vec![0];
let mut expanded = vec![false];
while let Some(index) = stack.pop() {
if expanded[index] {
continue;
}
expanded[index] = true;
let neighbs = {
let (node, _) = parents.get_index(index).unwrap();
if success(node) {
return Some(reverse_path(&parents, |&p| p, index));
}
successors(node).into_iter().collect::<Vec<_>>()
};
for next in neighbs.into_iter().rev() {
match parents.entry(next) {
Vacant(e) => {
let n = e.index();
e.insert(index);
expanded.push(false);
stack.push(n);
}
Occupied(mut e) => {
let n = e.index();
if !expanded[n] {
e.insert(index);
stack.push(n);
}
}
}
}
}
None
}
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: FxHashSet::default(),
successors,
}
}
pub struct DfsReachable<N, FN> {
to_see: Vec<N>,
visited: FxHashSet<N>,
successors: FN,
}
impl<N, FN> DfsReachable<N, FN>
where
N: Eq + Hash,
{
pub fn remaining_nodes_low_bound(&self) -> usize {
self.to_see.iter().collect::<FxHashSet<_>>().len()
}
}
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> {
loop {
let n = self.to_see.pop()?;
if !self.visited.insert(n.clone()) {
continue;
}
let mut to_insert = Vec::new();
for s in (self.successors)(&n) {
if !self.visited.contains(&s) {
to_insert.push(s);
}
}
self.to_see.extend(to_insert.into_iter().rev());
return Some(n);
}
}
}
impl<N, FN, IN> FusedIterator for DfsReachable<N, FN>
where
N: Eq + Hash + Clone,
FN: FnMut(&N) -> IN,
IN: IntoIterator<Item = N>,
{
}