use core::ops::ControlFlow;
use smallvec::SmallVec;
use super::Graph;
use crate::adt::SmallSet;
#[allow(unused_variables)]
pub trait GraphVisitor {
type Node;
fn on_node_reached(&mut self, from: Option<&Self::Node>, node: &Self::Node) -> ControlFlow<()> {
ControlFlow::Continue(())
}
fn on_block_visited(&mut self, node: &Self::Node) {}
}
pub struct DefaultGraphVisitor<T>(core::marker::PhantomData<T>);
impl<T> Default for DefaultGraphVisitor<T> {
fn default() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T> GraphVisitor for DefaultGraphVisitor<T> {
type Node = T;
}
#[repr(transparent)]
pub struct PreOrderIter<G>(LazyDfsVisitor<G, DefaultGraphVisitor<<G as Graph>::Node>>)
where
G: Graph;
impl<G> PreOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
pub fn new(root: <G as Graph>::Node) -> Self {
Self(LazyDfsVisitor::new(root, DefaultGraphVisitor::default()))
}
pub fn new_with_visited(
root: <G as Graph>::Node,
visited: impl IntoIterator<Item = <G as Graph>::Node>,
) -> Self {
Self(LazyDfsVisitor::new_with_visited(root, DefaultGraphVisitor::default(), visited))
}
}
impl<G> core::iter::FusedIterator for PreOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
}
impl<G> Iterator for PreOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
type Item = <G as Graph>::Node;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next::<false>()
}
}
#[repr(transparent)]
pub struct PostOrderIter<G>(LazyDfsVisitor<G, DefaultGraphVisitor<<G as Graph>::Node>>)
where
G: Graph;
impl<G> PostOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
#[inline]
pub fn new(root: <G as Graph>::Node) -> Self {
Self(LazyDfsVisitor::new(root, DefaultGraphVisitor::default()))
}
pub fn new_with_visited(
root: <G as Graph>::Node,
visited: impl IntoIterator<Item = <G as Graph>::Node>,
) -> Self {
Self(LazyDfsVisitor::new_with_visited(root, DefaultGraphVisitor::default(), visited))
}
}
impl<G> core::iter::FusedIterator for PostOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
}
impl<G> Iterator for PostOrderIter<G>
where
G: Graph,
<G as Graph>::Node: Eq,
{
type Item = <G as Graph>::Node;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next::<true>()
}
}
pub struct LazyDfsVisitor<G: Graph, V> {
visited: SmallSet<<G as Graph>::Node, 32>,
stack: SmallVec<[VisitNode<<G as Graph>::Node>; 8]>,
visitor: V,
}
struct VisitNode<T> {
parent: Option<T>,
node: T,
successors: SmallVec<[T; 2]>,
reached: bool,
}
impl<T> VisitNode<T>
where
T: Clone,
{
#[inline]
pub fn node(&self) -> T {
self.node.clone()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.successors.is_empty()
}
pub fn pop_successor(&mut self) -> T {
self.successors.pop().unwrap()
}
}
impl<G, V> LazyDfsVisitor<G, V>
where
G: Graph,
<G as Graph>::Node: Eq,
V: GraphVisitor<Node = <G as Graph>::Node>,
{
pub fn new(from: <G as Graph>::Node, visitor: V) -> Self {
Self::new_with_visited(from, visitor, None::<<G as Graph>::Node>)
}
pub fn new_with_visited(
from: <G as Graph>::Node,
visitor: V,
visited: impl IntoIterator<Item = <G as Graph>::Node>,
) -> Self {
use smallvec::smallvec;
let visited = visited.into_iter().collect::<SmallSet<_, 32>>();
if visited.contains(&from) {
return Self {
visited,
stack: smallvec![],
visitor,
};
}
let successors = SmallVec::from_iter(G::children(from.clone()));
Self {
visited,
stack: smallvec![VisitNode {
parent: None,
node: from,
successors,
reached: false,
}],
visitor,
}
}
#[allow(clippy::should_implement_trait)]
pub fn next<const POSTORDER: bool>(&mut self) -> Option<<G as Graph>::Node> {
loop {
let Some(node) = self.stack.last_mut() else {
break None;
};
if !node.reached {
node.reached = true;
let unvisited = self.visited.insert(node.node());
if !unvisited {
let _ = unsafe { self.stack.pop().unwrap_unchecked() };
continue;
}
let should_visit =
self.visitor.on_node_reached(node.parent.as_ref(), &node.node).is_continue();
if !should_visit {
let _ = unsafe { self.stack.pop().unwrap_unchecked() };
continue;
}
if POSTORDER {
continue;
} else {
break Some(node.node.clone());
}
}
if node.is_empty() {
let node = unsafe { self.stack.pop().unwrap_unchecked() };
self.visitor.on_block_visited(&node.node);
if POSTORDER {
break Some(node.node);
} else {
continue;
}
}
let parent = node.node();
let successor = node.pop_successor();
let successors = SmallVec::from_iter(G::children(successor.clone()));
self.stack.push(VisitNode {
parent: Some(parent),
node: successor,
successors,
reached: false,
});
}
}
}