1use hashbrown::{HashMap, HashSet};
14use std::hash::Hash;
15
16use petgraph::{
17 Undirected,
18 visit::{
19 EdgeCount, GraphBase, GraphProp, IntoEdges, IntoNodeIdentifiers, NodeIndexable, Time,
20 Visitable,
21 },
22};
23
24use crate::traversal::{DfsEvent, depth_first_search};
25
26const NULL: usize = usize::MAX;
27
28type Edge<G> = (<G as GraphBase>::NodeId, <G as GraphBase>::NodeId);
29
30#[inline]
31fn is_root(parent: &[usize], u: usize) -> bool {
32 parent[u] == NULL
33}
34
35fn _articulation_points<G>(
36 graph: G,
37 components: Option<&mut HashMap<Edge<G>, usize>>,
38 bridges: Option<&mut HashSet<Edge<G>>>,
39) -> HashSet<G::NodeId>
40where
41 G: GraphProp<EdgeType = Undirected>
42 + EdgeCount
43 + IntoEdges
44 + Visitable
45 + NodeIndexable
46 + IntoNodeIdentifiers,
47 G::NodeId: Eq + Hash,
48{
49 let num_nodes = graph.node_bound();
50
51 let mut low = vec![NULL; num_nodes];
52 let mut disc = vec![NULL; num_nodes];
53 let mut parent = vec![NULL; num_nodes];
54
55 let mut root_children: usize = 0;
56 let mut points = HashSet::new();
57
58 let mut edge_stack = Vec::new();
59 let need_components = components.is_some();
60 let mut tmp_components = if need_components {
61 HashMap::with_capacity(graph.edge_count())
62 } else {
63 HashMap::new()
64 };
65 let need_bridges = bridges.is_some();
66 let mut tmp_bridges = if need_bridges {
67 HashSet::with_capacity(graph.edge_count())
68 } else {
69 HashSet::new()
70 };
71 let mut num_components: usize = 0;
72
73 depth_first_search(graph, graph.node_identifiers(), |event| match event {
74 DfsEvent::Discover(u_id, Time(t)) => {
75 let u = graph.to_index(u_id);
76 low[u] = t;
77 disc[u] = t;
78 }
79 DfsEvent::TreeEdge(u_id, v_id, _) => {
80 let u = graph.to_index(u_id);
81 let v = graph.to_index(v_id);
82 parent[v] = u;
83 if is_root(&parent, u) {
84 root_children += 1;
85 }
86 if need_components {
87 edge_stack.push((u_id, v_id));
88 }
89 }
90 DfsEvent::BackEdge(u_id, v_id, _) => {
91 let u = graph.to_index(u_id);
92 let v = graph.to_index(v_id);
93
94 if v != parent[u] {
96 low[u] = low[u].min(disc[v]);
97 if need_components {
98 edge_stack.push((u_id, v_id));
99 }
100 }
101 }
102 DfsEvent::Finish(u_id, _) => {
103 let u = graph.to_index(u_id);
104 if is_root(&parent, u) {
105 if root_children > 1 {
106 points.insert(u_id);
107 }
108 root_children = 0;
110 } else {
111 let pu = parent[u];
112 let pu_id = graph.from_index(pu);
113 low[pu] = low[pu].min(low[u]);
114
115 if !is_root(&parent, pu) && low[u] >= disc[pu] {
116 points.insert(pu_id);
117 if need_components {
120 if let Some(at) = edge_stack.iter().rposition(|&x| x == (pu_id, u_id)) {
121 tmp_components.extend(
122 edge_stack[at..].iter().map(|edge| (*edge, num_components)),
123 );
124 edge_stack.truncate(at);
125 num_components += 1;
126 }
127 }
128 }
129
130 if need_bridges && low[u] > disc[pu] {
131 tmp_bridges.insert((pu_id, u_id));
132 }
133
134 if is_root(&parent, pu) && need_components {
135 if let Some(at) = edge_stack.iter().position(|&x| x == (pu_id, u_id)) {
136 tmp_components
137 .extend(edge_stack[at..].iter().map(|edge| (*edge, num_components)));
138 edge_stack.truncate(at);
139 num_components += 1;
140 }
141 }
142 }
143 }
144 _ => (),
145 });
146
147 if let Some(x) = components {
148 *x = tmp_components;
149 }
150 if let Some(x) = bridges {
151 *x = tmp_bridges;
152 }
153
154 points
155}
156
157pub fn articulation_points<G>(
202 graph: G,
203 components: Option<&mut HashMap<Edge<G>, usize>>,
204) -> HashSet<G::NodeId>
205where
206 G: GraphProp<EdgeType = Undirected>
207 + EdgeCount
208 + IntoEdges
209 + Visitable
210 + NodeIndexable
211 + IntoNodeIdentifiers,
212 G::NodeId: Eq + Hash,
213{
214 _articulation_points(graph, components, None)
215}
216
217pub fn bridges<G>(graph: G) -> HashSet<Edge<G>>
246where
247 G: GraphProp<EdgeType = Undirected>
248 + EdgeCount
249 + IntoEdges
250 + Visitable
251 + NodeIndexable
252 + IntoNodeIdentifiers,
253 G::NodeId: Eq + Hash,
254{
255 let mut bridges = HashSet::new();
256 _articulation_points(graph, None, Some(&mut bridges));
257 bridges
258}
259
260#[cfg(test)]
261mod tests {
262 use crate::connectivity::{articulation_points, bridges};
263 use hashbrown::{HashMap, HashSet};
264 use petgraph::graph::node_index as nx;
265 use petgraph::prelude::*;
266 use std::iter::FromIterator;
267
268 #[test]
269 fn test_articulation_points_repetitions() {
270 let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (1, 3)]);
271
272 let a_points = articulation_points(&graph, None);
273
274 assert_eq!(a_points, HashSet::from_iter([nx(1)]));
275 }
276
277 #[test]
278 fn test_articulation_points_cycle() {
279 let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
281
282 let a_points = articulation_points(&graph, None);
283
284 assert_eq!(a_points, HashSet::from_iter([nx(1)]));
285 }
286
287 #[test]
288 fn test_single_bridge() {
289 let graph = UnGraph::<(), ()>::from_edges([
290 (1, 2),
291 (2, 3),
292 (3, 4),
293 (3, 5),
294 (5, 6),
295 (6, 7),
296 (7, 8),
297 (5, 9),
298 (9, 10),
299 (1, 3),
301 (1, 4),
302 (2, 5),
303 (5, 10),
304 (6, 8),
305 ]);
306
307 assert_eq!(bridges(&graph), HashSet::from_iter([(nx(5), nx(6))]));
308 }
309
310 #[test]
311 fn test_bridges_cycle() {
313 let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
314 assert_eq!(bridges(&graph), HashSet::from_iter([]));
315 }
316
317 #[test]
318 fn test_biconnected_components_cycle() {
319 let graph = UnGraph::<(), ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3), (3, 4), (4, 1)]);
321
322 let mut components = HashMap::new();
323 let _ = articulation_points(&graph, Some(&mut components));
324
325 assert_eq!(
326 components,
327 HashMap::from_iter([
328 ((nx(1), nx(3)), 0),
329 ((nx(3), nx(4)), 0),
330 ((nx(4), nx(1)), 0),
331 ((nx(1), nx(2)), 1),
332 ((nx(2), nx(0)), 1),
333 ((nx(0), nx(1)), 1)
334 ])
335 );
336 }
337
338 #[test]
339 fn test_biconnected_components1() {
340 let graph = UnGraph::<(), ()>::from_edges([
342 (0, 1),
343 (0, 5),
344 (0, 6),
345 (0, 14),
346 (1, 5),
347 (1, 6),
348 (1, 14),
349 (2, 4),
350 (2, 10),
351 (3, 4),
352 (3, 15),
353 (4, 6),
354 (4, 7),
355 (4, 10),
356 (5, 14),
357 (6, 14),
358 (7, 9),
359 (8, 9),
360 (8, 12),
361 (8, 13),
362 (10, 15),
363 (11, 12),
364 (11, 13),
365 (12, 13),
366 ]);
367
368 let mut components = HashMap::new();
369 let a_points = articulation_points(&graph, Some(&mut components));
370
371 assert_eq!(
372 a_points,
373 HashSet::from_iter([nx(4), nx(6), nx(7), nx(8), nx(9)])
374 );
375 assert_eq!(
376 components,
377 HashMap::from_iter([
378 ((nx(3), nx(4)), 0),
379 ((nx(15), nx(3)), 0),
380 ((nx(10), nx(15)), 0),
381 ((nx(4), nx(10)), 0),
382 ((nx(10), nx(2)), 0),
383 ((nx(2), nx(4)), 0),
384 ((nx(13), nx(12)), 1),
385 ((nx(8), nx(13)), 1),
386 ((nx(11), nx(13)), 1),
387 ((nx(12), nx(11)), 1),
388 ((nx(12), nx(8)), 1),
389 ((nx(9), nx(8)), 2),
390 ((nx(7), nx(9)), 3),
391 ((nx(4), nx(7)), 4),
392 ((nx(6), nx(4)), 5),
393 ((nx(0), nx(14)), 6),
394 ((nx(1), nx(5)), 6),
395 ((nx(5), nx(0)), 6),
396 ((nx(5), nx(14)), 6),
397 ((nx(1), nx(14)), 6),
398 ((nx(14), nx(6)), 6),
399 ((nx(6), nx(0)), 6),
400 ((nx(6), nx(1)), 6),
401 ((nx(1), nx(0)), 6),
402 ])
403 )
404 }
405
406 #[test]
407 fn test_biconnected_components2() {
408 let mut graph: Graph<&str, (), Undirected> = Graph::new_undirected();
409 let a = graph.add_node("A");
410 let b = graph.add_node("B");
411 let c = graph.add_node("C");
412 let d = graph.add_node("D");
413 let e = graph.add_node("E");
414 let f = graph.add_node("F");
415 let g = graph.add_node("G");
416 let h = graph.add_node("H");
417 let i = graph.add_node("I");
418 let j = graph.add_node("J");
419
420 graph.extend_with_edges([
421 (a, b),
422 (b, c),
423 (c, a),
424 (c, d),
425 (d, e),
426 (e, c),
427 (f, i),
428 (i, j),
429 (j, h),
430 (h, g),
431 (g, f),
432 (g, i),
433 (g, j),
434 (e, g),
435 ]);
436
437 let mut components = HashMap::new();
438 let _ = articulation_points(&graph, Some(&mut components));
439
440 assert_eq!(
441 components,
442 HashMap::from_iter([
443 ((f, g), 0),
444 ((i, f), 0),
445 ((i, g), 0),
446 ((j, i), 0),
447 ((g, j), 0),
448 ((j, h), 0),
449 ((h, g), 0),
450 ((e, g), 1),
451 ((c, d), 2),
452 ((d, e), 2),
453 ((e, c), 2),
454 ((c, a), 3),
455 ((a, b), 3),
456 ((b, c), 3),
457 ])
458 )
459 }
460
461 #[test]
462 fn test_null_graph() {
463 let graph: Graph<(), (), Undirected> = Graph::new_undirected();
464
465 let mut components = HashMap::new();
466 let a_points = articulation_points(&graph, Some(&mut components));
467 let bridges = bridges(&graph);
468
469 assert_eq!(a_points, HashSet::new());
470 assert_eq!(bridges, HashSet::new());
471 assert_eq!(components, HashMap::new());
472 }
473}