1use crate::{IndexUndirectedGraphView, Vec};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct BiconnectedComponents<Node, Edge> {
6 components: Vec<Vec<Edge>>,
7 articulation_points: Vec<Node>,
8}
9
10impl<Node, Edge> BiconnectedComponents<Node, Edge> {
11 #[must_use]
13 pub fn components(&self) -> &[Vec<Edge>] {
14 &self.components
15 }
16
17 #[must_use]
19 pub fn component_count(&self) -> usize {
20 self.components.len()
21 }
22
23 #[must_use]
25 pub fn articulation_points(&self) -> &[Node] {
26 &self.articulation_points
27 }
28
29 #[must_use]
31 pub fn into_components(self) -> Vec<Vec<Edge>> {
32 self.components
33 }
34}
35
36#[must_use]
38pub fn biconnected_components<G>(graph: &G) -> BiconnectedComponents<G::Node, G::Edge>
39where
40 G: IndexUndirectedGraphView,
41{
42 biconnected_components_with_allowed::<G, false>(graph, Vec::new())
43}
44
45#[must_use]
50pub fn biconnected_components_filtered<G, F>(
51 graph: &G,
52 allows_edge: F,
53) -> BiconnectedComponents<G::Node, G::Edge>
54where
55 G: IndexUndirectedGraphView,
56 F: Fn(G::Edge) -> bool,
57{
58 let mut allowed = vec![false; graph.edge_bound()];
59 for edge in graph.edge_indices() {
60 allowed[G::edge_slot(edge)] = allows_edge(edge);
61 }
62 biconnected_components_with_allowed::<G, true>(graph, allowed)
63}
64
65fn biconnected_components_with_allowed<G, const FILTERED: bool>(
66 graph: &G,
67 allowed: Vec<bool>,
68) -> BiconnectedComponents<G::Node, G::Edge>
69where
70 G: IndexUndirectedGraphView,
71{
72 let mut nodes = graph.node_indices().collect::<Vec<_>>();
73 nodes.sort_unstable_by_key(|node| G::node_slot(*node));
74 let mut state = State::<G, FILTERED>::new(graph, allowed);
75 for root in nodes {
76 if state.discovery[G::node_slot(root)] == usize::MAX {
77 state.search_from(graph, root);
78 }
79 }
80 state.finish(graph)
81}
82
83struct Frame<Node, Edge> {
84 node: Node,
85 parent_edge: Option<Edge>,
86 next: usize,
87 degree: usize,
88 children: usize,
89}
90
91struct State<G, const FILTERED: bool>
92where
93 G: IndexUndirectedGraphView,
94{
95 time: usize,
96 discovery: Vec<usize>,
97 low: Vec<usize>,
98 articulation: Vec<bool>,
99 allowed: Vec<bool>,
100 seen_self_loop: Vec<bool>,
101 edge_stack: Vec<G::Edge>,
102 edge_component: Vec<usize>,
103 component_count: usize,
104}
105
106impl<G, const FILTERED: bool> State<G, FILTERED>
107where
108 G: IndexUndirectedGraphView,
109{
110 fn new(graph: &G, allowed: Vec<bool>) -> Self {
111 Self {
112 time: 0,
113 discovery: vec![usize::MAX; graph.node_bound()],
114 low: vec![0; graph.node_bound()],
115 articulation: vec![false; graph.node_bound()],
116 allowed,
117 seen_self_loop: vec![false; graph.edge_bound()],
118 edge_stack: Vec::new(),
119 edge_component: vec![usize::MAX; graph.edge_bound()],
120 component_count: 0,
121 }
122 }
123
124 fn search_from(&mut self, graph: &G, root: G::Node) {
125 self.discover(root);
126 let mut frames = vec![Self::frame(graph, root, None)];
127 while !frames.is_empty() {
128 let next = {
129 let frame = frames.last_mut().expect("non-empty DFS stack");
130 if frame.next == frame.degree {
131 None
132 } else {
133 let edge = graph
134 .incident_edge_at(frame.node, frame.next)
135 .expect("offset is inside incident edge range");
136 frame.next += 1;
137 Some((frame.node, frame.parent_edge, edge))
138 }
139 };
140 if let Some((node, parent_edge, edge)) = next {
141 self.explore_edge(graph, &mut frames, node, parent_edge, edge);
142 } else {
143 let frame = frames.pop().expect("non-empty DFS stack");
144 self.finish_frame(frames.last_mut(), &frame);
145 }
146 }
147 debug_assert!(self.edge_stack.is_empty());
148 }
149
150 fn explore_edge(
151 &mut self,
152 graph: &G,
153 frames: &mut Vec<Frame<G::Node, G::Edge>>,
154 node: G::Node,
155 parent_edge: Option<G::Edge>,
156 edge: G::Edge,
157 ) {
158 let edge_slot = G::edge_slot(edge);
159 if FILTERED && !self.allowed[edge_slot] {
160 return;
161 }
162 if Some(edge) == parent_edge {
163 return;
164 }
165 let Some(neighbor) = graph.opposite(edge, node) else {
166 return;
167 };
168 if neighbor == node {
169 if !self.seen_self_loop[edge_slot] {
170 self.seen_self_loop[edge_slot] = true;
171 self.edge_component[edge_slot] = self.component_count;
172 self.component_count += 1;
173 }
174 return;
175 }
176 let slot = G::node_slot(node);
177 let neighbor_slot = G::node_slot(neighbor);
178 if self.discovery[neighbor_slot] == usize::MAX {
179 frames.last_mut().expect("parent frame").children += 1;
180 self.edge_stack.push(edge);
181 self.discover(neighbor);
182 frames.push(Self::frame(graph, neighbor, Some(edge)));
183 } else if self.discovery[neighbor_slot] < self.discovery[slot] {
184 self.edge_stack.push(edge);
185 self.low[slot] = self.low[slot].min(self.discovery[neighbor_slot]);
186 }
187 }
188
189 fn finish_frame(
190 &mut self,
191 parent: Option<&mut Frame<G::Node, G::Edge>>,
192 frame: &Frame<G::Node, G::Edge>,
193 ) {
194 let slot = G::node_slot(frame.node);
195 let Some(tree_edge) = frame.parent_edge else {
196 if frame.children > 1 {
197 self.articulation[slot] = true;
198 }
199 return;
200 };
201 let parent = parent.expect("non-root frame has parent");
202 let parent_slot = G::node_slot(parent.node);
203 self.low[parent_slot] = self.low[parent_slot].min(self.low[slot]);
204 let parent_discovery = self.discovery[parent_slot];
205 if self.low[slot] >= parent_discovery {
206 if parent.parent_edge.is_some() {
207 self.articulation[parent_slot] = true;
208 }
209 self.pop_component(tree_edge);
210 }
211 }
212
213 fn frame(graph: &G, node: G::Node, parent_edge: Option<G::Edge>) -> Frame<G::Node, G::Edge> {
214 Frame {
215 node,
216 parent_edge,
217 next: 0,
218 degree: graph.incident_edges(node).len(),
219 children: 0,
220 }
221 }
222
223 fn discover(&mut self, node: G::Node) {
224 let slot = G::node_slot(node);
225 self.discovery[slot] = self.time;
226 self.low[slot] = self.time;
227 self.time += 1;
228 }
229
230 fn pop_component(&mut self, stop: G::Edge) {
231 let component = self.component_count;
232 self.component_count += 1;
233 while let Some(edge) = self.edge_stack.pop() {
234 self.edge_component[G::edge_slot(edge)] = component;
235 if edge == stop {
236 break;
237 }
238 }
239 }
240
241 fn finish(self, graph: &G) -> BiconnectedComponents<G::Node, G::Edge> {
242 let mut counts = vec![0; self.component_count];
243 for &component in &self.edge_component {
244 if component != usize::MAX {
245 counts[component] += 1;
246 }
247 }
248 let mut components = counts
249 .into_iter()
250 .map(Vec::with_capacity)
251 .collect::<Vec<_>>();
252 let mut edges = graph.edge_indices().collect::<Vec<_>>();
253 if !edges.is_sorted_by_key(|edge| G::edge_slot(*edge)) {
254 edges.sort_unstable_by_key(|edge| G::edge_slot(*edge));
255 }
256 for edge in edges {
257 let component = self.edge_component[G::edge_slot(edge)];
258 if component != usize::MAX {
259 components[component].push(edge);
260 }
261 }
262 components.retain(|component| !component.is_empty());
263 components.sort_unstable_by_key(|component| G::edge_slot(component[0]));
264 let mut articulation_points = graph
265 .node_indices()
266 .filter(|node| self.articulation[G::node_slot(*node)])
267 .collect::<Vec<_>>();
268 articulation_points.sort_unstable_by_key(|node| G::node_slot(*node));
269 BiconnectedComponents {
270 components,
271 articulation_points,
272 }
273 }
274}