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 let Some(frame) = frames.last_mut() {
128 let next = {
129 if frame.next == frame.degree {
130 None
131 } else {
132 let edge = graph.incident_edge_at(frame.node, frame.next);
133 frame.next += 1;
134 edge.map(|edge| (frame.node, frame.parent_edge, edge))
135 }
136 };
137 if let Some((node, parent_edge, edge)) = next {
138 self.explore_edge(graph, &mut frames, node, parent_edge, edge);
139 } else {
140 let Some(frame) = frames.pop() else {
141 break;
142 };
143 self.finish_frame(frames.last_mut(), &frame);
144 }
145 }
146 self.edge_stack.clear();
147 }
148
149 fn explore_edge(
150 &mut self,
151 graph: &G,
152 frames: &mut Vec<Frame<G::Node, G::Edge>>,
153 node: G::Node,
154 parent_edge: Option<G::Edge>,
155 edge: G::Edge,
156 ) {
157 let edge_slot = G::edge_slot(edge);
158 if FILTERED && !self.allowed[edge_slot] {
159 return;
160 }
161 if Some(edge) == parent_edge {
162 return;
163 }
164 let Some(neighbor) = graph.opposite(edge, node) else {
165 return;
166 };
167 if neighbor == node {
168 if !self.seen_self_loop[edge_slot] {
169 self.seen_self_loop[edge_slot] = true;
170 self.edge_component[edge_slot] = self.component_count;
171 self.component_count += 1;
172 }
173 return;
174 }
175 let slot = G::node_slot(node);
176 let neighbor_slot = G::node_slot(neighbor);
177 if self.discovery[neighbor_slot] == usize::MAX {
178 let Some(parent) = frames.last_mut() else {
179 return;
180 };
181 parent.children += 1;
182 self.edge_stack.push(edge);
183 self.discover(neighbor);
184 frames.push(Self::frame(graph, neighbor, Some(edge)));
185 } else if self.discovery[neighbor_slot] < self.discovery[slot] {
186 self.edge_stack.push(edge);
187 self.low[slot] = self.low[slot].min(self.discovery[neighbor_slot]);
188 }
189 }
190
191 fn finish_frame(
192 &mut self,
193 parent: Option<&mut Frame<G::Node, G::Edge>>,
194 frame: &Frame<G::Node, G::Edge>,
195 ) {
196 let slot = G::node_slot(frame.node);
197 let Some(tree_edge) = frame.parent_edge else {
198 if frame.children > 1 {
199 self.articulation[slot] = true;
200 }
201 return;
202 };
203 let Some(parent) = parent else {
204 return;
205 };
206 let parent_slot = G::node_slot(parent.node);
207 self.low[parent_slot] = self.low[parent_slot].min(self.low[slot]);
208 let parent_discovery = self.discovery[parent_slot];
209 if self.low[slot] >= parent_discovery {
210 if parent.parent_edge.is_some() {
211 self.articulation[parent_slot] = true;
212 }
213 self.pop_component(tree_edge);
214 }
215 }
216
217 fn frame(graph: &G, node: G::Node, parent_edge: Option<G::Edge>) -> Frame<G::Node, G::Edge> {
218 Frame {
219 node,
220 parent_edge,
221 next: 0,
222 degree: graph.incident_edges(node).len(),
223 children: 0,
224 }
225 }
226
227 fn discover(&mut self, node: G::Node) {
228 let slot = G::node_slot(node);
229 self.discovery[slot] = self.time;
230 self.low[slot] = self.time;
231 self.time += 1;
232 }
233
234 fn pop_component(&mut self, stop: G::Edge) {
235 let component = self.component_count;
236 self.component_count += 1;
237 while let Some(edge) = self.edge_stack.pop() {
238 self.edge_component[G::edge_slot(edge)] = component;
239 if edge == stop {
240 break;
241 }
242 }
243 }
244
245 fn finish(self, graph: &G) -> BiconnectedComponents<G::Node, G::Edge> {
246 let mut counts = vec![0; self.component_count];
247 for &component in &self.edge_component {
248 if component != usize::MAX {
249 counts[component] += 1;
250 }
251 }
252 let mut components = counts
253 .into_iter()
254 .map(Vec::with_capacity)
255 .collect::<Vec<_>>();
256 let mut edges = graph.edge_indices().collect::<Vec<_>>();
257 if !edges.is_sorted_by_key(|edge| G::edge_slot(*edge)) {
258 edges.sort_unstable_by_key(|edge| G::edge_slot(*edge));
259 }
260 for edge in edges {
261 let component = self.edge_component[G::edge_slot(edge)];
262 if component != usize::MAX {
263 components[component].push(edge);
264 }
265 }
266 components.retain(|component| !component.is_empty());
267 components.sort_unstable_by_key(|component| G::edge_slot(component[0]));
268 let mut articulation_points = graph
269 .node_indices()
270 .filter(|node| self.articulation[G::node_slot(*node)])
271 .collect::<Vec<_>>();
272 articulation_points.sort_unstable_by_key(|node| G::node_slot(*node));
273 BiconnectedComponents {
274 components,
275 articulation_points,
276 }
277 }
278}