use crate::pattern::Pattern;
pub fn unfold<A, V>(expand: impl Fn(A) -> (V, Vec<A>), seed: A) -> Pattern<V> {
enum Work<A, V> {
Expand(A),
Collect(V, usize),
}
let mut work_stack: Vec<Work<A, V>> = vec![Work::Expand(seed)];
let mut result_stack: Vec<Pattern<V>> = Vec::new();
while let Some(item) = work_stack.pop() {
match item {
Work::Expand(s) => {
let (value, children) = expand(s);
let n = children.len();
work_stack.push(Work::Collect(value, n));
for child in children.into_iter().rev() {
work_stack.push(Work::Expand(child));
}
}
Work::Collect(value, n) => {
let start = result_stack.len() - n;
let elements: Vec<Pattern<V>> = result_stack.drain(start..).collect();
result_stack.push(Pattern { value, elements });
}
}
}
result_stack
.pop()
.expect("unfold: result stack should have exactly one element")
}