use crate::program::StatementIdx;
#[derive(Clone, Debug)]
enum TopologicalOrderStatus {
NotStarted,
InProgress,
Done,
}
pub fn reverse_topological_ordering<
E,
Children: IntoIterator<Item = StatementIdx>,
GetChildren: Fn(StatementIdx) -> Result<Children, E>,
>(
detect_cycles: bool,
roots: impl Iterator<Item = StatementIdx>,
node_count: usize,
get_children: GetChildren,
cycle_err: impl Fn(StatementIdx) -> E,
) -> Result<Vec<StatementIdx>, E> {
let mut ordering = vec![];
let mut status = vec![TopologicalOrderStatus::NotStarted; node_count];
for root in roots {
calculate_reverse_topological_ordering(
detect_cycles,
&mut ordering,
&mut status,
root,
&get_children,
&cycle_err,
)?;
}
Ok(ordering)
}
fn calculate_reverse_topological_ordering<
E,
Children: IntoIterator<Item = StatementIdx>,
GetChildren: Fn(StatementIdx) -> Result<Children, E>,
>(
detect_cycles: bool,
ordering: &mut Vec<StatementIdx>,
status: &mut [TopologicalOrderStatus],
root: StatementIdx,
get_children: &GetChildren,
cycle_err: &impl Fn(StatementIdx) -> E,
) -> Result<(), E> {
let mut stack = vec![root];
while let Some(idx) = stack.pop() {
match status[idx.0] {
TopologicalOrderStatus::NotStarted => {
status[idx.0] = TopologicalOrderStatus::InProgress;
stack.push(idx);
for child in get_children(idx)? {
match status[child.0] {
TopologicalOrderStatus::InProgress if detect_cycles => {
return Err(cycle_err(child));
}
TopologicalOrderStatus::NotStarted => stack.push(child),
TopologicalOrderStatus::Done | TopologicalOrderStatus::InProgress => {}
}
}
}
TopologicalOrderStatus::InProgress => {
status[idx.0] = TopologicalOrderStatus::Done;
ordering.push(idx);
}
TopologicalOrderStatus::Done => {}
}
}
Ok(())
}