use core::{fmt::Debug, marker::PhantomData};
use daggy::Walker;
use petgraph::visit::GraphBase;
use std::collections::VecDeque;
#[derive(Clone, Debug)]
pub struct Recurse<G, F, T>
where
G: GraphBase,
{
queue: VecDeque<(G::NodeId, T)>,
recursive_fn: F,
pub depth_first: bool,
_graph: PhantomData<G>,
}
impl<G, F, T> Walker<&G> for Recurse<G, F, T>
where
G: GraphBase,
F: FnMut(&G, G::NodeId, &T) -> Option<Vec<(G::NodeId, T)>>,
{
type Item = (G::NodeId, T);
#[inline]
fn walk_next(&mut self, g: &G) -> Option<Self::Item> {
self.next(g)
}
}
impl<G, F, T> Recurse<G, F, T>
where
G: GraphBase,
F: FnMut(&G, G::NodeId, &T) -> Option<Vec<(G::NodeId, T)>>,
{
pub fn new(start: impl IntoIterator<Item = G::NodeId>, recursive_fn: F) -> Self
where
T: Default,
{
Self::new_seeded(start.into_iter().map(|n| (n, T::default())), recursive_fn)
}
pub fn new_seeded(start: impl IntoIterator<Item = (G::NodeId, T)>, recursive_fn: F) -> Self {
Recurse {
queue: start.into_iter().collect(),
recursive_fn,
depth_first: false,
_graph: PhantomData,
}
}
pub fn next(&mut self, g: &G) -> Option<(G::NodeId, T)> {
let Recurse {
ref mut queue,
ref mut recursive_fn,
depth_first,
..
} = *self;
loop {
let (current, path) = match depth_first {
false => queue.pop_front()?,
true => queue.pop_back()?,
};
let Some(accepted) = recursive_fn(g, current, &path) else {
continue;
};
for (node, path) in accepted {
queue.push_back((node, path));
}
break Some((current, path));
}
}
pub fn depth_first(mut self) -> Self {
self.depth_first = true;
self
}
}
#[test]
fn deps() {
use crate::graph::linked::Stack;
use daggy::Dag;
use petgraph::prelude::EdgeIndex;
let mut dag = Dag::new();
let a: petgraph::prelude::NodeIndex<usize> = dag.add_node("a");
let (eb, b) = dag.add_child(a, (), "b");
let (ec, c) = dag.add_child(b, (), "c");
let mut walk = Recurse::new(
vec![c],
|g: &Dag<&str, (), usize>, n, state: &Stack<EdgeIndex<usize>>| {
use petgraph::visit::EdgeRef as _;
use petgraph::visit::IntoEdgesDirected as _;
println!("{n:?}");
Some(
g.edges_directed(n, petgraph::Direction::Incoming)
.map(|e| (e.source(), state + e.id()))
.collect(),
)
},
);
let (n, p) = walk.next(&dag).expect("c");
assert!(p.iter().collect::<Vec<_>>().is_empty());
assert_eq!(dag[n], "c");
let (n, p) = walk.next(&dag).expect("b");
assert_eq!(dag[n], "b");
assert_eq!(p.iter().collect::<Vec<_>>(), vec![&ec]);
let (n, p) = walk.next(&dag).expect("a");
assert_eq!(dag[n], "a");
assert_eq!(p.iter().collect::<Vec<_>>(), vec![&eb, &ec]);
assert!(walk.next(&dag).is_none());
}
#[test]
fn deps_conditional() {
use crate::graph::linked::Stack;
use daggy::Dag;
use petgraph::prelude::EdgeIndex;
let mut dag = Dag::new();
let a: petgraph::prelude::NodeIndex<usize> = dag.add_node(Some(0));
let (_, b) = dag.add_child(a, true, Some(1));
let (_, c) = dag.add_child(b, false, None);
dag.add_parent(a, false, Some(2));
dag.add_parent(b, false, None);
dag.add_parent(c, false, Some(0));
let mut walk = Recurse::new(
vec![c],
|g: &Dag<Option<usize>, bool, usize>, n, state: &Stack<EdgeIndex<usize>>| {
use petgraph::visit::EdgeRef as _;
use petgraph::visit::IntoEdgesDirected as _;
println!("{n:?}");
let is_goal = state.iter().next().is_none();
let is_completed = g[n].is_some();
if is_goal && is_completed {
return None;
}
let mut waiting = false;
let mut due = false;
let mut require = vec![];
for e in g.edges_directed(n, petgraph::Direction::Incoming) {
let passed = g[e.source()] == Some(0);
let trigger = *e.weight();
if trigger && passed {
due = true;
break;
}
if !trigger && !passed {
waiting = true;
}
if passed {
continue;
}
require.push((e.source(), state + e.id()));
}
if due || !waiting {
Some(vec![])
} else {
Some(require)
}
},
);
let mut seq = vec![];
while let Some(x) = walk.next(&dag) {
seq.push(x);
}
insta::assert_debug_snapshot!(seq);
}