use std::collections::{HashMap, HashSet};
use crate::graph::capability::{Bigraph, StableEdge, StableNode};
use crate::graph::Graph;
type EdgeAdj<G> = Vec<(
<G as crate::graph::GraphProperty>::EdgeIx,
<G as crate::graph::GraphProperty>::NodeIx,
)>;
type BridgeFrame<G> = (
<G as crate::graph::GraphProperty>::NodeIx,
Option<<G as crate::graph::GraphProperty>::EdgeIx>,
EdgeAdj<G>,
usize,
);
type ArticulationFrame<G> = (
<G as crate::graph::GraphProperty>::NodeIx,
EdgeAdj<G>,
usize,
);
pub struct Bridges<'r, G: ?Sized, N, Ns, Frame> {
graph: &'r G,
disc: HashMap<N, usize>,
low: HashMap<N, usize>,
timer: usize,
nodes: Ns,
stack: Vec<Frame>,
}
pub fn bridges<'r, G>(
graph: &'r G,
) -> Bridges<'r, G, G::NodeIx, <G as crate::graph::GraphOperation<'r>>::NodeIndices, BridgeFrame<G>>
where
G: Graph + Bigraph + StableEdge + ?Sized,
{
Bridges {
graph,
disc: HashMap::new(),
low: HashMap::new(),
timer: 0,
nodes: <_ as crate::graph::GraphOperation<'_>>::node_indices(graph),
stack: Vec::new(),
}
}
impl<'r, G> Iterator
for Bridges<'r, G, G::NodeIx, <G as crate::graph::GraphOperation<'r>>::NodeIndices, BridgeFrame<G>>
where
G: Graph + Bigraph + StableEdge + ?Sized,
{
type Item = G::EdgeIx;
fn next(&mut self) -> Option<G::EdgeIx> {
loop {
if let Some((node, parent_edge, ref neighbors, ref mut idx)) = self.stack.last_mut() {
let node = *node;
let parent_edge = *parent_edge;
if *idx < neighbors.len() {
let (eix, neighbor) = neighbors[*idx];
*idx += 1;
if Some(eix) == parent_edge {
continue;
}
if self.disc.contains_key(&neighbor) {
let nl = self.low[&node].min(self.disc[&neighbor]);
self.low.insert(node, nl);
} else {
self.disc.insert(neighbor, self.timer);
self.low.insert(neighbor, self.timer);
self.timer += 1;
let next_neighbors =
unsafe { collect_undirected_neighbors(self.graph, neighbor) };
self.stack.push((neighbor, Some(eix), next_neighbors, 0));
}
} else {
let node_low = self.low[&node];
self.stack.pop();
if let Some((parent, _, _, _)) = self.stack.last() {
let parent = *parent;
let parent_low = self.low[&parent].min(node_low);
self.low.insert(parent, parent_low);
if node_low > self.disc[&parent] {
if let Some(pe) = parent_edge {
return Some(pe);
}
}
}
}
} else {
loop {
let start = self.nodes.next()?;
if !self.disc.contains_key(&start) {
self.disc.insert(start, self.timer);
self.low.insert(start, self.timer);
self.timer += 1;
let neighbors = unsafe { collect_undirected_neighbors(self.graph, start) };
self.stack.push((start, None, neighbors, 0));
break;
}
}
}
}
}
}
pub struct ArticulationPoints<'r, G: ?Sized, N, Ns, Frame> {
graph: &'r G,
disc: HashMap<N, usize>,
low: HashMap<N, usize>,
parent: HashMap<N, Option<N>>,
children_count: HashMap<N, usize>,
yielded: HashSet<N>,
timer: usize,
nodes: Ns,
stack: Vec<Frame>,
}
pub fn articulation_points<'r, G>(
graph: &'r G,
) -> ArticulationPoints<
'r,
G,
G::NodeIx,
<G as crate::graph::GraphOperation<'r>>::NodeIndices,
ArticulationFrame<G>,
>
where
G: Graph + Bigraph + StableNode + ?Sized,
{
ArticulationPoints {
graph,
disc: HashMap::new(),
low: HashMap::new(),
parent: HashMap::new(),
children_count: HashMap::new(),
yielded: HashSet::new(),
timer: 0,
nodes: <_ as crate::graph::GraphOperation<'_>>::node_indices(graph),
stack: Vec::new(),
}
}
impl<'r, G> Iterator
for ArticulationPoints<
'r,
G,
G::NodeIx,
<G as crate::graph::GraphOperation<'r>>::NodeIndices,
ArticulationFrame<G>,
>
where
G: Graph + Bigraph + StableNode + ?Sized,
{
type Item = G::NodeIx;
fn next(&mut self) -> Option<G::NodeIx> {
loop {
if let Some((node, ref neighbors, ref mut idx)) = self.stack.last_mut() {
let node = *node;
if *idx < neighbors.len() {
let (_eix, neighbor) = neighbors[*idx];
*idx += 1;
if self.parent[&node] == Some(neighbor) {
continue;
}
if self.disc.contains_key(&neighbor) {
let nl = self.low[&node].min(self.disc[&neighbor]);
self.low.insert(node, nl);
} else {
*self.children_count.entry(node).or_insert(0) += 1;
self.parent.insert(neighbor, Some(node));
self.disc.insert(neighbor, self.timer);
self.low.insert(neighbor, self.timer);
self.timer += 1;
let next_neighbors =
unsafe { collect_undirected_neighbors(self.graph, neighbor) };
self.stack.push((neighbor, next_neighbors, 0));
}
} else {
let node_low = self.low[&node];
let node_parent = self.parent[&node];
self.stack.pop();
if let Some(par) = node_parent {
let par_low = self.low[&par].min(node_low);
self.low.insert(par, par_low);
let par_is_root = self.parent[&par].is_none();
let is_ap = if par_is_root {
self.children_count.get(&par).copied().unwrap_or(0) >= 2
} else {
node_low >= self.disc[&par]
};
if is_ap && self.yielded.insert(par) {
return Some(par);
}
}
}
} else {
loop {
let start = self.nodes.next()?;
if !self.disc.contains_key(&start) {
self.parent.insert(start, None);
self.disc.insert(start, self.timer);
self.low.insert(start, self.timer);
self.timer += 1;
let neighbors = unsafe { collect_undirected_neighbors(self.graph, start) };
self.stack.push((start, neighbors, 0));
break;
}
}
}
}
}
}
unsafe fn collect_undirected_neighbors<G>(
graph: &G,
node: G::NodeIx,
) -> Vec<(G::EdgeIx, G::NodeIx)>
where
G: Graph + Bigraph + ?Sized,
{
let mut neighbors = Vec::new();
for eix in <G as crate::graph::GraphOperation<'_>>::edge_indices_of_unchecked(graph, node) {
for endpoint in <G as crate::graph::GraphOperation<'_>>::endpoints_unchecked(graph, eix) {
if endpoint != node {
neighbors.push((eix, endpoint));
}
}
}
neighbors
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BTreeGraph;
#[test]
fn bridge_linear() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
let b: HashSet<_> = bridges(&g).collect();
assert_eq!(b.len(), 2);
}
#[test]
fn bridge_cycle() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("2->0", [2, 0]).unwrap();
let b: HashSet<_> = bridges(&g).collect();
assert!(b.is_empty());
}
#[test]
fn bridge_with_pendant() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("2->0", [2, 0]).unwrap();
g.insert_edge("1->3", [1, 3]).unwrap();
let b: HashSet<_> = bridges(&g).collect();
assert_eq!(b.len(), 1);
assert!(b.contains(&"1->3"));
}
#[test]
fn articulation_point_linear() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
let ap: HashSet<_> = articulation_points(&g).collect();
assert_eq!(ap.len(), 1);
assert!(ap.contains(&1));
}
#[test]
fn articulation_point_cycle() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("2->0", [2, 0]).unwrap();
let ap: HashSet<_> = articulation_points(&g).collect();
assert!(ap.is_empty());
}
#[test]
fn articulation_point_star() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("0->2", [0, 2]).unwrap();
g.insert_edge("0->3", [0, 3]).unwrap();
let ap: HashSet<_> = articulation_points(&g).collect();
assert!(ap.contains(&0));
}
#[test]
fn no_bridges_in_complete_graph() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("0->2", [0, 2]).unwrap();
g.insert_edge("0->3", [0, 3]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("1->3", [1, 3]).unwrap();
g.insert_edge("2->3", [2, 3]).unwrap();
let b: HashSet<_> = bridges(&g).collect();
assert!(b.is_empty());
let ap: HashSet<_> = articulation_points(&g).collect();
assert!(ap.is_empty());
}
}