use hashbrown::{HashMap, HashSet};
use std::hash::Hash;
use petgraph::{
visit::{
EdgeCount, GraphBase, GraphProp, IntoEdges, IntoNodeIdentifiers, NodeIndexable, Time,
Visitable,
},
Undirected,
};
use crate::traversal::{depth_first_search, DfsEvent};
const NULL: usize = usize::MAX;
type Edge<G> = (<G as GraphBase>::NodeId, <G as GraphBase>::NodeId);
#[inline]
fn is_root(parent: &[usize], u: usize) -> bool {
parent[u] == NULL
}
fn _articulation_points<G>(
graph: G,
components: Option<&mut HashMap<Edge<G>, usize>>,
bridges: Option<&mut HashSet<Edge<G>>>,
) -> HashSet<G::NodeId>
where
G: GraphProp<EdgeType = Undirected>
+ EdgeCount
+ IntoEdges
+ Visitable
+ NodeIndexable
+ IntoNodeIdentifiers,
G::NodeId: Eq + Hash,
{
let num_nodes = graph.node_bound();
let mut low = vec![NULL; num_nodes];
let mut disc = vec![NULL; num_nodes];
let mut parent = vec![NULL; num_nodes];
let mut root_children: usize = 0;
let mut points = HashSet::new();
let mut edge_stack = Vec::new();
let need_components = components.is_some();
let mut tmp_components = if need_components {
HashMap::with_capacity(graph.edge_count())
} else {
HashMap::new()
};
let need_bridges = bridges.is_some();
let mut tmp_bridges = if need_bridges {
HashSet::with_capacity(graph.edge_count())
} else {
HashSet::new()
};
let mut num_components: usize = 0;
depth_first_search(graph, graph.node_identifiers(), |event| match event {
DfsEvent::Discover(u_id, Time(t)) => {
let u = graph.to_index(u_id);
low[u] = t;
disc[u] = t;
}
DfsEvent::TreeEdge(u_id, v_id, _) => {
let u = graph.to_index(u_id);
let v = graph.to_index(v_id);
parent[v] = u;
if is_root(&parent, u) {
root_children += 1;
}
if need_components {
edge_stack.push((u_id, v_id));
}
}
DfsEvent::BackEdge(u_id, v_id, _) => {
let u = graph.to_index(u_id);
let v = graph.to_index(v_id);
if v != parent[u] {
low[u] = low[u].min(disc[v]);
if need_components {
edge_stack.push((u_id, v_id));
}
}
}
DfsEvent::Finish(u_id, _) => {
let u = graph.to_index(u_id);
if is_root(&parent, u) {
if root_children > 1 {
points.insert(u_id);
}
root_children = 0;
} else {
let pu = parent[u];
let pu_id = graph.from_index(pu);
low[pu] = low[pu].min(low[u]);
if !is_root(&parent, pu) && low[u] >= disc[pu] {
points.insert(pu_id);
if need_components {
if let Some(at) = edge_stack.iter().rposition(|&x| x == (pu_id, u_id)) {
tmp_components.extend(
edge_stack[at..].iter().map(|edge| (*edge, num_components)),
);
edge_stack.truncate(at);
num_components += 1;
}
}
if need_bridges && low[u] != disc[pu] {
tmp_bridges.insert((pu_id, u_id));
}
}
if is_root(&parent, pu) && need_components {
if let Some(at) = edge_stack.iter().position(|&x| x == (pu_id, u_id)) {
tmp_components
.extend(edge_stack[at..].iter().map(|edge| (*edge, num_components)));
edge_stack.truncate(at);
num_components += 1;
}
}
}
}
_ => (),
});
if let Some(x) = components {
*x = tmp_components;
}
if let Some(x) = bridges {
*x = tmp_bridges;
}
points
}
pub fn articulation_points<G>(
graph: G,
components: Option<&mut HashMap<Edge<G>, usize>>,
) -> HashSet<G::NodeId>
where
G: GraphProp<EdgeType = Undirected>
+ EdgeCount
+ IntoEdges
+ Visitable
+ NodeIndexable
+ IntoNodeIdentifiers,
G::NodeId: Eq + Hash,
{
_articulation_points(graph, components, None)
}
pub fn bridges<G>(graph: G) -> HashSet<Edge<G>>
where
G: GraphProp<EdgeType = Undirected>
+ EdgeCount
+ IntoEdges
+ Visitable
+ NodeIndexable
+ IntoNodeIdentifiers,
G::NodeId: Eq + Hash,
{
let mut bridges = HashSet::new();
_articulation_points(graph, None, Some(&mut bridges));
bridges
}
#[cfg(test)]
mod tests {
use crate::connectivity::{articulation_points, bridges};
use hashbrown::{HashMap, HashSet};
use petgraph::graph::node_index as nx;
use petgraph::prelude::*;
use std::iter::FromIterator;
#[test]
fn test_articulation_points_repetitions() {
let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (1, 3)]);
let a_points = articulation_points(&graph, None);
assert_eq!(a_points, HashSet::from_iter([nx(1)]));
}
#[test]
fn test_articulation_points_cycle() {
let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
let a_points = articulation_points(&graph, None);
assert_eq!(a_points, HashSet::from_iter([nx(1)]));
}
#[test]
fn test_single_bridge() {
let graph = UnGraph::<(), ()>::from_edges([
(1, 2),
(2, 3),
(3, 4),
(3, 5),
(5, 6),
(6, 7),
(7, 8),
(5, 9),
(9, 10),
(1, 3),
(1, 4),
(2, 5),
(5, 10),
(6, 8),
]);
assert_eq!(bridges(&graph), HashSet::from_iter([(nx(5), nx(6))]));
}
#[test]
fn test_bridges_cycle() {
let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
assert_eq!(bridges(&graph), HashSet::from_iter([]));
}
#[test]
fn test_biconnected_components_cycle() {
let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
let mut components = HashMap::new();
let _ = articulation_points(&graph, Some(&mut components));
assert_eq!(
components,
HashMap::from_iter([
((nx(1), nx(3)), 0),
((nx(3), nx(4)), 0),
((nx(4), nx(1)), 0),
((nx(1), nx(2)), 1),
((nx(2), nx(0)), 1),
((nx(0), nx(1)), 1)
])
);
}
#[test]
fn test_biconnected_components1() {
let graph = UnGraph::<(), ()>::from_edges([
(0, 1),
(0, 5),
(0, 6),
(0, 14),
(1, 5),
(1, 6),
(1, 14),
(2, 4),
(2, 10),
(3, 4),
(3, 15),
(4, 6),
(4, 7),
(4, 10),
(5, 14),
(6, 14),
(7, 9),
(8, 9),
(8, 12),
(8, 13),
(10, 15),
(11, 12),
(11, 13),
(12, 13),
]);
let mut components = HashMap::new();
let a_points = articulation_points(&graph, Some(&mut components));
assert_eq!(
a_points,
HashSet::from_iter([nx(4), nx(6), nx(7), nx(8), nx(9)])
);
assert_eq!(
components,
HashMap::from_iter([
((nx(3), nx(4)), 0),
((nx(15), nx(3)), 0),
((nx(10), nx(15)), 0),
((nx(4), nx(10)), 0),
((nx(10), nx(2)), 0),
((nx(2), nx(4)), 0),
((nx(13), nx(12)), 1),
((nx(8), nx(13)), 1),
((nx(11), nx(13)), 1),
((nx(12), nx(11)), 1),
((nx(12), nx(8)), 1),
((nx(9), nx(8)), 2),
((nx(7), nx(9)), 3),
((nx(4), nx(7)), 4),
((nx(6), nx(4)), 5),
((nx(0), nx(14)), 6),
((nx(1), nx(5)), 6),
((nx(5), nx(0)), 6),
((nx(5), nx(14)), 6),
((nx(1), nx(14)), 6),
((nx(14), nx(6)), 6),
((nx(6), nx(0)), 6),
((nx(6), nx(1)), 6),
((nx(1), nx(0)), 6),
])
)
}
#[test]
fn test_biconnected_components2() {
let mut graph: Graph<&str, (), Undirected> = Graph::new_undirected();
let a = graph.add_node("A");
let b = graph.add_node("B");
let c = graph.add_node("C");
let d = graph.add_node("D");
let e = graph.add_node("E");
let f = graph.add_node("F");
let g = graph.add_node("G");
let h = graph.add_node("H");
let i = graph.add_node("I");
let j = graph.add_node("J");
graph.extend_with_edges([
(a, b),
(b, c),
(c, a),
(c, d),
(d, e),
(e, c),
(f, i),
(i, j),
(j, h),
(h, g),
(g, f),
(g, i),
(g, j),
(e, g),
]);
let mut components = HashMap::new();
let _ = articulation_points(&graph, Some(&mut components));
assert_eq!(
components,
HashMap::from_iter([
((f, g), 0),
((i, f), 0),
((i, g), 0),
((j, i), 0),
((g, j), 0),
((j, h), 0),
((h, g), 0),
((e, g), 1),
((c, d), 2),
((d, e), 2),
((e, c), 2),
((c, a), 3),
((a, b), 3),
((b, c), 3),
])
)
}
#[test]
fn test_null_graph() {
let graph: Graph<(), (), Undirected> = Graph::new_undirected();
let mut components = HashMap::new();
let a_points = articulation_points(&graph, Some(&mut components));
let bridges = bridges(&graph);
assert_eq!(a_points, HashSet::new());
assert_eq!(bridges, HashSet::new());
assert_eq!(components, HashMap::new());
}
}