use num_traits::{Float, Zero};
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::hash::Hash;
use crate::error::{GraphError, Result};
use crate::graph::Graph;
struct TarjanState<V> {
index: usize,
indices: HashMap<V, usize>,
lowlinks: HashMap<V, usize>,
stack: VecDeque<V>,
on_stack: HashSet<V>,
components: Vec<Vec<V>>,
}
impl<V: Hash + Eq + Copy> TarjanState<V> {
fn new() -> Self {
Self {
index: 0,
indices: HashMap::new(),
lowlinks: HashMap::new(),
stack: VecDeque::new(),
on_stack: HashSet::new(),
components: Vec::new(),
}
}
fn strong_connect<W>(&mut self, v: V, graph: &Graph<V, W>) -> Result<()>
where
V: Debug,
W: Float + Zero + Copy + Debug,
{
self.indices.insert(v, self.index);
self.lowlinks.insert(v, self.index);
self.index += 1;
self.stack.push_back(v);
self.on_stack.insert(v);
if let Ok(neighbors) = graph.neighbors(&v) {
for (w, _) in neighbors {
if !self.indices.contains_key(w) {
self.strong_connect(*w, graph)?;
if let (Some(&v_lowlink), Some(&w_lowlink)) =
(self.lowlinks.get(&v), self.lowlinks.get(w))
{
self.lowlinks.insert(v, v_lowlink.min(w_lowlink));
}
} else if self.on_stack.contains(w) {
if let (Some(&v_lowlink), Some(&w_index)) =
(self.lowlinks.get(&v), self.indices.get(w))
{
self.lowlinks.insert(v, v_lowlink.min(w_index));
}
}
}
}
if let (Some(&v_lowlink), Some(&v_index)) = (self.lowlinks.get(&v), self.indices.get(&v)) {
if v_lowlink == v_index {
let mut component = Vec::new();
while let Some(w) = self.stack.pop_back() {
self.on_stack.remove(&w);
component.push(w);
if w == v {
break;
}
}
self.components.push(component);
}
}
Ok(())
}
}
pub fn strongly_connected_components<V, W>(graph: &Graph<V, W>) -> Result<Vec<Vec<V>>>
where
V: Hash + Eq + Copy + Debug,
W: Float + Zero + Copy + Debug,
{
if !graph.is_directed() {
return Err(GraphError::invalid_input(
"Tarjan's algorithm requires a directed graph",
));
}
let mut state = TarjanState::new();
for &v in graph.vertices() {
if !state.indices.contains_key(&v) {
state.strong_connect(v, graph)?;
}
}
Ok(state.components)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tarjan_simple_cycle() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 1, 1.0);
graph.add_edge(1, 2, 1.0);
graph.add_edge(2, 0, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 1);
assert_eq!(components[0].len(), 3);
}
#[test]
fn test_tarjan_multiple_components() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 1, 1.0);
graph.add_edge(1, 2, 1.0);
graph.add_edge(2, 0, 1.0);
graph.add_edge(2, 3, 1.0);
graph.add_edge(3, 4, 1.0);
graph.add_edge(4, 3, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 2);
assert_eq!(components[0].len(), 2); assert_eq!(components[1].len(), 3); }
#[test]
fn test_tarjan_single_vertex() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_vertex(0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 1);
assert_eq!(components[0], vec![0]);
}
#[test]
fn test_tarjan_no_cycles() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 1, 1.0);
graph.add_edge(1, 2, 1.0);
graph.add_edge(2, 3, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 4);
for component in components {
assert_eq!(component.len(), 1);
}
}
#[test]
fn test_tarjan_self_loop() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 0, 1.0);
graph.add_edge(0, 1, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 2);
assert!(components.iter().any(|c| c == &vec![0]));
assert!(components.iter().any(|c| c == &vec![1]));
}
#[test]
fn test_tarjan_undirected_graph() {
let mut graph = Graph::new_undirected();
graph.add_edge(0, 1, 1.0);
assert!(matches!(
strongly_connected_components(&graph),
Err(GraphError::InvalidInput(_))
));
}
#[test]
fn test_tarjan_empty_graph() {
let graph: Graph<i32, f64> = Graph::new();
let components = strongly_connected_components(&graph).unwrap();
assert!(components.is_empty());
}
#[test]
fn test_tarjan_complex_graph() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 1, 1.0);
graph.add_edge(1, 2, 1.0);
graph.add_edge(2, 0, 1.0);
graph.add_edge(3, 4, 1.0);
graph.add_edge(4, 5, 1.0);
graph.add_edge(5, 3, 1.0);
graph.add_edge(2, 3, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 2);
assert_eq!(components[0].len(), 3); assert_eq!(components[1].len(), 3); }
#[test]
fn test_tarjan_disconnected_components() {
let mut graph: Graph<i32, f64> = Graph::new();
graph.add_edge(0, 1, 1.0);
graph.add_edge(1, 0, 1.0);
graph.add_edge(2, 3, 1.0);
graph.add_edge(3, 2, 1.0);
let components = strongly_connected_components(&graph).unwrap();
assert_eq!(components.len(), 2);
assert_eq!(components[0].len(), 2);
assert_eq!(components[1].len(), 2);
}
}