use crate::IndexGraphView;
use std::collections::VecDeque;
#[must_use]
pub fn topological_sort<G>(graph: &G) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
{
sort_with_filter(graph, &|_| true)
}
#[must_use]
pub fn topological_sort_filtered<G, F>(graph: &G, allows_edge: F) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
F: Fn(G::Edge) -> bool,
{
sort_with_filter(graph, &allows_edge)
}
#[must_use]
pub fn has_cycle<G>(graph: &G) -> bool
where
G: IndexGraphView,
{
topological_sort(graph).is_none()
}
#[must_use]
pub fn has_cycle_filtered<G, F>(graph: &G, allows_edge: F) -> bool
where
G: IndexGraphView,
F: Fn(G::Edge) -> bool,
{
topological_sort_filtered(graph, allows_edge).is_none()
}
#[must_use]
pub fn find_cycle<G>(graph: &G) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
{
find_with_filter(graph, &|_| true)
}
#[must_use]
pub fn find_cycle_filtered<G, F>(graph: &G, allows_edge: F) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
F: Fn(G::Edge) -> bool,
{
find_with_filter(graph, &allows_edge)
}
fn sort_with_filter<G, F>(graph: &G, allows_edge: &F) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
F: Fn(G::Edge) -> bool,
{
let mut indegree = vec![0_usize; graph.node_bound()];
for (edge, endpoints) in graph.edge_references() {
if !allows_edge(edge) {
continue;
}
if let Some(degree) = indegree.get_mut(G::node_slot(endpoints.target())) {
*degree += 1;
}
}
let mut ready = graph
.node_indices()
.filter(|node| indegree[G::node_slot(*node)] == 0)
.collect::<VecDeque<_>>();
let mut order = Vec::with_capacity(graph.node_count());
while let Some(node) = ready.pop_front() {
order.push(node);
for edge in graph.outgoing_edges(node) {
if !allows_edge(edge) {
continue;
}
let Some(endpoints) = graph.edge_endpoints(edge) else {
continue;
};
let Some(degree) = indegree.get_mut(G::node_slot(endpoints.target())) else {
continue;
};
*degree = degree.saturating_sub(1);
if *degree == 0 {
ready.push_back(endpoints.target());
}
}
}
(order.len() == graph.node_count()).then_some(order)
}
fn find_with_filter<G, F>(graph: &G, allows_edge: &F) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
F: Fn(G::Edge) -> bool,
{
let mut adjacency = vec![Vec::new(); graph.node_bound()];
for (edge, endpoints) in graph.edge_references() {
if !allows_edge(edge) {
continue;
}
if let Some(neighbors) = adjacency.get_mut(G::node_slot(endpoints.source())) {
neighbors.push(endpoints.target());
}
}
let mut color = vec![0_u8; graph.node_bound()];
let mut parent = vec![None; graph.node_bound()];
for start in graph.node_indices() {
if color[G::node_slot(start)] != 0 {
continue;
}
color[G::node_slot(start)] = 1;
let mut stack = vec![(start, 0_usize)];
while let Some((node, next)) = stack.last_mut() {
let slot = G::node_slot(*node);
let Some(&target) = adjacency[slot].get(*next) else {
color[slot] = 2;
stack.pop();
continue;
};
*next += 1;
let target_slot = G::node_slot(target);
if color[target_slot] == 0 {
parent[target_slot] = Some(*node);
color[target_slot] = 1;
stack.push((target, 0));
} else if color[target_slot] == 1 {
return reconstruct_cycle::<G>(*node, target, &parent);
}
}
}
None
}
fn reconstruct_cycle<G>(
node: G::Node,
target: G::Node,
parent: &[Option<G::Node>],
) -> Option<Vec<G::Node>>
where
G: IndexGraphView,
{
let mut cycle = vec![node];
let mut cursor = node;
while cursor != target {
cursor = parent[G::node_slot(cursor)]?;
cycle.push(cursor);
}
cycle.reverse();
cycle.push(target);
Some(cycle)
}