use crate::core::algo::traversal::TraversalError;
use crate::core::view::{EdgeId, GraphView, NodeId};
use std::collections::{HashMap, HashSet};
pub trait Bridges: GraphView {
fn articulation_points(&self) -> Result<Vec<NodeId>, TraversalError> {
let mut found = HashSet::new();
let mut state = TarjanState::default();
for root in self.node_ids() {
if state.discovery.contains_key(&root) {
continue;
}
let before = state.discovery.len();
self.tarjan_from(root, &mut state)?;
let children = state
.parent_edge
.iter()
.filter(|(node, _)| state.tree_parent.get(node) == Some(&root) && **node != root)
.count();
if children > 1 {
found.insert(root);
}
debug_assert!(state.discovery.len() >= before);
}
found.extend(state.articulation_points.iter().copied());
let mut points: Vec<NodeId> = found.into_iter().collect();
points.sort_unstable();
Ok(points)
}
fn bridge_edges(&self) -> Result<Vec<EdgeId>, TraversalError> {
let mut state = TarjanState::default();
for root in self.node_ids() {
if state.discovery.contains_key(&root) {
continue;
}
self.tarjan_from(root, &mut state)?;
}
state.bridges.sort_unstable();
Ok(state.bridges)
}
fn bridges(&self) -> Result<Vec<(NodeId, NodeId)>, TraversalError> {
let mut pairs: Vec<(NodeId, NodeId)> = self
.bridge_edges()?
.into_iter()
.filter_map(|edge| self.edge_endpoints(edge))
.map(|(from, to)| if from <= to { (from, to) } else { (to, from) })
.collect();
pairs.sort_unstable();
Ok(pairs)
}
fn tarjan_from(&self, root: NodeId, state: &mut TarjanState) -> Result<(), TraversalError> {
if !self.contains_node(root) {
return Err(TraversalError::InvalidNode(root));
}
if state.discovery.contains_key(&root) {
return Ok(());
}
state.time += 1;
state.discovery.insert(root, state.time);
state.low.insert(root, state.time);
let mut stack: Vec<(NodeId, Self::Links<'_>, Option<EdgeId>)> =
vec![(root, self.links(root), None)];
while let Some(frame) = stack.last_mut() {
let node = frame.0;
let arrival_edge = frame.2;
let next = frame.1.next();
match next {
Some(link) => {
if Some(link.edge) == arrival_edge {
continue;
}
if !self.contains_node(link.target) {
return Err(TraversalError::InvalidNode(link.target));
}
match state.discovery.get(&link.target) {
Some(&target_discovery) => {
let low = state
.low
.get_mut(&node)
.expect("low is recorded when a node is discovered");
*low = (*low).min(target_discovery);
}
None => {
state.time += 1;
state.discovery.insert(link.target, state.time);
state.low.insert(link.target, state.time);
state.parent_edge.insert(link.target, link.edge);
state.tree_parent.insert(link.target, node);
stack.push((link.target, self.links(link.target), Some(link.edge)));
}
}
}
None => {
stack.pop();
if let Some(&(parent, _, _)) = stack.last() {
let node_low = state.low[&node];
let parent_discovery = state.discovery[&parent];
let low = state
.low
.get_mut(&parent)
.expect("low is recorded when a node is discovered");
*low = (*low).min(node_low);
if node_low > parent_discovery {
if let Some(&edge) = state.parent_edge.get(&node) {
state.bridges.push(edge);
}
}
if node_low >= parent_discovery && stack.len() > 1 {
state.articulation_points.insert(parent);
}
}
}
}
}
Ok(())
}
}
impl<G: GraphView + ?Sized> Bridges for G {}
#[derive(Debug, Default)]
pub struct TarjanState {
pub discovery: HashMap<NodeId, usize>,
pub low: HashMap<NodeId, usize>,
pub parent_edge: HashMap<NodeId, EdgeId>,
pub tree_parent: HashMap<NodeId, NodeId>,
pub articulation_points: HashSet<NodeId>,
pub bridges: Vec<EdgeId>,
pub time: usize,
}
impl TarjanState {
pub fn visited(&self) -> HashSet<NodeId> {
self.discovery.keys().copied().collect()
}
}
#[cfg(test)]
#[cfg(feature = "graph")]
mod tests {
use super::*;
use crate::graph::Graph;
#[test]
fn test_every_edge_of_a_path_is_a_bridge() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let nodes: Vec<_> = (0..4).map(|i| graph.add_node(i)).collect();
for pair in nodes.windows(2) {
graph.add_edge(pair[0], pair[1], 1, ()).unwrap();
}
assert_eq!(
graph.bridges().unwrap(),
vec![
(nodes[0], nodes[1]),
(nodes[1], nodes[2]),
(nodes[2], nodes[3])
]
);
}
#[test]
fn test_a_cycle_has_no_bridges() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let nodes: Vec<_> = (0..3).map(|i| graph.add_node(i)).collect();
for i in 0..3 {
graph.add_edge(nodes[i], nodes[(i + 1) % 3], 1, ()).unwrap();
}
assert!(graph.bridges().unwrap().is_empty());
}
#[test]
fn test_parallel_edges_are_not_bridges() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let a = graph.add_node(0);
let b = graph.add_node(1);
let c = graph.add_node(2);
graph.add_edge(a, b, 1, ()).unwrap();
graph.add_edge(a, b, 2, ()).unwrap();
let single = graph.add_edge(b, c, 3, ()).unwrap();
assert_eq!(graph.bridge_edges().unwrap(), vec![single]);
assert_eq!(graph.bridges().unwrap(), vec![(b, c)]);
}
#[test]
fn test_bridges_across_disconnected_components() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let a = graph.add_node(0);
let b = graph.add_node(1);
let c = graph.add_node(2);
let d = graph.add_node(3);
graph.add_edge(a, b, 1, ()).unwrap();
graph.add_edge(c, d, 1, ()).unwrap();
graph.add_node(4);
assert_eq!(graph.bridges().unwrap(), vec![(a, b), (c, d)]);
}
#[test]
fn test_deep_chain_does_not_overflow_the_stack() {
let mut graph = Graph::<u32, usize, ()>::new(false);
let nodes: Vec<_> = (0..200_000).map(|i| graph.add_node(i)).collect();
for pair in nodes.windows(2) {
graph.add_edge(pair[0], pair[1], 1, ()).unwrap();
}
assert_eq!(graph.bridge_edges().unwrap().len(), nodes.len() - 1);
}
#[test]
fn test_the_middle_of_a_path_is_an_articulation_point() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let nodes: Vec<_> = (0..3).map(|i| graph.add_node(i)).collect();
graph.add_edge(nodes[0], nodes[1], 1, ()).unwrap();
graph.add_edge(nodes[1], nodes[2], 1, ()).unwrap();
assert_eq!(graph.articulation_points().unwrap(), vec![nodes[1]]);
}
#[test]
fn test_a_cycle_has_no_articulation_points() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let nodes: Vec<_> = (0..4).map(|i| graph.add_node(i)).collect();
for i in 0..4 {
graph.add_edge(nodes[i], nodes[(i + 1) % 4], 1, ()).unwrap();
}
assert!(graph.articulation_points().unwrap().is_empty());
}
#[test]
fn test_the_hub_of_a_star_is_an_articulation_point() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let hub = graph.add_node(0);
for i in 1..5 {
let spoke = graph.add_node(i);
graph.add_edge(hub, spoke, 1, ()).unwrap();
}
assert_eq!(graph.articulation_points().unwrap(), vec![hub]);
}
#[test]
fn test_articulation_points_match_the_definition() {
use crate::core::{Connectivity, NodeFiltered};
let mut graph = Graph::<u32, u32, ()>::new(false);
let nodes: Vec<_> = (0..7).map(|i| graph.add_node(i)).collect();
for &(a, b) in &[
(0, 1),
(1, 2),
(2, 0),
(2, 3),
(3, 4),
(4, 5),
(5, 3),
(3, 6),
] {
graph.add_edge(nodes[a], nodes[b], 1, ()).unwrap();
}
let reported: HashSet<NodeId> = graph.articulation_points().unwrap().into_iter().collect();
let before = graph.weakly_connected_components().unwrap().len();
for node in graph.node_ids() {
let without = NodeFiltered::new(&graph, move |other| other != node);
let after = without.weakly_connected_components().unwrap().len();
let splits = after > before;
assert_eq!(
reported.contains(&node),
splits,
"node {:?}: reported {}, components {} -> {}",
node,
reported.contains(&node),
before,
after
);
}
}
#[test]
fn test_two_parallel_edges_leave_no_articulation_point() {
let mut graph = Graph::<u32, u32, ()>::new(false);
let a = graph.add_node(0);
let b = graph.add_node(1);
graph.add_edge(a, b, 1, ()).unwrap();
graph.add_edge(a, b, 2, ()).unwrap();
assert!(graph.articulation_points().unwrap().is_empty());
}
}