use std::collections::BTreeSet;
use super::graph::LayoutGraph;
pub fn run(graph: &mut LayoutGraph) {
let n = graph.node_ids.len();
if n == 0 {
return;
}
let mut adj: Vec<Vec<(usize, usize)>> = vec![Vec::new(); n];
for (edge_idx, &(from, to, _)) in graph.edges.iter().enumerate() {
adj[from].push((edge_idx, to));
}
let mut visited = vec![false; n];
let mut in_stack = vec![false; n]; let mut back_edges: BTreeSet<usize> = BTreeSet::new();
for start in 0..n {
if !visited[start] {
dfs_find_back_edges(start, &adj, &mut visited, &mut in_stack, &mut back_edges);
}
}
graph.reversed_edges = back_edges;
}
fn dfs_find_back_edges(
node: usize,
adj: &[Vec<(usize, usize)>],
visited: &mut [bool],
in_stack: &mut [bool],
back_edges: &mut BTreeSet<usize>,
) {
visited[node] = true;
in_stack[node] = true;
for &(edge_idx, target) in &adj[node] {
if !visited[target] {
dfs_find_back_edges(target, adj, visited, in_stack, back_edges);
} else if in_stack[target] {
back_edges.insert(edge_idx);
}
}
in_stack[node] = false;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engines::graph::algorithms::layered::graph::DiGraph;
#[test]
fn test_acyclic_no_cycle() {
let mut graph: DiGraph<()> = DiGraph::new();
graph.add_node("A", ());
graph.add_node("B", ());
graph.add_node("C", ());
graph.add_edge("A", "B");
graph.add_edge("B", "C");
let mut lg = LayoutGraph::from_digraph(&graph, |_, _| (10.0, 10.0));
run(&mut lg);
assert!(lg.reversed_edges.is_empty());
}
#[test]
fn test_acyclic_simple_cycle() {
let mut graph: DiGraph<()> = DiGraph::new();
graph.add_node("A", ());
graph.add_node("B", ());
graph.add_edge("A", "B");
graph.add_edge("B", "A");
let mut lg = LayoutGraph::from_digraph(&graph, |_, _| (10.0, 10.0));
run(&mut lg);
assert_eq!(lg.reversed_edges.len(), 1);
}
#[test]
fn test_acyclic_triangle_cycle() {
let mut graph: DiGraph<()> = DiGraph::new();
graph.add_node("A", ());
graph.add_node("B", ());
graph.add_node("C", ());
graph.add_edge("A", "B");
graph.add_edge("B", "C");
graph.add_edge("C", "A");
let mut lg = LayoutGraph::from_digraph(&graph, |_, _| (10.0, 10.0));
run(&mut lg);
assert_eq!(lg.reversed_edges.len(), 1);
}
#[test]
fn test_acyclic_self_loop() {
let mut graph: DiGraph<()> = DiGraph::new();
graph.add_node("A", ());
graph.add_edge("A", "A");
let mut lg = LayoutGraph::from_digraph(&graph, |_, _| (10.0, 10.0));
run(&mut lg);
assert_eq!(lg.reversed_edges.len(), 1);
}
#[test]
fn test_acyclic_disconnected() {
let mut graph: DiGraph<()> = DiGraph::new();
graph.add_node("A", ());
graph.add_node("B", ());
graph.add_node("C", ());
graph.add_node("D", ());
graph.add_edge("A", "B");
graph.add_edge("C", "D");
let mut lg = LayoutGraph::from_digraph(&graph, |_, _| (10.0, 10.0));
run(&mut lg);
assert!(lg.reversed_edges.is_empty());
}
}