use std::{collections::HashSet, hash::Hash, rc::Rc};
pub trait Vertex: Hash + Eq {
fn adjacents(self: &Rc<Self>) -> Vec<Rc<Self>>;
}
pub fn topological_sort<V: Vertex>(vertices: impl Iterator<Item = Rc<V>>) -> Vec<Rc<V>> {
let mut visited = HashSet::new();
let mut stack = Vec::new();
for vertex in vertices {
if visited.contains(&vertex) {
continue;
}
sort_impl(&vertex, &mut visited, &mut stack);
}
stack.reverse();
stack
}
fn sort_impl<V: Vertex>(vertex: &Rc<V>, visited: &mut HashSet<Rc<V>>, stack: &mut Vec<Rc<V>>) {
visited.insert(Rc::clone(vertex));
for adjacent_vertex in vertex.adjacents() {
if visited.contains(&adjacent_vertex) {
continue;
}
sort_impl(&adjacent_vertex, visited, stack)
}
stack.push(Rc::clone(vertex))
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Hash, PartialEq, Eq, Debug)]
struct TestVertex {
name: &'static str,
adjacents: Vec<Rc<TestVertex>>,
}
impl TestVertex {
fn new(name: &'static str, others: &[&Rc<TestVertex>]) -> Rc<TestVertex> {
Rc::new(TestVertex {
name,
adjacents: others.iter().map(|v| Rc::clone(v)).collect(),
})
}
}
impl Vertex for TestVertex {
fn adjacents(self: &Rc<Self>) -> Vec<Rc<TestVertex>> {
self.adjacents.iter().map(Rc::clone).collect()
}
}
#[test]
#[allow(clippy::many_single_char_names)]
fn test_sorting_works() {
let a = TestVertex::new("a", &[]);
let b = TestVertex::new("b", &[]);
let c = TestVertex::new("c", &[&a, &b]);
let d = TestVertex::new("d", &[&c]);
let e = TestVertex::new("e", &[&d]);
let array = [&a, &b, &c, &d, &e];
let list = array.iter().map(|v| Rc::clone(v));
assert_eq!(topological_sort(list), vec![e, d, c, b, a]);
}
}