use super::AlgorithmError;
use crate::containers::queues::Deque;
use crate::graph::Graph;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TarjanState {
pub index: usize,
pub lowlink: usize,
pub on_stack: bool,
pub component_offset: usize,
}
impl Default for TarjanState {
fn default() -> Self {
Self {
index: usize::MAX, lowlink: usize::MAX,
on_stack: false,
component_offset: 0,
}
}
}
pub fn tarjan_scc<'a, G, S>(
graph: &G,
state: &mut [TarjanState],
mut stack: S,
components: &mut [(&'a [usize], usize)],
node_buffer: &'a mut [usize],
) -> Result<usize, AlgorithmError<usize>>
where
G: Graph<usize>,
S: Deque<usize>,
AlgorithmError<usize>: From<G::Error>,
{
let mut index_counter = 0;
let mut component_count = 0;
let mut buffer_offset = 0;
for node_idx in graph.iter_nodes()? {
if node_idx >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(node_idx),
));
}
if state[node_idx].index == usize::MAX {
state[node_idx].component_offset = component_count;
let (_new_index, new_components, new_buffer_offset) = tarjan_dfs(
graph,
node_idx,
state,
&mut stack,
&mut index_counter,
&mut node_buffer[buffer_offset..],
components,
)?;
component_count += new_components;
buffer_offset += new_buffer_offset;
if component_count > components.len() {
return Err(AlgorithmError::ResultCapacityExceeded);
}
}
}
let mut current_offset = 0;
for component in components.iter_mut().take(component_count) {
let component_size = component.1;
let component_slice = &node_buffer[current_offset..current_offset + component_size];
*component = (component_slice, component_size);
current_offset += component_size;
}
Ok(component_count)
}
fn tarjan_dfs<G, S>(
graph: &G,
node: usize,
state: &mut [TarjanState],
stack: &mut S,
index_counter: &mut usize,
node_buffer: &mut [usize],
components: &mut [(&[usize], usize)],
) -> Result<(usize, usize, usize), AlgorithmError<usize>>
where
G: Graph<usize>,
S: Deque<usize>,
AlgorithmError<usize>: From<G::Error>,
{
if node >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(node),
));
}
state[node].index = *index_counter;
state[node].lowlink = *index_counter;
state[node].on_stack = true;
*index_counter += 1;
stack
.push_back(node)
.map_err(|_| AlgorithmError::StackCapacityExceeded)?;
let mut component_count = 0;
let mut buffer_offset = 0;
for neighbor_idx in graph.outgoing_edges(node)? {
if neighbor_idx >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(neighbor_idx),
));
}
if state[neighbor_idx].index == usize::MAX {
state[neighbor_idx].component_offset = state[node].component_offset + component_count;
let (_new_index, new_components, new_buffer_offset) = tarjan_dfs(
graph,
neighbor_idx,
state,
stack,
index_counter,
&mut node_buffer[buffer_offset..],
components,
)?;
component_count += new_components;
buffer_offset += new_buffer_offset;
state[node].lowlink = state[node].lowlink.min(state[neighbor_idx].lowlink);
} else if state[neighbor_idx].on_stack {
state[node].lowlink = state[node].lowlink.min(state[neighbor_idx].index);
}
}
if state[node].lowlink == state[node].index {
let mut component_size = 0;
loop {
let Some(stack_node) = stack.pop_back() else {
return Err(AlgorithmError::InvalidState);
};
state[stack_node].on_stack = false;
if buffer_offset + component_size >= node_buffer.len() {
return Err(AlgorithmError::ResultCapacityExceeded);
}
node_buffer[buffer_offset + component_size] = stack_node;
component_size += 1;
if stack_node == node {
break;
}
}
let component_index = state[node].component_offset + component_count;
if component_index >= components.len() {
return Err(AlgorithmError::ResultCapacityExceeded);
}
components[component_index] = (&[], component_size);
component_count += 1;
buffer_offset += component_size;
}
Ok((*index_counter, component_count, buffer_offset))
}
pub fn count_tarjan_scc<G, S>(
graph: &G,
state: &mut [TarjanState],
mut stack: S,
) -> Result<usize, AlgorithmError<usize>>
where
G: Graph<usize>,
S: Deque<usize>,
AlgorithmError<usize>: From<G::Error>,
{
let mut index_counter = 0;
let mut component_count = 0;
for node_idx in graph.iter_nodes()? {
if node_idx >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(node_idx),
));
}
if state[node_idx].index == usize::MAX {
component_count +=
tarjan_count_dfs(graph, node_idx, state, &mut stack, &mut index_counter)?;
}
}
Ok(component_count)
}
fn tarjan_count_dfs<G, S>(
graph: &G,
node: usize,
state: &mut [TarjanState],
stack: &mut S,
index_counter: &mut usize,
) -> Result<usize, AlgorithmError<usize>>
where
G: Graph<usize>,
S: Deque<usize>,
AlgorithmError<usize>: From<G::Error>,
{
if node >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(node),
));
}
state[node].index = *index_counter;
state[node].lowlink = *index_counter;
state[node].on_stack = true;
*index_counter += 1;
stack
.push_back(node)
.map_err(|_| AlgorithmError::StackCapacityExceeded)?;
let mut component_count = 0;
for neighbor_idx in graph.outgoing_edges(node)? {
if neighbor_idx >= state.len() {
return Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(neighbor_idx),
));
}
if state[neighbor_idx].index == usize::MAX {
component_count += tarjan_count_dfs(graph, neighbor_idx, state, stack, index_counter)?;
state[node].lowlink = state[node].lowlink.min(state[neighbor_idx].lowlink);
} else if state[neighbor_idx].on_stack {
state[node].lowlink = state[node].lowlink.min(state[neighbor_idx].index);
}
}
if state[node].lowlink == state[node].index {
component_count += 1;
loop {
let Some(stack_node) = stack.pop_back() else {
return Err(AlgorithmError::InvalidState);
};
state[stack_node].on_stack = false;
if stack_node == node {
break;
}
}
}
Ok(component_count)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::adjacency_list::map_adjacency_list::MapAdjacencyList;
use crate::containers::{
maps::{staticdict::Dictionary, MapTrait},
queues::CircularQueue,
};
use crate::edgelist::edge_list::EdgeList;
#[test]
fn test_tarjan_scc_simple_cycle() {
let edges = [(0, 1), (1, 2), (2, 0)];
let graph = EdgeList::<10, usize, _>::new(edges);
let mut state = [TarjanState::default(); 10];
let stack = CircularQueue::<usize, 10>::new();
let mut components: [(&[usize], usize); 5] = [(&[], 0); 5];
let mut node_buffer = [0usize; 10];
let component_count =
tarjan_scc(&graph, &mut state, stack, &mut components, &mut node_buffer).unwrap();
assert_eq!(component_count, 1);
assert_eq!(components[0].0.len(), 3);
let mut nodes_in_component = [0; 3];
for (i, &node) in components[0].0.iter().enumerate() {
nodes_in_component[i] = node;
}
nodes_in_component.sort();
assert_eq!(nodes_in_component, [0, 1, 2]);
}
#[test]
fn test_tarjan_scc_disconnected_components() {
let edges = [(0, 1), (1, 0), (2, 3), (3, 2)];
let graph = EdgeList::<10, usize, _>::new(edges);
let mut state = [TarjanState::default(); 10];
let stack = CircularQueue::<usize, 10>::new();
let count = count_tarjan_scc(&graph, &mut state, stack).unwrap();
assert_eq!(count, 2);
}
#[test]
fn test_tarjan_scc_dag() {
let edges = [(0, 1), (0, 2), (1, 3), (2, 3)];
let graph = EdgeList::<10, usize, _>::new(edges);
let mut state = [TarjanState::default(); 10];
let stack = CircularQueue::<usize, 10>::new();
let count = count_tarjan_scc(&graph, &mut state, stack).unwrap();
assert_eq!(count, 4);
}
#[test]
fn test_tarjan_scc_complex() {
let mut dict = Dictionary::<usize, [usize; 3], 10>::new();
dict.insert(0, [1, 3, 0]).unwrap(); dict.insert(1, [2, 1, 1]).unwrap(); dict.insert(2, [0, 2, 2]).unwrap(); dict.insert(3, [4, 3, 3]).unwrap(); dict.insert(4, [5, 4, 4]).unwrap(); dict.insert(5, [4, 5, 5]).unwrap();
let graph = MapAdjacencyList::new_unchecked(dict);
let mut state = [TarjanState::default(); 10];
let stack = CircularQueue::<usize, 20>::new();
let mut components: [(&[usize], usize); 5] = [(&[], 0); 5];
let mut node_buffer = [0usize; 10];
let component_count =
tarjan_scc(&graph, &mut state, stack, &mut components, &mut node_buffer).unwrap();
assert_eq!(component_count, 3);
let mut sizes = [0; 3];
for i in 0..component_count {
sizes[i] = components[i].0.len();
}
sizes.sort();
assert_eq!(sizes, [1, 2, 3]); }
#[test]
fn test_tarjan_scc_self_loops() {
let edges = [(0, 0), (1, 1), (0, 1)];
let graph = EdgeList::<10, usize, _>::new(edges);
let mut state = [TarjanState::default(); 10];
let stack = CircularQueue::<usize, 10>::new();
let count = count_tarjan_scc(&graph, &mut state, stack).unwrap();
assert_eq!(count, 2);
}
#[test]
fn test_tarjan_scc_single_node() {
let mut dict = Dictionary::<usize, [usize; 0], 5>::new();
dict.insert(42, []).unwrap();
let graph = MapAdjacencyList::new_unchecked(dict);
let mut state = [TarjanState::default(); 50];
let stack = CircularQueue::<usize, 10>::new();
let count = count_tarjan_scc(&graph, &mut state, stack).unwrap();
assert_eq!(count, 1);
}
#[test]
fn test_tarjan_scc_bounds_checking() {
let mut dict = Dictionary::<usize, [usize; 1], 5>::new();
dict.insert(0, [10]).unwrap();
let graph = MapAdjacencyList::new_unchecked(dict);
let mut state = [TarjanState::default(); 5]; let stack = CircularQueue::<usize, 10>::new();
let result = count_tarjan_scc(&graph, &mut state, stack);
assert!(matches!(
result,
Err(AlgorithmError::GraphError(
crate::graph::GraphError::NodeNotFound(10)
))
));
}
#[test]
fn test_tarjan_scc_invalid_state_error() {
let error = AlgorithmError::<usize>::InvalidState;
match error {
AlgorithmError::InvalidState => {
assert!(true);
}
_ => panic!("InvalidState variant should match"),
}
}
}