extern crate alloc;
use alloc::vec::Vec;
struct Frame<Id> {
id: Id,
expanded: bool,
}
pub fn post_order<Id, FC, FV, FVis>(
root: Id,
mut children_of: FC,
mut is_visited: FV,
mut visit: FVis,
) where
Id: Copy,
FC: FnMut(Id, &mut dyn FnMut(Id)),
FV: FnMut(Id) -> bool,
FVis: FnMut(Id),
{
let mut stack: Vec<Frame<Id>> = Vec::with_capacity(64);
stack.push(Frame {
id: root,
expanded: false,
});
while let Some(top) = stack.last_mut() {
if top.expanded {
let id = top.id;
stack.pop();
if !is_visited(id) {
visit(id);
}
continue;
}
if is_visited(top.id) {
stack.pop();
continue;
}
top.expanded = true;
let parent_id = top.id;
let mut buf: Vec<Id> = Vec::new();
children_of(parent_id, &mut |c| buf.push(c));
for child in buf.into_iter().rev() {
stack.push(Frame {
id: child,
expanded: false,
});
}
}
}
pub fn pre_order<Id, FC, FV, FVis>(
root: Id,
mut children_of: FC,
mut is_visited: FV,
mut visit: FVis,
) where
Id: Copy,
FC: FnMut(Id, &mut dyn FnMut(Id)),
FV: FnMut(Id) -> bool,
FVis: FnMut(Id) -> bool,
{
let mut stack: Vec<Id> = Vec::with_capacity(64);
stack.push(root);
while let Some(id) = stack.pop() {
if is_visited(id) {
continue;
}
if !visit(id) {
continue;
}
let mut buf: Vec<Id> = Vec::new();
children_of(id, &mut |c| buf.push(c));
for child in buf.into_iter().rev() {
stack.push(child);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::collections::BTreeSet;
use core::cell::RefCell;
fn tree() -> Vec<Vec<u32>> {
alloc::vec![
alloc::vec![1, 2],
alloc::vec![3, 4],
alloc::vec![],
alloc::vec![],
alloc::vec![],
]
}
fn dag() -> Vec<Vec<u32>> {
alloc::vec![
alloc::vec![1, 2],
alloc::vec![3],
alloc::vec![3],
alloc::vec![],
]
}
#[test]
fn post_order_tree() {
let edges = tree();
let mut out: Vec<u32> = Vec::new();
post_order(
0u32,
|id, push| {
if let Some(cs) = edges.get(id as usize) {
for &c in cs {
push(c);
}
}
},
|_| false,
|id| out.push(id),
);
assert_eq!(out, alloc::vec![3, 4, 1, 2, 0]);
}
#[test]
fn pre_order_tree() {
let edges = tree();
let mut out: Vec<u32> = Vec::new();
pre_order(
0u32,
|id, push| {
if let Some(cs) = edges.get(id as usize) {
for &c in cs {
push(c);
}
}
},
|_| false,
|id| {
out.push(id);
true
},
);
assert_eq!(out, alloc::vec![0, 1, 3, 4, 2]);
}
#[test]
fn post_order_dag_dedups_shared_child() {
let edges = dag();
let out: RefCell<Vec<u32>> = RefCell::new(Vec::new());
let seen: RefCell<BTreeSet<u32>> = RefCell::new(BTreeSet::new());
post_order(
0u32,
|id, push| {
if let Some(cs) = edges.get(id as usize) {
for &c in cs {
push(c);
}
}
},
|id| seen.borrow().contains(&id),
|id| {
seen.borrow_mut().insert(id);
out.borrow_mut().push(id);
},
);
let out = out.into_inner();
assert_eq!(out.iter().filter(|&&x| x == 3).count(), 1);
let pos = |v: u32| out.iter().position(|&x| x == v).expect("node missing");
assert!(pos(3) < pos(1));
assert!(pos(1) < pos(0));
}
#[test]
fn pre_order_prune() {
let edges = tree();
let mut out: Vec<u32> = Vec::new();
pre_order(
0u32,
|id, push| {
if let Some(cs) = edges.get(id as usize) {
for &c in cs {
push(c);
}
}
},
|_| false,
|id| {
out.push(id);
id != 1
},
);
assert_eq!(out, alloc::vec![0, 1, 2]);
}
#[test]
fn deep_chain_no_overflow() {
const N: u32 = 100_000;
let mut count = 0usize;
post_order(
0u32,
|id, push| {
if id < N {
push(id + 1);
}
},
|_| false,
|_| {
count += 1;
},
);
assert_eq!(count, (N + 1) as usize);
}
}