use std::collections::HashSet;
use std::hash::Hash;
use std::iter::FusedIterator;
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct DftCycles<'a, T, F, I>
where
T: ?Sized,
F: FnMut(&'a T) -> I,
I: Iterator<Item = &'a T>,
T: Eq + Hash,
{
queue: Vec<(usize, &'a T)>,
path: Vec<&'a T>,
visited: HashSet<&'a T>,
iter_connections: F,
}
impl<'a, T, F, I> DftCycles<'a, T, F, I>
where
T: ?Sized,
F: FnMut(&'a T) -> I,
I: Iterator<Item = &'a T>,
T: Eq + Hash,
{
#[inline]
pub fn new(root: &'a T, iter_connections: F) -> Self {
Self {
queue: vec![(0, root)],
path: Vec::new(),
visited: HashSet::new(),
iter_connections,
}
}
}
impl<'a, T, F, I> Iterator for DftCycles<'a, T, F, I>
where
T: ?Sized,
F: FnMut(&'a T) -> I,
I: Iterator<Item = &'a T>,
T: Eq + Hash,
{
type Item = Vec<&'a T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
while let Some((depth, node)) = self.queue.pop() {
if depth < self.path.len() {
let (path, visited) = (&mut self.path, &mut self.visited);
path.drain(depth..).for_each(|node| {
visited.remove(node);
});
}
if !self.visited.insert(node) {
return Some(self.path.clone());
}
self.path.push(node);
let children = (self.iter_connections)(node);
let children = children.collect::<Vec<_>>();
let children = children.into_iter().rev();
self.queue.extend(children.map(|child| (depth + 1, child)));
}
None
}
}
impl<'a, T, F, I> FusedIterator for DftCycles<'a, T, F, I>
where
T: ?Sized,
F: FnMut(&'a T) -> I,
I: Iterator<Item = &'a T>,
T: Eq + Hash,
{
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(PartialEq, Eq, Hash)]
struct Vertex(&'static str, Vec<usize>);
#[test]
fn dft_cycles() {
let graph = vec![
Vertex("A", vec![1, 3]), Vertex("B", vec![2, 3]), Vertex("C", vec![0, 3]), Vertex("D", vec![0]), ];
let start = &graph[0];
let cycles = DftCycles::new(start, |vertex| vertex.1.iter().map(|&i| &graph[i]));
let mut cycles = cycles.map(|path| path.iter().map(|vertex| vertex.0).collect::<Vec<_>>());
assert_eq!(cycles.next(), Some(vec!["A", "B", "C"]));
assert_eq!(cycles.next(), Some(vec!["A", "B", "C", "D"]));
assert_eq!(cycles.next(), Some(vec!["A", "B", "D"]));
assert_eq!(cycles.next(), Some(vec!["A", "D"]));
assert_eq!(cycles.next(), None);
}
}