use alloc::{collections::VecDeque, vec, vec::Vec};
use oxgraph_topology::{ElementIndex, ElementSuccessors, TopologyBase};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ToposortError {
Cycle,
}
impl core::fmt::Display for ToposortError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Cycle => formatter.write_str("topological sort found a cycle"),
}
}
}
impl core::error::Error for ToposortError {}
pub fn topological_sort<G>(
graph: &G,
elements: &[<G as TopologyBase>::ElementId],
) -> Result<Vec<<G as TopologyBase>::ElementId>, ToposortError>
where
G: ElementIndex + ElementSuccessors,
{
let bound = graph.element_bound();
let mut member = vec![false; bound];
let mut nodes: Vec<G::ElementId> = Vec::new();
for &element in elements {
let index = graph.element_index(element);
if index < bound && !member[index] {
member[index] = true;
nodes.push(element);
}
}
let mut indegree = vec![0usize; bound];
for &node in &nodes {
for successor in graph.element_successors(node) {
let target = graph.element_index(successor);
if target < bound && member[target] {
indegree[target] += 1;
}
}
}
let mut queue: VecDeque<G::ElementId> = VecDeque::new();
for &node in &nodes {
if indegree[graph.element_index(node)] == 0 {
queue.push_back(node);
}
}
let mut order = Vec::with_capacity(nodes.len());
while let Some(node) = queue.pop_front() {
order.push(node);
for successor in graph.element_successors(node) {
let target = graph.element_index(successor);
if target >= bound || !member[target] {
continue;
}
indegree[target] -= 1;
if indegree[target] == 0 {
queue.push_back(successor);
}
}
}
if order.len() == nodes.len() {
Ok(order)
} else {
Err(ToposortError::Cycle)
}
}