cosmolkit_ringdecomposer/lib.rs
1use std::collections::BTreeMap;
2
3#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
4pub enum RingDecomposerError {
5 #[error("node index {node} out of range for graph with {node_count} nodes")]
6 NodeOutOfRange { node: usize, node_count: usize },
7 #[error("self-loop at node {node} is not allowed")]
8 SelfLoop { node: usize },
9 #[error("duplicate edge between {from} and {to}")]
10 DuplicateEdge { from: usize, to: usize },
11 #[error("graph has no nodes")]
12 EmptyGraph,
13 #[error("edge not found between node {from} and node {to}")]
14 EdgeNotFound { from: usize, to: usize },
15 #[error("unsupported RingDecomposerLib branch: {reason}")]
16 UnsupportedBranch { reason: &'static str },
17 #[error("internal invariant violation: {message}")]
18 InvariantViolation { message: &'static str },
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct EdgeId(usize);
23
24impl EdgeId {
25 #[must_use]
26 pub const fn new(index: usize) -> Self {
27 Self(index)
28 }
29
30 #[must_use]
31 pub const fn index(self) -> usize {
32 self.0
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct Edge {
38 id: EdgeId,
39 from: usize,
40 to: usize,
41}
42
43impl Edge {
44 #[must_use]
45 pub const fn id(self) -> EdgeId {
46 self.id
47 }
48
49 #[must_use]
50 pub const fn from(self) -> usize {
51 self.from
52 }
53
54 #[must_use]
55 pub const fn to(self) -> usize {
56 self.to
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct Graph {
62 node_count: usize,
63 adjacency: Vec<Vec<(usize, EdgeId)>>,
64 edges: Vec<Edge>,
65 edge_ids_by_endpoints: BTreeMap<(usize, usize), EdgeId>,
66}
67
68impl Graph {
69 #[must_use]
70 pub fn new(node_count: usize) -> Self {
71 // BEGIN RDL C FUNCTION RDL_initNewGraph
72 // RDL✔️✔️: RDL_graph *RDL_initNewGraph(unsigned V)
73 // RDL✔️✔️: {
74 // RDL✔️✔️: return RDL_initNewGraph_g(V, 1);
75 // RDL✔️✔️: }
76 // END RDL C FUNCTION RDL_initNewGraph
77 //
78 // BEGIN RDL C FUNCTION RDL_initNewGraph_g
79 // RDL❗✔️: RDL_graph *RDL_initNewGraph_g(unsigned V, char owns_edges)
80 // RDL❗✔️: {
81 // RDL❗✔️: RDL_graph *graph = malloc(sizeof(*graph));
82 // RDL❗✔️: unsigned *degree;
83 // RDL❗✔️: unsigned i;
84 // RDL❗✔️: degree = malloc(V * sizeof(*degree));
85 // RDL❗✔️: for(i=0; i<V; ++i)
86 // RDL❗✔️: {
87 // RDL❗✔️: degree[i] = 0;
88 // RDL❗✔️: }
89 // RDL❗✔️: RDL_initGraph(graph,V,degree,owns_edges);
90 // RDL❗✔️:
91 // RDL❗✔️: return graph;
92 // RDL❗✔️: }
93 // END RDL C FUNCTION RDL_initNewGraph_g
94 Self {
95 node_count,
96 adjacency: vec![Vec::new(); node_count],
97 edges: Vec::new(),
98 edge_ids_by_endpoints: BTreeMap::new(),
99 }
100 }
101
102 #[must_use]
103 pub const fn node_count(&self) -> usize {
104 self.node_count
105 }
106
107 #[must_use]
108 pub fn edge_count(&self) -> usize {
109 self.edges.len()
110 }
111
112 #[must_use]
113 pub fn edges(&self) -> &[Edge] {
114 &self.edges
115 }
116
117 #[must_use]
118 pub fn neighbors(&self, node: usize) -> Option<&[(usize, EdgeId)]> {
119 self.adjacency.get(node).map(Vec::as_slice)
120 }
121
122 pub fn add_undirected_edge(
123 &mut self,
124 from: usize,
125 to: usize,
126 ) -> Result<EdgeId, RingDecomposerError> {
127 // BEGIN RDL C FUNCTION RDL_addUEdge
128 // RDL✔️✔️: unsigned RDL_addUEdge(RDL_graph *gra, RDL_node from, RDL_node to)
129 // RDL✔️✔️: {
130 // RDL✔️✔️: return RDL_addUEdge_g(gra, from, to);
131 // RDL✔️✔️: }
132 // END RDL C FUNCTION RDL_addUEdge
133 //
134 // BEGIN RDL C FUNCTION RDL_addUEdge_g
135 // RDL✔️✔️: unsigned RDL_addUEdge_g(RDL_graph *gra, unsigned from, unsigned to)
136 // RDL✔️✔️: {
137 // RDL✔️✔️: unsigned edge_id, i;
138 // RDL✔️✔️:
139 // RDL✔️✔️: if(from >= gra->V || to >= gra->V) {
140 // RDL✔️✔️: RDL_outputFunc(RDL_ERROR, "Tried to add an edge with atoms not in range.\n");
141 // RDL✔️✔️: RDL_outputFunc(RDL_ERROR, "edge (%u,%u) can not be added to graph with %u atoms.\n",
142 // RDL✔️✔️: from, to, gra->V);
143 // RDL✔️✔️: return RDL_INVALID_RESULT;
144 // RDL✔️✔️: }
145 if from >= self.node_count {
146 return Err(RingDecomposerError::NodeOutOfRange {
147 node: from,
148 node_count: self.node_count,
149 });
150 }
151 if to >= self.node_count {
152 return Err(RingDecomposerError::NodeOutOfRange {
153 node: to,
154 node_count: self.node_count,
155 });
156 }
157 // RDL✔️✔️:
158 // RDL✔️✔️: if (from == to) {
159 // RDL✔️✔️: RDL_outputFunc(RDL_WARNING, "Adding a loop is not allowed, node %u\n", from);
160 // RDL✔️✔️: return RDL_INVALID_RESULT;
161 // RDL✔️✔️: }
162 if from == to {
163 return Err(RingDecomposerError::SelfLoop { node: from });
164 }
165 let (left, right) = canonical_edge_key(from, to);
166 // RDL✔️✔️:
167 // RDL✔️✔️: for(i=0; i<gra->degree[from]; ++i) {
168 // RDL✔️✔️: if(gra->adjList[from][i][0] == to) {
169 // RDL✔️✔️: /*edge already exists*/
170 // RDL✔️✔️: return RDL_DUPLICATE_EDGE;
171 // RDL✔️✔️: }
172 // RDL✔️✔️: }
173 if self.edge_ids_by_endpoints.contains_key(&(left, right)) {
174 return Err(RingDecomposerError::DuplicateEdge { from, to });
175 }
176 // RDL✔️✔️: RDL_addEdge(gra, from, to);
177 // RDL✔️✔️: RDL_addEdge(gra, to, from);
178 // RDL✔️✔️: --gra->E; /*was incremented twice*/
179 // RDL✔️✔️:
180 // RDL✔️✔️: edge_id = RDL_addToEdgeArray(gra, from, to);
181 let edge_id = EdgeId::new(self.edges.len());
182 self.edges.push(Edge {
183 id: edge_id,
184 from: left,
185 to: right,
186 });
187 self.edge_ids_by_endpoints.insert((left, right), edge_id);
188 // RDL✔️✔️:
189 // RDL✔️✔️: gra->adjList[from][gra->degree[from]-1][1] = edge_id;
190 // RDL✔️✔️: gra->adjList[to][gra->degree[to]-1][1] = edge_id;
191 self.adjacency[from].push((to, edge_id));
192 self.adjacency[to].push((from, edge_id));
193 // RDL✔️✔️:
194 // RDL✔️✔️: return edge_id;
195 // RDL✔️✔️: }
196 // END RDL C FUNCTION RDL_addUEdge_g
197 Ok(edge_id)
198 }
199
200 pub fn edge_id(&self, from: usize, to: usize) -> Result<EdgeId, RingDecomposerError> {
201 // BEGIN RDL C FUNCTION RDL_edgeId
202 // RDL✔️✔️: unsigned RDL_edgeId(const RDL_graph *gra, unsigned from, unsigned to)
203 // RDL✔️✔️: {
204 // RDL✔️✔️: unsigned j, edge;
205 // RDL✔️✔️:
206 // RDL✔️✔️: if(from > to) {
207 // RDL✔️✔️: /*swap order to make from < to*/
208 // RDL✔️✔️: edge = to;
209 // RDL✔️✔️: to = from;
210 // RDL✔️✔️: from = edge;
211 // RDL✔️✔️: }
212 let (left, right) = canonical_edge_key(from, to);
213 // RDL✔️✔️:
214 // RDL✔️✔️: edge = RDL_INVALID_RESULT;
215 // RDL✔️✔️:
216 // RDL✔️✔️: for(j=0; j<gra->degree[from]; ++j) {
217 // RDL✔️✔️: if(gra->adjList[from][j][0] == to) {
218 // RDL✔️✔️: edge = gra->adjList[from][j][1];
219 // RDL✔️✔️: break;
220 // RDL✔️✔️: }
221 // RDL✔️✔️: }
222 // RDL✔️✔️:
223 // RDL✔️✔️: return edge;
224 // RDL✔️✔️: }
225 // END RDL C FUNCTION RDL_edgeId
226 self.edge_ids_by_endpoints
227 .get(&(left, right))
228 .copied()
229 .ok_or(RingDecomposerError::EdgeNotFound { from, to })
230 }
231
232 #[must_use]
233 pub fn is_adjacent(&self, from: usize, to: usize) -> bool {
234 // BEGIN RDL C FUNCTION RDL_isAdj
235 // RDL✔️✔️: int RDL_isAdj(const RDL_graph *graph, unsigned i, unsigned j)
236 // RDL✔️✔️: {
237 // RDL✔️✔️: unsigned idx;
238 // RDL✔️✔️: for(idx=0; idx<graph->degree[i]; ++idx)
239 // RDL✔️✔️: {
240 // RDL✔️✔️: if(graph->adjList[i][idx][0] == j)
241 // RDL✔️✔️: {
242 // RDL✔️✔️: return 1;
243 // RDL✔️✔️: }
244 // RDL✔️✔️: }
245 // RDL✔️✔️: return 0;
246 // RDL✔️✔️: }
247 // END RDL C FUNCTION RDL_isAdj
248 self.adjacency
249 .get(from)
250 .is_some_and(|neighbors| neighbors.iter().any(|(node, _)| *node == to))
251 }
252
253 #[must_use]
254 pub fn is_connected(&self) -> bool {
255 // BEGIN RDL C FUNCTION RDL_checkGraphConnected
256 // RDL✔️✔️: char RDL_checkGraphConnected(const RDL_graph *gra)
257 // RDL✔️✔️: {
258 // RDL✔️✔️: unsigned i;
259 // RDL✔️✔️: char *visited;
260 // RDL✔️✔️: char result;
261 // RDL✔️✔️: result = 1;
262 // RDL✔️✔️: visited = malloc(gra->V * sizeof(*visited));
263 // RDL✔️✔️: visited[0] = 1; /*start vertex*/
264 if self.node_count == 0 {
265 return true;
266 }
267 let mut visited = vec![false; self.node_count];
268 let mut stack = vec![0usize];
269 visited[0] = true;
270 while let Some(node) = stack.pop() {
271 for &(neighbor, _) in &self.adjacency[node] {
272 if !visited[neighbor] {
273 visited[neighbor] = true;
274 stack.push(neighbor);
275 }
276 }
277 }
278 // RDL✔️✔️: for(i=1; i<gra->V; ++i)
279 // RDL✔️✔️: {
280 // RDL✔️✔️: visited[i] = 0; /*unvisited*/
281 // RDL✔️✔️: }
282 // RDL✔️✔️: RDL_DFSvisit(gra, 0, visited);
283 // RDL✔️✔️: for(i=0; i<gra->V; ++i)
284 // RDL✔️✔️: {
285 // RDL✔️✔️: if(visited[i] == 0) /*one was unvisited*/
286 // RDL✔️✔️: {
287 // RDL✔️✔️: result = 0;
288 // RDL✔️✔️: break;
289 // RDL✔️✔️: }
290 // RDL✔️✔️: }
291 // RDL✔️✔️:
292 // RDL✔️✔️: free(visited);
293 // RDL✔️✔️: return result;
294 // RDL✔️✔️: }
295 // END RDL C FUNCTION RDL_checkGraphConnected
296 visited.into_iter().all(|is_visited| is_visited)
297 }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
301pub struct DirectedPathGraph {
302 node_count: usize,
303 adjacency: Vec<Vec<usize>>,
304}
305
306impl DirectedPathGraph {
307 #[must_use]
308 pub fn new(node_count: usize) -> Self {
309 Self {
310 node_count,
311 adjacency: vec![Vec::new(); node_count],
312 }
313 }
314
315 fn add_directed_edge(&mut self, from: usize, to: usize) {
316 // BEGIN RDL C FUNCTION RDL_addEdge
317 // RDL✔️✔️: void RDL_addEdge(RDL_graph *gra, unsigned from, unsigned to)
318 // RDL✔️✔️: {
319 // RDL✔️✔️: unsigned i;
320 // RDL✔️✔️:
321 // RDL✔️✔️: /* loops */
322 // RDL✔️✔️: if (from == to) {
323 // RDL✔️✔️: return;
324 // RDL✔️✔️: }
325 if from == to || from >= self.node_count || to >= self.node_count {
326 return;
327 }
328 // RDL✔️✔️:
329 // RDL✔️✔️: for(i=0; i<gra->degree[from]; ++i) {
330 // RDL✔️✔️: if(gra->adjList[from][i][0] == to) {
331 // RDL✔️✔️: /*edge already exists*/
332 // RDL✔️✔️: return;
333 // RDL✔️✔️: }
334 // RDL✔️✔️: }
335 if self.adjacency[from].contains(&to) {
336 return;
337 }
338 // RDL✔️✔️: ++gra->E;
339 // RDL✔️✔️: ++gra->degree[from];
340 // RDL✔️✔️: ...
341 // RDL✔️✔️: gra->adjList[from][ gra->degree[from]-1 ][0] = to;
342 // RDL✔️✔️: }
343 // END RDL C FUNCTION RDL_addEdge
344 self.adjacency[from].push(to);
345 }
346
347 #[must_use]
348 pub const fn node_count(&self) -> usize {
349 self.node_count
350 }
351
352 #[must_use]
353 pub fn neighbors(&self, node: usize) -> Option<&[usize]> {
354 self.adjacency.get(node).map(Vec::as_slice)
355 }
356}
357
358#[derive(Debug, Clone, PartialEq, Eq)]
359pub struct BiconnectedComponents {
360 components: Vec<BiconnectedComponent>,
361 edge_to_component: Vec<Option<(usize, usize)>>,
362 node_to_components: Vec<Vec<(usize, usize)>>,
363}
364
365impl BiconnectedComponents {
366 #[must_use]
367 pub fn calculate(graph: &Graph) -> Self {
368 tarjan_biconnected_components(graph)
369 }
370
371 #[must_use]
372 pub fn components(&self) -> &[BiconnectedComponent] {
373 &self.components
374 }
375
376 #[must_use]
377 pub fn component_count(&self) -> usize {
378 self.components.len()
379 }
380
381 #[must_use]
382 pub fn edge_component(&self, edge: EdgeId) -> Option<(usize, usize)> {
383 self.edge_to_component.get(edge.index()).copied().flatten()
384 }
385
386 #[must_use]
387 pub fn node_components(&self, node: usize) -> Option<&[(usize, usize)]> {
388 self.node_to_components.get(node).map(Vec::as_slice)
389 }
390}
391
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub struct BiconnectedComponent {
394 graph: Graph,
395 original_nodes: Vec<usize>,
396 original_edges: Vec<EdgeId>,
397}
398
399impl BiconnectedComponent {
400 #[must_use]
401 pub const fn graph(&self) -> &Graph {
402 &self.graph
403 }
404
405 #[must_use]
406 pub fn original_nodes(&self) -> &[usize] {
407 &self.original_nodes
408 }
409
410 #[must_use]
411 pub fn original_edges(&self) -> &[EdgeId] {
412 &self.original_edges
413 }
414}
415
416#[derive(Debug, Clone, PartialEq, Eq)]
417pub struct ShortestPathInfo {
418 predecessor: Vec<Vec<Option<usize>>>,
419 distance: Vec<Vec<Option<usize>>>,
420 reachable: Vec<Vec<bool>>,
421 directed_paths: Vec<DirectedPathGraph>,
422}
423
424impl ShortestPathInfo {
425 #[must_use]
426 pub fn calculate(graph: &Graph) -> Self {
427 // BEGIN RDL C FUNCTION RDL_AllPairsShortestPaths
428 // RDL✔️✔️: RDL_sPathInfo *RDL_AllPairsShortestPaths(RDL_graph *gra)
429 // RDL✔️✔️: {
430 // RDL✔️✔️: RDL_sPathInfo *info = malloc(sizeof(*info));
431 // RDL✔️✔️:
432 // RDL✔️✔️: RDL_initializeSPathInfo(info, gra);
433 // RDL✔️✔️: RDL_findpaths(info, gra);
434 // RDL✔️✔️:
435 // RDL✔️✔️: return info;
436 // RDL✔️✔️: }
437 // END RDL C FUNCTION RDL_AllPairsShortestPaths
438 let mut info = Self::initialized_for_graph(graph);
439 info.find_paths(graph);
440 info
441 }
442
443 fn initialized_for_graph(graph: &Graph) -> Self {
444 // BEGIN RDL C FUNCTION RDL_initializeSPathInfo
445 // RDL✔️✔️: static void RDL_initializeSPathInfo(RDL_sPathInfo *info, RDL_graph *gra)
446 // RDL✔️✔️: {
447 // RDL✔️✔️: unsigned i;
448 // RDL✔️✔️: info->pred = RDL_alloc2DUIntArray(gra->V, gra->V);
449 // RDL✔️✔️: info->dist = RDL_alloc2DUIntArray(gra->V, gra->V);
450 // RDL✔️✔️: info->reachable = RDL_alloc2DCharArray(gra->V, gra->V);
451 // RDL✔️✔️: info->dPaths = malloc(gra->V * sizeof(*info->dPaths));
452 // RDL✔️✔️: for(i=0; i<gra->V; ++i)
453 // RDL✔️✔️: {
454 // RDL✔️✔️: info->dPaths[i] = RDL_initNewGraph_g(gra->V, 0);
455 // RDL✔️✔️: }
456 // RDL✔️✔️: }
457 // END RDL C FUNCTION RDL_initializeSPathInfo
458 let node_count = graph.node_count();
459 Self {
460 predecessor: vec![vec![None; node_count]; node_count],
461 distance: vec![vec![None; node_count]; node_count],
462 reachable: vec![vec![false; node_count]; node_count],
463 directed_paths: vec![DirectedPathGraph::new(node_count); node_count],
464 }
465 }
466
467 fn find_paths(&mut self, graph: &Graph) {
468 // BEGIN RDL C FUNCTION RDL_findpaths
469 // RDL✔️✔️: static void RDL_findpaths(RDL_sPathInfo *spi, RDL_graph *gra)
470 // RDL✔️✔️: {
471 // RDL✔️✔️: const unsigned INFINITY = UINT_MAX;
472 // RDL✔️✔️: unsigned i,j,w,adj,run;
473 // RDL✔️✔️: unsigned q_head, q_nextfree, q_size;
474 // RDL✔️✔️: char *color = malloc(gra->V * sizeof(*color));
475 // RDL✔️✔️: unsigned *queue = malloc(gra->V * sizeof(*queue));
476 // RDL✔️✔️:
477 // RDL✔️✔️: for(run=1; run<3; ++run) /*2 run throughs to get Vr correct*/
478 // RDL✔️✔️: {
479 let node_count = graph.node_count();
480 for run in 1..3 {
481 // RDL✔️✔️: for(i=0; i<gra->V; ++i)
482 for source in 0..node_count {
483 // RDL✔️✔️: q_head=0;
484 // RDL✔️✔️: q_nextfree=0;
485 // RDL✔️✔️: q_size=0;
486 let mut queue = Vec::with_capacity(node_count);
487 let mut queue_head = 0usize;
488 let mut color = vec!['w'; node_count];
489 // RDL✔️✔️: for(j=0; j<gra->V; ++j)
490 // RDL✔️✔️: {
491 // RDL✔️✔️: if(run==1)
492 // RDL✔️✔️: {
493 if run == 1 {
494 for target in 0..node_count {
495 // RDL✔️✔️: spi->dist[i][j] = INFINITY;
496 // RDL✔️✔️: spi->pred[i][j] = INFINITY;
497 // RDL✔️✔️: spi->reachable[i][j] = 0;
498 self.distance[source][target] = None;
499 self.predecessor[source][target] = None;
500 self.reachable[source][target] = false;
501 }
502 }
503 // RDL✔️✔️: }
504 // RDL✔️✔️: color[j] = 'w'; /*white*/
505 // RDL✔️✔️: }
506 // RDL✔️✔️: spi->dist[i][i] = 0;
507 // RDL✔️✔️: spi->pred[i][i] = i;
508 self.distance[source][source] = Some(0);
509 self.predecessor[source][source] = Some(source);
510 // RDL✔️✔️: color[i] = 'b'; /*black*/
511 color[source] = 'b';
512 // RDL✔️✔️: queue[q_nextfree]=i; /*enqueue*/
513 // RDL✔️✔️: ++q_nextfree; /*enqueue*/
514 // RDL✔️✔️: ++q_size; /*enqueue*/
515 queue.push(source);
516 // RDL✔️✔️:
517 // RDL✔️✔️: while(q_size > 0)
518 while queue_head < queue.len() {
519 // RDL✔️✔️: w = queue[q_head]; /*deqeue*/
520 // RDL✔️✔️: ++q_head; /*dequeue*/
521 // RDL✔️✔️: --q_size; /*dequeue*/
522 let current = queue[queue_head];
523 queue_head += 1;
524 // RDL✔️✔️: for(adj=0; adj<gra->degree[w]; ++adj) /*for each node adj to w*/
525 for &(neighbor, _) in &graph.adjacency[current] {
526 // RDL✔️✔️: j=gra->adjList[w][adj][0];
527 // RDL✔️✔️: /*if j precedes i in order (only first run)*/
528 // RDL✔️✔️: if(run==2 || ((gra->degree[j] == gra->degree[i]) && j<i) ||
529 // RDL✔️✔️: gra->degree[j] < gra->degree[i])
530 if run != 2
531 && !((graph.adjacency[neighbor].len() == graph.adjacency[source].len()
532 && neighbor < source)
533 || graph.adjacency[neighbor].len() < graph.adjacency[source].len())
534 {
535 continue;
536 }
537 // RDL✔️✔️: if(color[j] == 'w') /*unvisited*/
538 if color[neighbor] != 'w' {
539 continue;
540 }
541 // RDL✔️✔️: {
542 let candidate_distance =
543 self.distance[source][current].expect("queued vertex is reachable") + 1;
544 // RDL✔️✔️: if(run==2)
545 if run == 2 {
546 // RDL✔️✔️: {/*if in the 2nd run a dist gets shorter*/
547 // RDL✔️✔️: if(spi->dist[i][w]+1 < spi->dist[i][j])
548 // RDL✔️✔️: {/*not element of Vr*/
549 // RDL✔️✔️: spi->reachable[i][j] = 0;
550 // RDL✔️✔️: }
551 if self.distance[source][neighbor]
552 .is_none_or(|distance| candidate_distance < distance)
553 {
554 self.reachable[source][neighbor] = false;
555 }
556 }
557 // RDL✔️✔️: }
558 // RDL✔️✔️: if(run==1 || (spi->dist[i][w]+1 < spi->dist[i][j]))
559 // RDL✔️✔️: {/*if 2nd run and dist stays the same, pred shouldn't
560 // RDL✔️✔️: change to keep the shortest path along ordering*/
561 if run == 1
562 || self.distance[source][neighbor]
563 .is_none_or(|distance| candidate_distance < distance)
564 {
565 // RDL✔️✔️: /*predecessor of j on a shortest path from i to j*/
566 // RDL✔️✔️: spi->pred[i][j] = w;
567 self.predecessor[source][neighbor] = Some(current);
568 }
569 // RDL✔️✔️: }
570 // RDL✔️✔️: if(spi->dist[i][j] > spi->dist[i][w]+1)
571 // RDL✔️✔️: {
572 // RDL✔️✔️: spi->dist[i][j] = spi->dist[i][w] + 1;
573 // RDL✔️✔️: }
574 if self.distance[source][neighbor]
575 .is_none_or(|distance| candidate_distance < distance)
576 {
577 self.distance[source][neighbor] = Some(candidate_distance);
578 }
579 // RDL✔️✔️: color[j] = 'b';
580 color[neighbor] = 'b';
581 // RDL✔️✔️: queue[q_nextfree] = j; /*enqueue*/
582 // RDL✔️✔️: ++q_nextfree; /*enqueue*/
583 // RDL✔️✔️: ++q_size; /*enqueue*/
584 queue.push(neighbor);
585 // RDL✔️✔️: if(run==1)
586 // RDL✔️✔️: {/*reachable should not change to 1 in the 2nd run*/
587 // RDL✔️✔️: spi->reachable[i][j] = 1;
588 // RDL✔️✔️: }
589 if run == 1 {
590 self.reachable[source][neighbor] = true;
591 }
592 }
593 // RDL✔️✔️: }
594 // RDL✔️✔️: }
595 // RDL✔️✔️: }
596 }
597 }
598 // RDL✔️✔️: }
599 // RDL✔️✔️: }
600 }
601 // RDL✔️✔️: }
602 // END RDL C FUNCTION RDL_findpaths
603 }
604
605 #[must_use]
606 pub fn predecessor(&self, from: usize, to: usize) -> Option<usize> {
607 self.predecessor
608 .get(from)
609 .and_then(|row| row.get(to))
610 .copied()
611 .flatten()
612 }
613
614 #[must_use]
615 pub fn distance(&self, from: usize, to: usize) -> Option<usize> {
616 self.distance
617 .get(from)
618 .and_then(|row| row.get(to))
619 .copied()
620 .flatten()
621 }
622
623 #[must_use]
624 pub fn reachable_preceding(&self, from: usize, to: usize) -> bool {
625 self.reachable
626 .get(from)
627 .and_then(|row| row.get(to))
628 .copied()
629 .unwrap_or(false)
630 }
631
632 #[must_use]
633 pub fn directed_path_graphs(&self) -> &[DirectedPathGraph] {
634 &self.directed_paths
635 }
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub struct CycleFamily {
640 weight: usize,
641 r: usize,
642 p: usize,
643 q: usize,
644 x: Option<usize>,
645 prototype: Vec<bool>,
646 relevant: bool,
647}
648
649impl CycleFamily {
650 #[must_use]
651 pub const fn weight(&self) -> usize {
652 self.weight
653 }
654
655 #[must_use]
656 pub const fn root(&self) -> usize {
657 self.r
658 }
659
660 #[must_use]
661 pub const fn p(&self) -> usize {
662 self.p
663 }
664
665 #[must_use]
666 pub const fn q(&self) -> usize {
667 self.q
668 }
669
670 #[must_use]
671 pub const fn x(&self) -> Option<usize> {
672 self.x
673 }
674
675 #[must_use]
676 pub fn prototype(&self) -> &[bool] {
677 &self.prototype
678 }
679
680 #[must_use]
681 pub const fn is_relevant(&self) -> bool {
682 self.relevant
683 }
684}
685
686#[derive(Debug, Clone, PartialEq, Eq)]
687pub struct CycleFamilies {
688 families: Vec<CycleFamily>,
689}
690
691impl CycleFamilies {
692 #[must_use]
693 pub fn calculate(graph: &mut Graph, shortest_paths: &mut ShortestPathInfo) -> Self {
694 find_cycle_families(graph, shortest_paths)
695 }
696
697 #[must_use]
698 pub fn families(&self) -> &[CycleFamily] {
699 &self.families
700 }
701
702 #[must_use]
703 pub fn len(&self) -> usize {
704 self.families.len()
705 }
706
707 #[must_use]
708 pub fn is_empty(&self) -> bool {
709 self.families.is_empty()
710 }
711}
712
713#[derive(Debug, Clone, PartialEq, Eq)]
714struct UrfInfo {
715 nof_protos: Vec<usize>,
716 relations: Vec<Vec<Vec<bool>>>,
717 urfs: Vec<Vec<usize>>,
718}
719
720impl UrfInfo {
721 fn check_urf_relation(
722 cycle_families: &mut CycleFamilies,
723 graph: &Graph,
724 shortest_paths: &ShortestPathInfo,
725 ) -> Self {
726 // BEGIN RDL C FUNCTION RDL_checkURFRelation
727 // RDL❗✔️: RDL_URFinfo *RDL_checkURFRelation(RDL_cfURF *RCFs, RDL_graph *graph, RDL_sPathInfo* spi)
728 // RDL❗✔️: {
729 // RDL❗✔️: RDL_URFinfo *uInfo = RDL_initUrfInfo(RCFs, graph);
730 let mut info = Self::init_urf_info(cycle_families);
731 // RDL❗✔️: RDL_findRelations(RCFs, graph, uInfo, spi);
732 info.find_relations(cycle_families, graph, shortest_paths);
733 // RDL❗✔️:
734 // RDL❗✔️: uInfo->nofURFs = RDL_countURFs(uInfo);
735 // RDL❗✔️: RDL_fillURFs(uInfo, RCFs);
736 info.fill_urfs();
737 // RDL❗✔️: return uInfo;
738 // RDL❗✔️: }
739 // END RDL C FUNCTION RDL_checkURFRelation
740 info
741 }
742
743 fn init_urf_info(cycle_families: &CycleFamilies) -> Self {
744 // BEGIN RDL C FUNCTION RDL_initUrfInfo
745 // RDL❗✔️: RDL_URFinfo *RDL_initUrfInfo(RDL_cfURF *RCFs, RDL_graph *graph)
746 // RDL❗✔️: {
747 // RDL❗✔️: RDL_URFinfo *urfInfo;
748 // RDL❗✔️: char ***URFrel;
749 // RDL❗✔️: unsigned numOfWeights = 1;
750 // RDL❗✔️: unsigned *numOfProtos;
751 // RDL❗✔️: unsigned i, j, k, currWeight, currIdx;
752 if cycle_families.is_empty() {
753 return Self {
754 nof_protos: Vec::new(),
755 relations: Vec::new(),
756 urfs: Vec::new(),
757 };
758 }
759 // RDL❗✔️:
760 // RDL❗✔️: /* count number of different weights occuring*/
761 let mut nof_protos = Vec::<usize>::new();
762 let mut current_weight = cycle_families.families[0].weight();
763 nof_protos.push(0);
764 for family in cycle_families.families() {
765 if family.weight() != current_weight {
766 current_weight = family.weight();
767 nof_protos.push(0);
768 }
769 *nof_protos
770 .last_mut()
771 .expect("weight bucket exists after initialization") += 1;
772 }
773 // RDL❗✔️:
774 // RDL❗✔️: /*allocate everything*/
775 // RDL❗✔️: /*initialize with zeros*/
776 let relations = nof_protos
777 .iter()
778 .map(|&count| vec![vec![false; count]; count])
779 .collect::<Vec<_>>();
780 // RDL❗✔️:
781 // RDL❗✔️: urfInfo->nofWeights = numOfWeights;
782 // RDL❗✔️: urfInfo->nofProtos = numOfProtos;
783 // RDL❗✔️: urfInfo->URFrel = URFrel;
784 // RDL❗✔️:
785 // RDL❗✔️: return urfInfo;
786 // RDL❗✔️: }
787 // END RDL C FUNCTION RDL_initUrfInfo
788 Self {
789 nof_protos,
790 relations,
791 urfs: Vec::new(),
792 }
793 }
794
795 fn idx_weight(&self, weight: usize, index: usize) -> usize {
796 // BEGIN RDL C FUNCTION RDL_idxWeight
797 // RDL✔️✔️: unsigned RDL_idxWeight(RDL_URFinfo *uInfo, unsigned weight, unsigned j)
798 // RDL✔️✔️: {
799 // RDL✔️✔️: unsigned i, sum = 0;
800 // RDL✔️✔️: for(i=0; i<weight; ++i)
801 // RDL✔️✔️: {
802 // RDL✔️✔️: sum += uInfo->nofProtos[i];
803 // RDL✔️✔️: }
804 // RDL✔️✔️: return sum + j;
805 // RDL✔️✔️: }
806 // END RDL C FUNCTION RDL_idxWeight
807 self.nof_protos.iter().take(weight).sum::<usize>() + index
808 }
809
810 fn find_relations(
811 &mut self,
812 cycle_families: &mut CycleFamilies,
813 graph: &Graph,
814 shortest_paths: &ShortestPathInfo,
815 ) {
816 // BEGIN RDL C FUNCTION RDL_findRelations
817 // RDL✔️✔️: void RDL_findRelations(RDL_cfURF *RCFs, RDL_graph *graph,
818 // RDL✔️✔️: RDL_URFinfo *uInfo, RDL_sPathInfo* spi)
819 // RDL✔️✔️: {
820 // RDL✔️✔️: RDL_checkDependencies(RCFs, graph, uInfo);
821 self.check_dependencies(cycle_families, graph);
822 // RDL✔️✔️: RDL_checkEdges(RCFs, graph, uInfo, spi);
823 self.check_edges(cycle_families, graph, shortest_paths);
824 // RDL✔️✔️: RDL_findTransitiveClosure(uInfo);
825 self.find_transitive_closure();
826 // RDL✔️✔️: }
827 // END RDL C FUNCTION RDL_findRelations
828 }
829
830 fn check_dependencies(&mut self, cycle_families: &mut CycleFamilies, graph: &Graph) {
831 // BEGIN RDL C FUNCTION RDL_checkDependencies
832 // RDL❗✔️: void RDL_checkDependencies(RDL_cfURF *RCFs, RDL_graph *graph, RDL_URFinfo *uInfo)
833 // RDL❗✔️: {
834 // RDL❗✔️: if(RCFs->nofFams < 3) {
835 if cycle_families.len() < 3 {
836 // RDL❗✔️: /*if only 0, 1 or 2 families exist, they are all relevant and independent*/
837 for weight in 0..self.nof_protos.len() {
838 for index in 0..self.nof_protos[weight] {
839 self.relations[weight][index][index] = true;
840 }
841 }
842 for family in &mut cycle_families.families {
843 family.relevant = true;
844 }
845 // RDL❗✔️: return;
846 return;
847 }
848 // RDL❗✔️: }
849 let mut basis_cycles = Vec::<Vec<bool>>::with_capacity(
850 graph
851 .edge_count()
852 .saturating_sub(graph.node_count())
853 .saturating_add(1),
854 );
855 let mut relevant_cycles = Vec::<Vec<bool>>::new();
856 let mut relevant_cycles_map = Vec::<usize>::new();
857 let mut prototypes_for_gaussian = cycle_families
858 .families()
859 .iter()
860 .map(|family| family.prototype().to_vec())
861 .collect::<Vec<_>>();
862 // RDL❗✔️:
863 // RDL❗✔️: /* iterate over all weights */
864 for weight in 0..self.nof_protos.len() {
865 // RDL❗✔️: currentBasisCyclesSmallerEnd = currentBasisCyclesSize;
866 // RDL❗✔️: currentRelevantCyclesSmallerEnd = currentRelevantCyclesSize;
867 let current_basis_cycles_smaller_end = basis_cycles.len();
868 let current_relevant_cycles_smaller_end = relevant_cycles.len();
869 for index_in_weight in 0..self.nof_protos[weight] {
870 // RDL❗✔️: index = RDL_idxWeights(uInfo, i, j);
871 let family_index = self.idx_weight(weight, index_in_weight);
872 // RDL❗✔️: /* copy current cycle */
873 let mut cycle_with_smaller_added = prototypes_for_gaussian[family_index].clone();
874 // RDL❗✔️: /* add smaller cycles of the basis (set B<) */
875 for pivot in 0..current_basis_cycles_smaller_end {
876 if cycle_with_smaller_added[pivot] {
877 xor_bool_slice(&mut cycle_with_smaller_added, &basis_cycles[pivot]);
878 }
879 }
880 // RDL❗✔️:
881 // RDL❗✔️: if (RDL_bitset_empty(compressedCycleWithSmallerAdded, empty_cycle, compressedSize)) {
882 if !cycle_with_smaller_added.iter().any(|&bit| bit) {
883 // RDL❗✔️: continue;
884 continue;
885 }
886 // RDL❗✔️:
887 // RDL❗✔️: /* this cycle does not depend on strictly smaller cycles => relevant */
888 relevant_cycles.push(cycle_with_smaller_added.clone());
889 relevant_cycles_map.push(index_in_weight);
890 cycle_families.families[family_index].relevant = true;
891 self.relations[weight][index_in_weight][index_in_weight] = true;
892 // RDL❗✔️:
893 // RDL❗✔️: /* make a copy for adding the equal sized cycles */
894 let mut cycle_with_equal_added = cycle_with_smaller_added.clone();
895 // RDL❗✔️: /* add equal sized cycles of the basis (set B=) */
896 for pivot in current_basis_cycles_smaller_end..basis_cycles.len() {
897 if cycle_with_equal_added[pivot] {
898 xor_bool_slice(&mut cycle_with_equal_added, &basis_cycles[pivot]);
899 }
900 }
901 // RDL❗✔️:
902 // RDL❗✔️: if (RDL_bitset_empty(compressedCycleWithEqualAdded, empty_cycle, compressedSize)) {
903 if !cycle_with_equal_added.iter().any(|&bit| bit) {
904 // RDL❗✔️: for (k = currentRelevantCyclesSmallerEnd; k < currentRelevantCyclesSize-1; ++k) {
905 for relevant_index in
906 current_relevant_cycles_smaller_end..relevant_cycles.len() - 1
907 {
908 let mut comparison = cycle_with_smaller_added.clone();
909 xor_bool_slice(&mut comparison, &relevant_cycles[relevant_index]);
910 if !comparison.iter().any(|&bit| bit) {
911 let related_index = relevant_cycles_map[relevant_index];
912 self.relations[weight][index_in_weight][related_index] = true;
913 self.relations[weight][related_index][index_in_weight] = true;
914 }
915 }
916 } else {
917 // RDL❗✔️: oldBasisCyclesSize = currentBasisCyclesSize;
918 let old_basis_cycles_size = basis_cycles.len();
919 // RDL❗✔️: compressedBasisCycles[currentBasisCyclesSize] = compressedCycleWithEqualAdded;
920 basis_cycles.push(cycle_with_equal_added);
921 // RDL❗✔️:
922 // RDL❗✔️: if (!RDL_bitset_test(compressedCycleWithEqualAdded, oldBasisCyclesSize)) {
923 if !basis_cycles[old_basis_cycles_size][old_basis_cycles_size] {
924 for column in old_basis_cycles_size + 1..graph.edge_count() {
925 if basis_cycles[old_basis_cycles_size][column] {
926 swap_bool_columns(&mut basis_cycles, old_basis_cycles_size, column);
927 swap_bool_columns(
928 &mut relevant_cycles,
929 old_basis_cycles_size,
930 column,
931 );
932 swap_bool_columns(
933 &mut prototypes_for_gaussian,
934 old_basis_cycles_size,
935 column,
936 );
937 break;
938 }
939 }
940 }
941 }
942 }
943 }
944 // RDL❗✔️: }
945 // END RDL C FUNCTION RDL_checkDependencies
946 }
947
948 fn check_edges(
949 &mut self,
950 cycle_families: &CycleFamilies,
951 graph: &Graph,
952 shortest_paths: &ShortestPathInfo,
953 ) {
954 // BEGIN RDL C FUNCTION RDL_checkEdges
955 // RDL❗✔️: void RDL_checkEdges(RDL_cfURF *RCFs, RDL_graph *graph, RDL_URFinfo *uInfo, RDL_sPathInfo* spi)
956 // RDL❗✔️: {
957 // RDL❗✔️: for(i=0; i<uInfo->nofWeights; ++i) /*go through all matrices*/
958 for weight in 0..self.nof_protos.len() {
959 let mut edge_lists = vec![None; self.nof_protos[weight]];
960 for left in 0..self.nof_protos[weight] {
961 for right in left + 1..self.nof_protos[weight] {
962 // RDL❗✔️: if(uInfo->URFrel[i][j][k] == 1)
963 if !self.relations[weight][left][right] {
964 continue;
965 }
966 if edge_lists[left].is_none() {
967 edge_lists[left] = Some(make_edge_list(
968 &cycle_families.families[self.idx_weight(weight, left)],
969 graph,
970 shortest_paths,
971 ));
972 }
973 if edge_lists[right].is_none() {
974 edge_lists[right] = Some(make_edge_list(
975 &cycle_families.families[self.idx_weight(weight, right)],
976 graph,
977 shortest_paths,
978 ));
979 }
980 let shared = sorted_edge_lists_share_edge(
981 edge_lists[left].as_deref().expect("left edge list exists"),
982 edge_lists[right]
983 .as_deref()
984 .expect("right edge list exists"),
985 );
986 // RDL❗✔️: /* if shared edge, relation is confirmed */
987 self.relations[weight][left][right] = shared;
988 self.relations[weight][right][left] = shared;
989 }
990 }
991 }
992 // RDL❗✔️: }
993 // END RDL C FUNCTION RDL_checkEdges
994 }
995
996 fn find_transitive_closure(&mut self) {
997 // BEGIN RDL C FUNCTION RDL_findTransitiveClosure
998 // RDL✔️✔️: void RDL_findTransitiveClosure(RDL_URFinfo *uInfo)
999 // RDL✔️✔️: {
1000 // RDL✔️✔️: for(i = 0; i < uInfo->nofWeights; ++i) {
1001 for weight in 0..self.nof_protos.len() {
1002 let mut visited = vec![false; self.nof_protos[weight]];
1003 let mut connected_components = Vec::<Vec<usize>>::new();
1004 // RDL✔️✔️: for(j = 0; j < uInfo->nofProtos[i]; ++j) {
1005 for start in 0..self.nof_protos[weight] {
1006 if visited[start] {
1007 continue;
1008 }
1009 let mut component = Vec::new();
1010 let mut stack = vec![start];
1011 visited[start] = true;
1012 while let Some(current) = stack.pop() {
1013 component.push(current);
1014 for (next, &related) in self.relations[weight][current].iter().enumerate() {
1015 if related && !visited[next] {
1016 visited[next] = true;
1017 stack.push(next);
1018 }
1019 }
1020 }
1021 connected_components.push(component);
1022 }
1023 // RDL✔️✔️:
1024 // RDL✔️✔️: /* every pair inside a CC is URF related */
1025 for component in connected_components {
1026 for left in 0..component.len() {
1027 for right in left + 1..component.len() {
1028 let u = component[left];
1029 let v = component[right];
1030 self.relations[weight][u][v] = true;
1031 self.relations[weight][v][u] = true;
1032 }
1033 }
1034 }
1035 }
1036 // RDL✔️✔️: }
1037 // END RDL C FUNCTION RDL_findTransitiveClosure
1038 }
1039
1040 fn fill_urfs(&mut self) {
1041 // BEGIN RDL C FUNCTION RDL_countURFs
1042 // RDL✔️✔️: unsigned RDL_countURFs(RDL_URFinfo *uInfo)
1043 // RDL✔️✔️: {
1044 // RDL✔️✔️: /*count number of 1s indicating URF-relation*/
1045 // RDL✔️✔️: }
1046 // END RDL C FUNCTION RDL_countURFs
1047 //
1048 // BEGIN RDL C FUNCTION RDL_fillURFs
1049 // RDL✔️✔️: void RDL_fillURFs(RDL_URFinfo *uInfo, RDL_cfURF *CFs)
1050 // RDL✔️✔️: {
1051 // RDL✔️✔️: for(i=0; i<uInfo->nofWeights; ++i)
1052 let mut urfs = Vec::new();
1053 for weight in 0..self.nof_protos.len() {
1054 let mut already_in_urf = vec![false; self.nof_protos[weight]];
1055 for start in 0..self.nof_protos[weight] {
1056 // RDL✔️✔️: if((uInfo->URFrel[i][j][j] == 1) && (alreadyInURF[i][j] == 0))
1057 if !self.relations[weight][start][start] || already_in_urf[start] {
1058 continue;
1059 }
1060 let mut urf = Vec::new();
1061 for current in start..self.nof_protos[weight] {
1062 if self.relations[weight][start][current] {
1063 urf.push(self.idx_weight(weight, current));
1064 already_in_urf[current] = true;
1065 }
1066 }
1067 urfs.push(urf);
1068 }
1069 }
1070 // RDL✔️✔️: }
1071 // END RDL C FUNCTION RDL_fillURFs
1072 self.urfs = urfs;
1073 }
1074}
1075
1076#[derive(Debug, Clone, PartialEq)]
1077pub struct RingDecomposition {
1078 graph: Graph,
1079 urfs: Vec<UniqueRingFamily>,
1080 relevant_cycle_count: f64,
1081}
1082
1083impl RingDecomposition {
1084 pub fn calculate(graph: Graph) -> Result<Self, RingDecomposerError> {
1085 // BEGIN RDL C FUNCTION RDL_calculate
1086 // RDL❗✔️: RDL_data *RDL_calculate(RDL_graph *gra)
1087 // RDL❗✔️: {
1088 // RDL❗✔️: RDL_data *data;
1089 // RDL❗✔️: unsigned i, j, k, urf_index, rcf_index;
1090 // RDL❗✔️: unsigned nof_relevant_fams, nof_relevant_fams_sum;
1091 // RDL❗✔️:
1092 // RDL❗✔️: if (!gra) {
1093 // RDL❗✔️: RDL_outputFunc(RDL_ERROR, "The graph is NULL.\n");
1094 // RDL❗✔️: return NULL;
1095 // RDL❗✔️: }
1096 // RDL❗✔️:
1097 // RDL❗✔️: /* we can't calculate anything if there isn't at least one node */
1098 // RDL❗✔️: if(!gra->V) {
1099 // RDL❗✔️: RDL_outputFunc(RDL_ERROR, "The graph has no nodes.\n");
1100 // RDL❗✔️: return NULL;
1101 // RDL❗✔️: }
1102 if graph.node_count == 0 {
1103 return Err(RingDecomposerError::EmptyGraph);
1104 }
1105 let cyclomatic_number =
1106 graph.edge_count() + connected_component_count(&graph) - graph.node_count();
1107 if cyclomatic_number == 0 {
1108 return Ok(Self {
1109 graph,
1110 urfs: Vec::new(),
1111 relevant_cycle_count: 0.0,
1112 });
1113 }
1114 // RDL❗✔️: data = malloc(sizeof(*data));
1115 // RDL❗✔️:
1116 // RDL❗✔️: /* FIRST STEP: TARJAN */
1117 // RDL❗✔️: data->bccGraphs = RDL_tarjanBCC(gra);
1118 let bcc = BiconnectedComponents::calculate(&graph);
1119 if bcc.component_count() == 0 {
1120 return Ok(Self {
1121 graph,
1122 urfs: Vec::new(),
1123 relevant_cycle_count: 0.0,
1124 });
1125 }
1126 // RDL❗✔️: data->nofURFs = 0;
1127 // RDL❗✔️: data->nofRCFs = 0;
1128 // RDL❗✔️:
1129 // RDL❗✔️: /* allocate result structures */
1130 // RDL❗✔️: data->spiPerBCC = malloc(data->bccGraphs->nof_bcc * sizeof(*data->spiPerBCC));
1131 // RDL❗✔️: data->CFsPerBCC = malloc(data->bccGraphs->nof_bcc * sizeof(*data->CFsPerBCC));
1132 // RDL❗✔️: data->urfInfoPerBCC = malloc(data->bccGraphs->nof_bcc * sizeof(*data->urfInfoPerBCC));
1133 // RDL❗✔️: data->nofURFsPerBCC = malloc(data->bccGraphs->nof_bcc * sizeof(*data->nofURFsPerBCC));
1134 // RDL❗✔️: data->nofRCFsPerBCC = malloc(data->bccGraphs->nof_bcc * sizeof(*data->nofRCFsPerBCC));
1135 let mut urfs = Vec::new();
1136 let mut relevant_cycle_count = 0usize;
1137 for component in bcc.components() {
1138 // RDL❗✔️: /* solve APSP problem */
1139 // RDL❗✔️: data->spiPerBCC[i] = RDL_AllPairsShortestPaths(data->bccGraphs->bcc_graphs[i]);
1140 let mut component_graph = component.graph().clone();
1141 let mut shortest_paths = ShortestPathInfo::calculate(&component_graph);
1142 // RDL❗✔️: /* calculate RCFs with Vismara's algortihm */
1143 // RDL❗✔️: data->CFsPerBCC[i] = RDL_findCycleFams(data->bccGraphs->bcc_graphs[i], data->spiPerBCC[i]);
1144 let mut cycle_families =
1145 CycleFamilies::calculate(&mut component_graph, &mut shortest_paths);
1146 if cycle_families.is_empty() {
1147 continue;
1148 }
1149 // RDL❗✔️: data->urfInfoPerBCC[i] = RDL_checkURFRelation(data->CFsPerBCC[i],
1150 // RDL❗✔️: data->bccGraphs->bcc_graphs[i], data->spiPerBCC[i]);
1151 let urf_info =
1152 UrfInfo::check_urf_relation(&mut cycle_families, &component_graph, &shortest_paths);
1153 relevant_cycle_count += cycle_families
1154 .families()
1155 .iter()
1156 .filter(|family| family.is_relevant())
1157 .count();
1158 for urf in &urf_info.urfs {
1159 urfs.push(unique_ring_family_from_component_urf(
1160 urf,
1161 &cycle_families,
1162 component,
1163 ));
1164 }
1165 }
1166 // RDL❗✔️: ...
1167 // RDL❗✔️: return data;
1168 // RDL❗✔️: }
1169 // END RDL C FUNCTION RDL_calculate
1170 Ok(Self {
1171 graph,
1172 urfs,
1173 relevant_cycle_count: relevant_cycle_count as f64,
1174 })
1175 }
1176
1177 #[must_use]
1178 pub const fn graph(&self) -> &Graph {
1179 &self.graph
1180 }
1181
1182 #[must_use]
1183 pub fn urfs(&self) -> &[UniqueRingFamily] {
1184 &self.urfs
1185 }
1186
1187 #[must_use]
1188 pub fn urf_count(&self) -> usize {
1189 self.urfs.len()
1190 }
1191
1192 #[must_use]
1193 pub const fn relevant_cycle_count(&self) -> f64 {
1194 self.relevant_cycle_count
1195 }
1196}
1197
1198#[derive(Debug, Clone, PartialEq, Eq)]
1199pub struct UniqueRingFamily {
1200 nodes: Vec<usize>,
1201 edges: Vec<EdgeId>,
1202}
1203
1204impl UniqueRingFamily {
1205 #[must_use]
1206 pub fn nodes(&self) -> &[usize] {
1207 &self.nodes
1208 }
1209
1210 #[must_use]
1211 pub fn edges(&self) -> &[EdgeId] {
1212 &self.edges
1213 }
1214}
1215
1216fn canonical_edge_key(from: usize, to: usize) -> (usize, usize) {
1217 if from <= to { (from, to) } else { (to, from) }
1218}
1219
1220fn connected_component_count(graph: &Graph) -> usize {
1221 if graph.node_count == 0 {
1222 return 0;
1223 }
1224 let mut seen = vec![false; graph.node_count];
1225 let mut count = 0usize;
1226 for start in 0..graph.node_count {
1227 if seen[start] {
1228 continue;
1229 }
1230 count += 1;
1231 seen[start] = true;
1232 let mut stack = vec![start];
1233 while let Some(node) = stack.pop() {
1234 for &(neighbor, _) in &graph.adjacency[node] {
1235 if !seen[neighbor] {
1236 seen[neighbor] = true;
1237 stack.push(neighbor);
1238 }
1239 }
1240 }
1241 }
1242 count
1243}
1244
1245fn find_cycle_families(graph: &mut Graph, shortest_paths: &mut ShortestPathInfo) -> CycleFamilies {
1246 // BEGIN RDL C FUNCTION RDL_findCycleFams
1247 // RDL✔️✔️: RDL_cfURF *RDL_findCycleFams(RDL_graph *gra, RDL_sPathInfo *spi)
1248 // RDL✔️✔️: {
1249 // RDL✔️✔️: RDL_cfURF *rc = malloc(sizeof(*rc));
1250 // RDL✔️✔️: /*number of CFs is at most 2m^2+vn (Vismara Lemma 3)*/
1251 // RDL✔️✔️:
1252 // RDL✔️✔️: rc->alloced = 64;
1253 // RDL✔️✔️: rc->fams = malloc(rc->alloced * sizeof(*rc->fams));
1254 // RDL✔️✔️: rc->nofFams = 0;
1255 // RDL✔️✔️: RDL_vismara(rc, gra, spi);
1256 let mut families = Vec::new();
1257 vismara_cycle_families(&mut families, graph, shortest_paths);
1258 // RDL✔️✔️:
1259 // RDL✔️✔️: if (!rc->fams) {
1260 // RDL✔️✔️: RDL_outputFunc(RDL_ERROR, "Graph is too large, can't allocate memory!\n");
1261 // RDL✔️✔️: free(rc);
1262 // RDL✔️✔️: return NULL;
1263 // RDL✔️✔️: }
1264 // RDL✔️✔️:
1265 // RDL✔️✔️: rc->alloced = rc->nofFams;
1266 // RDL✔️✔️: rc->fams = realloc(rc->fams, rc->alloced * sizeof(*rc->fams));
1267 // RDL✔️✔️:
1268 // RDL✔️✔️: /* sort by size */
1269 // RDL✔️✔️: qsort(rc->fams, rc->nofFams, sizeof(*rc->fams), RDL_cycleFamsComp);
1270 families.sort_by_key(CycleFamily::weight);
1271 // RDL✔️✔️: return rc;
1272 // RDL✔️✔️: }
1273 // END RDL C FUNCTION RDL_findCycleFams
1274 CycleFamilies { families }
1275}
1276
1277fn vismara_cycle_families(
1278 families: &mut Vec<CycleFamily>,
1279 graph: &mut Graph,
1280 shortest_paths: &mut ShortestPathInfo,
1281) {
1282 // BEGIN RDL C FUNCTION RDL_vismara
1283 // RDL✔️✔️: static void RDL_vismara(RDL_cfURF *rc, RDL_graph *gra, RDL_sPathInfo *spi)
1284 // RDL✔️✔️: {
1285 // RDL✔️✔️: unsigned i,j;
1286 // RDL✔️✔️: unsigned rv,yv,zv,pv,qv; /*variables as in Vismara's algorithm, extended by a 'v'*/
1287 // RDL✔️✔️: unsigned *evenCand; /*'S' in Vismara's algorithm*/
1288 // RDL✔️✔️: unsigned nofCandidates = 0;
1289 // RDL✔️✔️: evenCand = malloc(gra->V * sizeof(*evenCand));
1290 // RDL✔️✔️:
1291 // RDL✔️✔️: for(rv = 0; rv < gra->V; ++rv) {
1292 for root in 0..graph.node_count() {
1293 // RDL✔️✔️: for(yv = 0; yv < gra->V; ++yv) {
1294 for y in 0..graph.node_count() {
1295 // RDL✔️✔️: /*all yv reachable from rv respecting the ordering*/
1296 // RDL✔️✔️: if(spi->reachable[rv][yv] == 1) {
1297 if !shortest_paths.reachable_preceding(root, y) {
1298 continue;
1299 }
1300 // RDL✔️✔️: nofCandidates = 0;
1301 let mut even_candidates = Vec::new();
1302 // RDL✔️✔️: for(i = 0; i < gra->degree[yv]; ++i) {
1303 for &(z, _) in &graph.adjacency[y] {
1304 // RDL✔️✔️: zv = gra->adjList[yv][i][0];
1305 // RDL✔️✔️: /*all zv reachable from rv respecting the ordering and adjacent to yv*/
1306 // RDL✔️✔️: if(spi->reachable[rv][zv] == 1) {
1307 if !shortest_paths.reachable_preceding(root, z) {
1308 continue;
1309 }
1310 let root_to_z = shortest_paths
1311 .distance(root, z)
1312 .expect("reachable vertex has distance");
1313 let root_to_y = shortest_paths
1314 .distance(root, y)
1315 .expect("reachable vertex has distance");
1316 // RDL✔️✔️: if(spi->dist[rv][zv] + 1 == spi->dist[rv][yv]) {
1317 // RDL✔️✔️: evenCand[nofCandidates] = zv;
1318 // RDL✔️✔️: ++nofCandidates;
1319 // RDL✔️✔️: }
1320 if root_to_z + 1 == root_to_y {
1321 even_candidates.push(z);
1322 }
1323 // RDL✔️✔️: else if(spi->dist[rv][zv] != spi->dist[rv][yv] + 1
1324 // RDL✔️✔️: && (gra->degree[zv] < gra->degree[yv] ||
1325 // RDL✔️✔️: (gra->degree[zv] == gra->degree[yv] && zv<yv))
1326 // RDL✔️✔️: && RDL_pathsShareOnlyStart(rv, yv, zv, gra, spi) == 1) {
1327 else if root_to_z != root_to_y + 1
1328 && (graph.adjacency[z].len() < graph.adjacency[y].len()
1329 || (graph.adjacency[z].len() == graph.adjacency[y].len() && z < y))
1330 && paths_share_only_start(root, y, z, graph, shortest_paths)
1331 {
1332 // RDL✔️✔️: /*add odd cycle rv-yv rv-zv zv-yv*/
1333 // RDL✔️✔️: RDL_addOdd(rv, yv, zv, gra, spi, rc);
1334 add_odd_cycle_family(families, root, y, z, graph, shortest_paths);
1335 }
1336 // RDL✔️✔️: /*to fill dPaths in sPathInfo with the edges to r*/
1337 // RDL✔️✔️: if(spi->dist[rv][zv] == 1) {
1338 // RDL✔️✔️: RDL_addEdge(spi->dPaths[rv], zv, rv);
1339 // RDL✔️✔️: }
1340 if root_to_z == 1 {
1341 shortest_paths.directed_paths[root].add_directed_edge(z, root);
1342 }
1343 // RDL✔️✔️: }
1344 }
1345 // RDL✔️✔️: }
1346 // RDL✔️✔️: /*any pair in evenCand*/
1347 // RDL✔️✔️: for(i = 0; i < nofCandidates; ++i) {
1348 for i in 0..even_candidates.len() {
1349 // RDL✔️✔️: pv = evenCand[i];
1350 let p = even_candidates[i];
1351 // RDL✔️✔️: for(j = i+1; j < nofCandidates; ++j) {
1352 for &q in &even_candidates[i + 1..] {
1353 // RDL✔️✔️: qv = evenCand[j];
1354 // RDL✔️✔️: if((RDL_pathsShareOnlyStart(rv, pv, qv, gra, spi) == 1)) {
1355 if paths_share_only_start(root, p, q, graph, shortest_paths) {
1356 // RDL✔️✔️: /*add even cycle rv-pv rv-qv pv-yv-qv*/
1357 // RDL✔️✔️: RDL_addEven(rv, pv, yv, qv, gra, spi, rc);
1358 add_even_cycle_family(families, root, p, y, q, graph, shortest_paths);
1359 }
1360 // RDL✔️✔️: }
1361 }
1362 // RDL✔️✔️: }
1363 }
1364 // RDL✔️✔️: }
1365 // RDL✔️✔️: /*to fill dPaths in sPathInfo/fill U_r (see Vismara)*/
1366 // RDL✔️✔️: for(i = 0; i < nofCandidates; ++i) {
1367 for p in even_candidates {
1368 // RDL✔️✔️: pv = evenCand[i];
1369 // RDL✔️✔️: RDL_addEdge(spi->dPaths[rv], yv, pv);
1370 shortest_paths.directed_paths[root].add_directed_edge(y, p);
1371 // RDL✔️✔️: }
1372 }
1373 // RDL✔️✔️: }
1374 }
1375 // RDL✔️✔️: }
1376 }
1377 // RDL✔️✔️: }
1378 // RDL✔️✔️:
1379 // RDL✔️✔️: free(evenCand);
1380 // RDL✔️✔️: }
1381 // END RDL C FUNCTION RDL_vismara
1382}
1383
1384fn paths_share_only_start(
1385 root: usize,
1386 y: usize,
1387 z: usize,
1388 graph: &Graph,
1389 shortest_paths: &ShortestPathInfo,
1390) -> bool {
1391 // BEGIN RDL C FUNCTION RDL_pathsShareOnlyStart
1392 // RDL✔️✔️: static int RDL_pathsShareOnlyStart(unsigned r, unsigned y, unsigned z, RDL_graph *gra, RDL_sPathInfo *spi)
1393 // RDL✔️✔️: {
1394 // RDL✔️✔️: unsigned result = 0, i, pnt, count=0;
1395 // RDL✔️✔️: unsigned *vertInRY, *vertInRZ; /*edges in P(r,y) and P(r,z)*/
1396 let mut vertices_in_root_y = vec![false; graph.node_count()];
1397 let mut vertices_in_root_z = vec![false; graph.node_count()];
1398 // RDL✔️✔️: vertInRY[y] = 1;
1399 // RDL✔️✔️: vertInRZ[z] = 1;
1400 vertices_in_root_y[y] = true;
1401 vertices_in_root_z[z] = true;
1402 // RDL✔️✔️: pnt = y;
1403 // RDL✔️✔️: do
1404 // RDL✔️✔️: {
1405 let mut point = y;
1406 loop {
1407 // RDL✔️✔️: pnt = spi->pred[r][pnt];
1408 point = shortest_paths
1409 .predecessor(root, point)
1410 .expect("reachable path has predecessor");
1411 // RDL✔️✔️: vertInRY[pnt] = 1;
1412 vertices_in_root_y[point] = true;
1413 // RDL✔️✔️: }while(pnt != r);
1414 if point == root {
1415 break;
1416 }
1417 }
1418 // RDL✔️✔️: pnt = z;
1419 // RDL✔️✔️: do
1420 // RDL✔️✔️: {
1421 point = z;
1422 loop {
1423 // RDL✔️✔️: pnt = spi->pred[r][pnt];
1424 point = shortest_paths
1425 .predecessor(root, point)
1426 .expect("reachable path has predecessor");
1427 // RDL✔️✔️: vertInRZ[pnt] = 1;
1428 vertices_in_root_z[point] = true;
1429 // RDL✔️✔️: }while(pnt != r);
1430 if point == root {
1431 break;
1432 }
1433 }
1434 // RDL✔️✔️: for(i=0; i<gra->V && count<2; ++i)
1435 let mut count = 0usize;
1436 for index in 0..graph.node_count() {
1437 // RDL✔️✔️: if(vertInRY[i] == 1 && vertInRZ[i] == 1)
1438 // RDL✔️✔️: {
1439 // RDL✔️✔️: ++count;
1440 // RDL✔️✔️: }
1441 if vertices_in_root_y[index] && vertices_in_root_z[index] {
1442 count += 1;
1443 if count >= 2 {
1444 break;
1445 }
1446 }
1447 }
1448 // RDL✔️✔️: if(count == 1 && (vertInRY[r] == 1) && (vertInRZ[r] == 1))
1449 // RDL✔️✔️: {
1450 // RDL✔️✔️: result = 1;
1451 // RDL✔️✔️: }
1452 // RDL✔️✔️:
1453 // RDL✔️✔️: return result;
1454 // RDL✔️✔️: }
1455 // END RDL C FUNCTION RDL_pathsShareOnlyStart
1456 count == 1 && vertices_in_root_y[root] && vertices_in_root_z[root]
1457}
1458
1459fn find_prototype(
1460 root: usize,
1461 y: usize,
1462 z: usize,
1463 x: Option<usize>,
1464 graph: &Graph,
1465 shortest_paths: &ShortestPathInfo,
1466) -> Vec<bool> {
1467 // BEGIN RDL C FUNCTION RDL_findPrototype
1468 // RDL✔️✔️: static char *RDL_findPrototype(unsigned r, unsigned y, unsigned z, unsigned x,
1469 // RDL✔️✔️: RDL_graph *gra, RDL_sPathInfo *spi)
1470 // RDL✔️✔️: {
1471 // RDL✔️✔️: unsigned i, vert1, vert2;
1472 // RDL✔️✔️: char *proto;
1473 // RDL✔️✔️:
1474 // RDL✔️✔️: proto = malloc(gra->E * sizeof(*proto));
1475 // RDL✔️✔️: for(i=0; i<gra->E; ++i)
1476 // RDL✔️✔️: {
1477 // RDL✔️✔️: proto[i] = 0;
1478 // RDL✔️✔️: }
1479 let mut prototype = vec![false; graph.edge_count()];
1480 // RDL✔️✔️: /*path from r to y*/
1481 mark_path_edges(&mut prototype, root, y, graph, shortest_paths);
1482 // RDL✔️✔️: /*path from r to z*/
1483 mark_path_edges(&mut prototype, root, z, graph, shortest_paths);
1484 // RDL✔️✔️: if(x == UINT_MAX)/*odd cycle*/
1485 if let Some(x) = x {
1486 // RDL✔️✔️: else /*even cycle*/
1487 // RDL✔️✔️: {
1488 // RDL✔️✔️: proto[RDL_edgeId(gra,y,x)] = 1;
1489 // RDL✔️✔️: proto[RDL_edgeId(gra,z,x)] = 1;
1490 // RDL✔️✔️: }
1491 prototype[graph.edge_id(y, x).expect("cycle edge exists").index()] = true;
1492 prototype[graph.edge_id(z, x).expect("cycle edge exists").index()] = true;
1493 } else {
1494 // RDL✔️✔️: {
1495 // RDL✔️✔️: proto[RDL_edgeId(gra,y,z)] = 1;
1496 // RDL✔️✔️: }
1497 prototype[graph.edge_id(y, z).expect("cycle edge exists").index()] = true;
1498 }
1499 // RDL✔️✔️: return proto;
1500 // RDL✔️✔️: }
1501 // END RDL C FUNCTION RDL_findPrototype
1502 prototype
1503}
1504
1505fn mark_path_edges(
1506 prototype: &mut [bool],
1507 root: usize,
1508 target: usize,
1509 graph: &Graph,
1510 shortest_paths: &ShortestPathInfo,
1511) {
1512 let mut vertex_1 = target;
1513 loop {
1514 let vertex_2 = vertex_1;
1515 vertex_1 = shortest_paths
1516 .predecessor(root, vertex_1)
1517 .expect("reachable path has predecessor");
1518 prototype[graph
1519 .edge_id(vertex_1, vertex_2)
1520 .expect("path edge exists")
1521 .index()] = true;
1522 if vertex_1 == root {
1523 break;
1524 }
1525 }
1526}
1527
1528fn add_odd_cycle_family(
1529 families: &mut Vec<CycleFamily>,
1530 root: usize,
1531 y: usize,
1532 z: usize,
1533 graph: &Graph,
1534 shortest_paths: &ShortestPathInfo,
1535) {
1536 // BEGIN RDL C FUNCTION RDL_addOdd
1537 // RDL✔️✔️: static void RDL_addOdd(unsigned r, unsigned y, unsigned z,
1538 // RDL✔️✔️: RDL_graph *gra, RDL_sPathInfo *spi, RDL_cfURF *rc)
1539 // RDL✔️✔️: {
1540 // RDL✔️✔️: RDL_cfam *new;
1541 // RDL✔️✔️: ...
1542 // RDL✔️✔️: new->r = r;
1543 // RDL✔️✔️: new->p = y;
1544 // RDL✔️✔️: new->q = z;
1545 // RDL✔️✔️: new->x = UINT_MAX; /*odd cycle*/
1546 // RDL✔️✔️: new->mark = 0;
1547 // RDL✔️✔️: new->prototype = RDL_findPrototype(r, y, z, UINT_MAX, gra, spi);
1548 // RDL✔️✔️: new->weight = spi->dist[r][y] + spi->dist[r][z] + 1;
1549 // RDL✔️✔️: rc->fams[rc->nofFams++] = new;
1550 // RDL✔️✔️: }
1551 // END RDL C FUNCTION RDL_addOdd
1552 families.push(CycleFamily {
1553 weight: shortest_paths.distance(root, y).expect("reachable y")
1554 + shortest_paths.distance(root, z).expect("reachable z")
1555 + 1,
1556 r: root,
1557 p: y,
1558 q: z,
1559 x: None,
1560 prototype: find_prototype(root, y, z, None, graph, shortest_paths),
1561 relevant: false,
1562 });
1563}
1564
1565fn add_even_cycle_family(
1566 families: &mut Vec<CycleFamily>,
1567 root: usize,
1568 y: usize,
1569 x: usize,
1570 z: usize,
1571 graph: &Graph,
1572 shortest_paths: &ShortestPathInfo,
1573) {
1574 // BEGIN RDL C FUNCTION RDL_addEven
1575 // RDL✔️✔️: static void RDL_addEven(unsigned r, unsigned y, unsigned x,
1576 // RDL✔️✔️: unsigned z, RDL_graph *gra, RDL_sPathInfo *spi, RDL_cfURF *rc)
1577 // RDL✔️✔️: {
1578 // RDL✔️✔️: RDL_cfam *new;
1579 // RDL✔️✔️: ...
1580 // RDL✔️✔️: new->r = r;
1581 // RDL✔️✔️: new->p = y;
1582 // RDL✔️✔️: new->q = z;
1583 // RDL✔️✔️: new->x = x; /*even cycle*/
1584 // RDL✔️✔️: new->mark = 0;
1585 // RDL✔️✔️: new->prototype = RDL_findPrototype(r, y, z, x, gra, spi);
1586 // RDL✔️✔️: new->weight = spi->dist[r][y] + spi->dist[r][z] + 2;
1587 // RDL✔️✔️: rc->fams[rc->nofFams++] = new;
1588 // RDL✔️✔️: }
1589 // END RDL C FUNCTION RDL_addEven
1590 families.push(CycleFamily {
1591 weight: shortest_paths.distance(root, y).expect("reachable y")
1592 + shortest_paths.distance(root, z).expect("reachable z")
1593 + 2,
1594 r: root,
1595 p: y,
1596 q: z,
1597 x: Some(x),
1598 prototype: find_prototype(root, y, z, Some(x), graph, shortest_paths),
1599 relevant: false,
1600 });
1601}
1602
1603fn xor_bool_slice(dst: &mut [bool], src: &[bool]) {
1604 // BEGIN RDL C FUNCTION RDL_bitset_xor_inplace
1605 // RDL✔️✔️: void RDL_bitset_xor_inplace(unsigned char* dst, unsigned const char* src, unsigned size)
1606 // RDL✔️✔️: {
1607 // RDL✔️✔️: unsigned i;
1608 // RDL✔️✔️:
1609 // RDL✔️✔️: for (i = 0; i < size; ++i) {
1610 // RDL✔️✔️: dst[i] ^= src[i];
1611 // RDL✔️✔️: }
1612 // RDL✔️✔️: }
1613 // END RDL C FUNCTION RDL_bitset_xor_inplace
1614 for (dst_bit, src_bit) in dst.iter_mut().zip(src) {
1615 *dst_bit ^= *src_bit;
1616 }
1617}
1618
1619fn swap_bool_columns(rows: &mut [Vec<bool>], left: usize, right: usize) {
1620 // BEGIN RDL C FUNCTION RDL_swap_columns
1621 // RDL✔️✔️: void RDL_swap_columns(unsigned char** rows, unsigned nof_rows, unsigned col1, unsigned col2)
1622 // RDL✔️✔️: {
1623 // RDL✔️✔️: unsigned char val1, val2;
1624 // RDL✔️✔️: unsigned i;
1625 // RDL✔️✔️:
1626 // RDL✔️✔️: for (i = 0; i < nof_rows; ++i) {
1627 // RDL✔️✔️: val1 = RDL_bitset_test(rows[i], col1);
1628 // RDL✔️✔️: val2 = RDL_bitset_test(rows[i], col2);
1629 // RDL✔️✔️:
1630 // RDL✔️✔️: if (!val1 != !val2) {
1631 // RDL✔️✔️: RDL_bitset_flip(rows[i], col1);
1632 // RDL✔️✔️: RDL_bitset_flip(rows[i], col2);
1633 // RDL✔️✔️: }
1634 // RDL✔️✔️: }
1635 // RDL✔️✔️: }
1636 // END RDL C FUNCTION RDL_swap_columns
1637 for row in rows {
1638 if row[left] != row[right] {
1639 row.swap(left, right);
1640 }
1641 }
1642}
1643
1644fn find_edges(
1645 edges: &mut [bool],
1646 root: usize,
1647 target: usize,
1648 graph: &Graph,
1649 shortest_paths: &ShortestPathInfo,
1650 visited: &mut [bool],
1651) {
1652 // BEGIN RDL C FUNCTION RDL_giveEdges
1653 // RDL✔️✔️: void RDL_giveEdges(unsigned a, unsigned b, char *array,
1654 // RDL✔️✔️: const RDL_graph *gra, const RDL_sPathInfo *spi, char* visited)
1655 // RDL✔️✔️: {
1656 // RDL✔️✔️: unsigned i, vertex, edge;
1657 // RDL✔️✔️:
1658 // RDL✔️✔️: if(a==b) {
1659 // RDL✔️✔️: return;
1660 // RDL✔️✔️: }
1661 if root == target {
1662 return;
1663 }
1664 // RDL✔️✔️:
1665 // RDL✔️✔️: visited[b] = 1;
1666 visited[target] = true;
1667 // RDL✔️✔️:
1668 // RDL✔️✔️: /*for each vertex adjacent to b in U_a*/
1669 if let Some(neighbors) = shortest_paths.directed_paths[root].neighbors(target) {
1670 for &vertex in neighbors {
1671 // RDL✔️✔️: edge = RDL_edgeId(gra,b,vertex);
1672 // RDL✔️✔️: array[edge] = 1;
1673 edges[graph
1674 .edge_id(target, vertex)
1675 .expect("directed path edge exists in graph")
1676 .index()] = true;
1677 // RDL✔️✔️: if (!visited[vertex]) {
1678 if !visited[vertex] {
1679 // RDL✔️✔️: RDL_giveEdges(a, vertex, array, gra, spi, visited);
1680 find_edges(edges, root, vertex, graph, shortest_paths, visited);
1681 }
1682 // RDL✔️✔️: }
1683 }
1684 }
1685 // RDL✔️✔️: }
1686 // END RDL C FUNCTION RDL_giveEdges
1687}
1688
1689fn make_edge_list(
1690 family: &CycleFamily,
1691 graph: &Graph,
1692 shortest_paths: &ShortestPathInfo,
1693) -> Vec<usize> {
1694 // BEGIN RDL C FUNCTION make_edge_list
1695 // RDL❗✔️: static void make_edge_list(
1696 // RDL❗✔️: unsigned** edge_list,
1697 // RDL❗✔️: unsigned* edge_list_size,
1698 // RDL❗✔️: char* edges, RDL_graph *graph,
1699 // RDL❗✔️: RDL_cfam* rcf, RDL_sPathInfo* spi)
1700 // RDL❗✔️: {
1701 // RDL❗✔️: memset(edges, 0, graph->E * sizeof(*edges));
1702 let mut edge_membership = vec![false; graph.edge_count()];
1703 // RDL❗✔️: RDL_findEdges(edges, rcf, graph, spi);
1704 find_family_edges(&mut edge_membership, family, graph, shortest_paths);
1705 // RDL❗✔️:
1706 // RDL❗✔️: for (i = 0; i < graph->E; ++i) {
1707 // RDL❗✔️: if (edges[i]) {
1708 // RDL❗✔️: (*edge_list)[*edge_list_size] = i;
1709 // RDL❗✔️: ++(*edge_list_size);
1710 // RDL❗✔️: }
1711 // RDL❗✔️: }
1712 // RDL❗✔️: }
1713 // END RDL C FUNCTION make_edge_list
1714 edge_membership
1715 .iter()
1716 .enumerate()
1717 .filter_map(|(edge, &contains)| contains.then_some(edge))
1718 .collect()
1719}
1720
1721fn find_family_edges(
1722 edges: &mut [bool],
1723 family: &CycleFamily,
1724 graph: &Graph,
1725 shortest_paths: &ShortestPathInfo,
1726) {
1727 // BEGIN RDL C FUNCTION RDL_findEdges
1728 // RDL❗✔️: void RDL_findEdges(char *edges, RDL_cfam *RCF, RDL_graph *gra, RDL_sPathInfo *spi)
1729 // RDL❗✔️: {
1730 // RDL❗✔️: char* visited;
1731 let mut visited = vec![false; graph.node_count()];
1732 // RDL❗✔️: RDL_giveEdges(RCF->r, RCF->p, edges, gra, spi, visited);
1733 find_edges(
1734 edges,
1735 family.root(),
1736 family.p(),
1737 graph,
1738 shortest_paths,
1739 &mut visited,
1740 );
1741 // RDL❗✔️: memset(visited, 0, gra->E * sizeof(*visited));
1742 visited.fill(false);
1743 // RDL❗✔️: RDL_giveEdges(RCF->r, RCF->q, edges, gra, spi, visited);
1744 find_edges(
1745 edges,
1746 family.root(),
1747 family.q(),
1748 graph,
1749 shortest_paths,
1750 &mut visited,
1751 );
1752 // RDL❗✔️: if(RCF->x < UINT_MAX) /*even family*/
1753 if let Some(x) = family.x() {
1754 // RDL❗✔️: edges[RDL_edgeId(gra, RCF->x, RCF->p)] = 1;
1755 // RDL❗✔️: edges[RDL_edgeId(gra, RCF->x, RCF->q)] = 1;
1756 edges[graph
1757 .edge_id(x, family.p())
1758 .expect("cycle edge exists")
1759 .index()] = true;
1760 edges[graph
1761 .edge_id(x, family.q())
1762 .expect("cycle edge exists")
1763 .index()] = true;
1764 } else {
1765 // RDL❗✔️: edges[RDL_edgeId(gra, RCF->p, RCF->q)] = 1;
1766 edges[graph
1767 .edge_id(family.p(), family.q())
1768 .expect("cycle edge exists")
1769 .index()] = true;
1770 }
1771 // RDL❗✔️: }
1772 // END RDL C FUNCTION RDL_findEdges
1773}
1774
1775fn sorted_edge_lists_share_edge(left: &[usize], right: &[usize]) -> bool {
1776 // BEGIN RDL C FUNCTION RDL_shareEdges
1777 // RDL❗✔️: char RDL_shareEdges(RDL_cfURF *RCFs, unsigned idx1, unsigned idx2, RDL_graph *graph,
1778 // RDL❗✔️: RDL_sPathInfo *spi)
1779 // RDL❗✔️: {
1780 // RDL❗✔️: for(i=0; i<graph->E; ++i)
1781 // RDL❗✔️: {
1782 // RDL❗✔️: if(edges1[i] == 1 && edges2[i] == 1)
1783 // RDL❗✔️: {
1784 // RDL❗✔️: result = 1;
1785 // RDL❗✔️: break;
1786 // RDL❗✔️: }
1787 // RDL❗✔️: }
1788 // RDL❗✔️: return result;
1789 // RDL❗✔️: }
1790 // END RDL C FUNCTION RDL_shareEdges
1791 let mut left_index = 0usize;
1792 let mut right_index = 0usize;
1793 while left_index < left.len() && right_index < right.len() {
1794 match left[left_index].cmp(&right[right_index]) {
1795 std::cmp::Ordering::Less => left_index += 1,
1796 std::cmp::Ordering::Greater => right_index += 1,
1797 std::cmp::Ordering::Equal => return true,
1798 }
1799 }
1800 false
1801}
1802
1803fn unique_ring_family_from_component_urf(
1804 urf: &[usize],
1805 cycle_families: &CycleFamilies,
1806 component: &BiconnectedComponent,
1807) -> UniqueRingFamily {
1808 let mut edge_membership = vec![false; component.graph().edge_count()];
1809 for &family_index in urf {
1810 for (edge_index, &contains) in cycle_families.families()[family_index]
1811 .prototype()
1812 .iter()
1813 .enumerate()
1814 {
1815 edge_membership[edge_index] |= contains;
1816 }
1817 }
1818 let mut edges = Vec::new();
1819 let mut node_membership = vec![false; component.original_nodes().len()];
1820 for (local_edge, &contains) in edge_membership.iter().enumerate() {
1821 if !contains {
1822 continue;
1823 }
1824 edges.push(component.original_edges()[local_edge]);
1825 let edge = component.graph().edges()[local_edge];
1826 node_membership[edge.from()] = true;
1827 node_membership[edge.to()] = true;
1828 }
1829 let nodes = node_membership
1830 .iter()
1831 .enumerate()
1832 .filter_map(|(local_node, &contains)| {
1833 contains.then_some(component.original_nodes()[local_node])
1834 })
1835 .collect();
1836 UniqueRingFamily { nodes, edges }
1837}
1838
1839fn tarjan_biconnected_components(graph: &Graph) -> BiconnectedComponents {
1840 // BEGIN RDL C FUNCTION RDL_tarjanBCC
1841 // RDL❗✔️: RDL_BCCGraph* RDL_tarjanBCC(const RDL_graph* graph)
1842 // RDL❗✔️: {
1843 // RDL❗✔️: unsigned *bcc, u, node, i, j, k, ii[2],
1844 // RDL❗✔️: nof_bcc, *bcc_size, nof_nontrivial_bcc=0, curr_bcc, found;
1845 // RDL❗✔️: unsigned *non_trivial_mapping;
1846 // RDL❗✔️: RDL_BCCGraph* result;
1847 // RDL❗✔️:
1848 // RDL❗✔️: nof_bcc = RDL_tarjan(graph, &bcc);
1849 let edge_components = tarjan_edge_components(graph);
1850 // RDL❗✔️:
1851 // RDL❗✔️: result = malloc(sizeof(*result));
1852 // RDL❗✔️: bcc_size = malloc(nof_bcc * sizeof(*bcc_size));
1853 let max_component = edge_components.iter().copied().max().unwrap_or(0);
1854 let mut component_sizes = vec![0usize; max_component + 1];
1855 // RDL❗✔️: non_trivial_mapping = malloc(nof_bcc * sizeof(*non_trivial_mapping));
1856 // RDL❗✔️:
1857 // RDL❗✔️: for (i = 0; i < nof_bcc; ++i) {
1858 // RDL❗✔️: bcc_size[i] = 0;
1859 // RDL❗✔️: }
1860 // RDL❗✔️:
1861 // RDL❗✔️: for (i = 0; i < graph->E; ++i) {
1862 for &component in &edge_components {
1863 if component != 0 {
1864 // RDL❗✔️: curr_bcc = bcc[i];
1865 // RDL❗✔️: ++bcc_size[curr_bcc - 1];
1866 component_sizes[component] += 1;
1867 }
1868 }
1869 // RDL❗✔️: }
1870 // RDL❗✔️:
1871 // RDL❗✔️: for (i = 0; i < nof_bcc; ++i) {
1872 // RDL❗✔️: if(bcc_size[i] > 1) {
1873 // RDL❗✔️: non_trivial_mapping[i] = nof_nontrivial_bcc;
1874 // RDL❗✔️: ++nof_nontrivial_bcc;
1875 // RDL❗✔️: }
1876 // RDL❗✔️: else {
1877 // RDL❗✔️: non_trivial_mapping[i] = RDL_NO_RINGSYSTEM;
1878 // RDL❗✔️: }
1879 // RDL❗✔️: }
1880 let mut non_trivial_mapping = vec![None; max_component + 1];
1881 let mut next_component = 0usize;
1882 for component in 1..=max_component {
1883 if component_sizes[component] > 1 {
1884 non_trivial_mapping[component] = Some(next_component);
1885 next_component += 1;
1886 }
1887 }
1888
1889 let mut original_edges_by_component = vec![Vec::new(); next_component];
1890 let mut original_nodes_by_component = vec![Vec::<usize>::new(); next_component];
1891 let mut local_node_by_original_and_component = BTreeMap::<(usize, usize), usize>::new();
1892 let mut edge_to_component = vec![None; graph.edge_count()];
1893 let mut node_to_components = vec![Vec::<(usize, usize)>::new(); graph.node_count()];
1894
1895 // RDL❗✔️: for (i = 0; i < graph->E; ++i) {
1896 for (edge_index, &raw_component) in edge_components.iter().enumerate() {
1897 // RDL❗✔️: curr_bcc = bcc[i];
1898 let Some(component_index) = non_trivial_mapping.get(raw_component).copied().flatten()
1899 else {
1900 // RDL❗✔️: /* skip trivial BCCs */
1901 // RDL❗✔️: if (bcc_size[curr_bcc-1] <= 1) {
1902 // RDL❗✔️: continue;
1903 // RDL❗✔️: }
1904 continue;
1905 };
1906 // RDL❗✔️:
1907 // RDL❗✔️: /* -1 because of 1-based BCCs */
1908 // RDL❗✔️: u = non_trivial_mapping[curr_bcc - 1];
1909 let local_edge_index = original_edges_by_component[component_index].len();
1910 edge_to_component[edge_index] = Some((component_index, local_edge_index));
1911 original_edges_by_component[component_index].push(EdgeId::new(edge_index));
1912 let edge = graph.edges[edge_index];
1913 // RDL❗✔️:
1914 // RDL❗✔️: for (k = 0; k <= 1; ++k) {
1915 for node in [edge.from(), edge.to()] {
1916 // RDL❗✔️: node = graph->edges[i][k];
1917 // RDL❗✔️:
1918 // RDL❗✔️: found = 0;
1919 if local_node_by_original_and_component.contains_key(&(component_index, node)) {
1920 continue;
1921 }
1922 // RDL❗✔️: if (!found) {
1923 // RDL❗✔️: ++result->nof_bcc_per_node[node];
1924 let local_node_index = original_nodes_by_component[component_index].len();
1925 local_node_by_original_and_component.insert((component_index, node), local_node_index);
1926 node_to_components[node].push((component_index, local_node_index));
1927 // RDL❗✔️: result->node_from_bcc_mapping[u][result->nof_nodes_per_bcc[u]-1] = node;
1928 original_nodes_by_component[component_index].push(node);
1929 }
1930 // RDL❗✔️: }
1931 }
1932 // RDL❗✔️: }
1933 // RDL❗✔️:
1934 // RDL❗✔️: for (u = 0; u < nof_nontrivial_bcc; ++u) {
1935 // RDL❗✔️: result->bcc_graphs[u] = RDL_initNewGraph(result->nof_nodes_per_bcc[u]);
1936 // RDL❗✔️: }
1937 let mut component_graphs = original_nodes_by_component
1938 .iter()
1939 .map(|nodes| Graph::new(nodes.len()))
1940 .collect::<Vec<_>>();
1941 // RDL❗✔️:
1942 // RDL❗✔️: for (i = 0; i < graph->E; ++i) {
1943 for (edge_index, component_mapping) in edge_to_component.iter().copied().enumerate() {
1944 // RDL❗✔️: u = result->edge_to_bcc_mapping[i][0];
1945 let Some((component_index, _)) = component_mapping else {
1946 // RDL❗✔️:
1947 // RDL❗✔️: /* skip if there is no BCC */
1948 // RDL❗✔️: if (u == RDL_NO_RINGSYSTEM) {
1949 // RDL❗✔️: continue;
1950 // RDL❗✔️: }
1951 continue;
1952 };
1953 let edge = graph.edges[edge_index];
1954 let left = local_node_by_original_and_component[&(component_index, edge.from())];
1955 let right = local_node_by_original_and_component[&(component_index, edge.to())];
1956 // RDL❗✔️: RDL_addUEdge(result->bcc_graphs[u], ii[0], ii[1]);
1957 let _ = component_graphs[component_index].add_undirected_edge(left, right);
1958 }
1959 // RDL❗✔️:
1960 // RDL❗✔️: return result;
1961 // RDL❗✔️: }
1962 // END RDL C FUNCTION RDL_tarjanBCC
1963
1964 let components = component_graphs
1965 .into_iter()
1966 .enumerate()
1967 .map(|(index, graph)| BiconnectedComponent {
1968 graph,
1969 original_nodes: std::mem::take(&mut original_nodes_by_component[index]),
1970 original_edges: std::mem::take(&mut original_edges_by_component[index]),
1971 })
1972 .collect();
1973
1974 BiconnectedComponents {
1975 components,
1976 edge_to_component,
1977 node_to_components,
1978 }
1979}
1980
1981fn tarjan_edge_components(graph: &Graph) -> Vec<usize> {
1982 // BEGIN RDL C FUNCTION RDL_tarjan
1983 // RDL❗✔️: static unsigned RDL_tarjan(const RDL_graph* graph,
1984 // RDL❗✔️: unsigned** bcc)
1985 // RDL❗✔️: {
1986 // RDL❗✔️: unsigned *d, *low, u, time, curr_bcc, i;
1987 // RDL❗✔️: RDL_stack* edge_stack;
1988 // RDL❗✔️:
1989 // RDL❗✔️: d = malloc(graph->V * sizeof(*d));
1990 // RDL❗✔️: low = malloc(graph->V * sizeof(*low));
1991 // RDL❗✔️: *bcc = malloc(graph->E * sizeof(**bcc));
1992 let mut discovery = vec![0usize; graph.node_count()];
1993 let mut low = vec![0usize; graph.node_count()];
1994 let mut edge_components = vec![0usize; graph.edge_count()];
1995 let mut edge_stack = Vec::<EdgeId>::new();
1996 let mut time = 0usize;
1997 let mut current_component = 0usize;
1998 // RDL❗✔️: for (i = 0; i < graph->E; ++i) {
1999 // RDL❗✔️: (*bcc)[i] = 0;
2000 // RDL❗✔️: }
2001 // RDL❗✔️:
2002 // RDL❗✔️: edge_stack = RDL_stack_new();
2003 // RDL❗✔️:
2004 // RDL❗✔️: time = 0;
2005 // RDL❗✔️: curr_bcc = 1;
2006 // RDL❗✔️:
2007 // RDL❗✔️: for (u = 0; u < graph->V; ++u) {
2008 // RDL❗✔️: d[u] = 0;
2009 // RDL❗✔️: low[u] = 0;
2010 // RDL❗✔️: }
2011 // RDL❗✔️:
2012 // RDL❗✔️: for (u = 0; u < graph->V; ++u) {
2013 for node in 0..graph.node_count() {
2014 // RDL❗✔️: if (d[u] == 0) {
2015 // RDL❗✔️: RDL_tarjanVisit(graph, u, d, low, &time, &curr_bcc, *bcc, edge_stack);
2016 // RDL❗✔️: }
2017 if discovery[node] == 0 {
2018 tarjan_visit(
2019 graph,
2020 node,
2021 None,
2022 &mut discovery,
2023 &mut low,
2024 &mut time,
2025 &mut current_component,
2026 &mut edge_stack,
2027 &mut edge_components,
2028 );
2029 }
2030 }
2031 // RDL❗✔️: }
2032 // RDL❗✔️:
2033 // RDL❗✔️: return (curr_bcc-1);
2034 // RDL❗✔️: }
2035 // END RDL C FUNCTION RDL_tarjan
2036 edge_components
2037}
2038
2039#[allow(clippy::too_many_arguments)]
2040fn tarjan_visit(
2041 graph: &Graph,
2042 node: usize,
2043 parent: Option<usize>,
2044 discovery: &mut [usize],
2045 low: &mut [usize],
2046 time: &mut usize,
2047 current_component: &mut usize,
2048 edge_stack: &mut Vec<EdgeId>,
2049 edge_components: &mut [usize],
2050) {
2051 // BEGIN RDL C FUNCTION RDL_tarjanVisit
2052 // RDL❗✔️: static void RDL_tarjanVisit(const RDL_graph* graph, unsigned start, unsigned* d,
2053 // RDL❗✔️: unsigned* low, unsigned* time, unsigned* curr_bcc, unsigned* bcc,
2054 // RDL❗✔️: RDL_stack* edge_stack)
2055 // RDL❗✔️: {
2056 // RDL❗✔️: ...
2057 // RDL❗✔️: ++(*time);
2058 // RDL❗✔️: d[start] = *time;
2059 // RDL❗✔️: low[start] = d[start];
2060 *time += 1;
2061 discovery[node] = *time;
2062 low[node] = discovery[node];
2063 // RDL❗✔️:
2064 // RDL❗✔️: while (!RDL_stack_empty(dfs_stack)) {
2065 for &(neighbor, edge_id) in &graph.adjacency[node] {
2066 // RDL❗✔️: v = graph->adjList[u][j][0];
2067 // RDL❗✔️: uv_edge = RDL_edgeId(graph, u, v);
2068 // RDL❗✔️:
2069 // RDL❗✔️: if (d[v] == 0) {
2070 if discovery[neighbor] == 0 {
2071 // RDL❗✔️: edge_elements[next_free_edge_element] = uv_edge;
2072 // RDL❗✔️: RDL_stack_push(edge_stack, &(edge_elements[next_free_edge_element]));
2073 edge_stack.push(edge_id);
2074 tarjan_visit(
2075 graph,
2076 neighbor,
2077 Some(node),
2078 discovery,
2079 low,
2080 time,
2081 current_component,
2082 edge_stack,
2083 edge_components,
2084 );
2085 // RDL❗✔️: low[u] = low[u] < low[v] ? low[u] : low[v];
2086 low[node] = low[node].min(low[neighbor]);
2087 // RDL❗✔️: if (low[v] >= d[u]) {
2088 if low[neighbor] >= discovery[node] {
2089 *current_component += 1;
2090 // RDL❗✔️: do {
2091 loop {
2092 // RDL❗✔️: element = RDL_stack_top(edge_stack);
2093 // RDL❗✔️: curr_edge = *element;
2094 // RDL❗✔️: RDL_stack_pop(edge_stack);
2095 let Some(component_edge) = edge_stack.pop() else {
2096 break;
2097 };
2098 // RDL❗✔️:
2099 // RDL❗✔️: bcc[curr_edge] = *curr_bcc;
2100 edge_components[component_edge.index()] = *current_component;
2101 // RDL❗✔️: } while (curr_edge != uv_edge);
2102 if component_edge == edge_id {
2103 break;
2104 }
2105 }
2106 // RDL❗✔️: ++(*curr_bcc);
2107 }
2108 // RDL❗✔️: }
2109 // RDL❗✔️: }
2110 // RDL❗✔️: else if (d[v] < d[u] && v != parent) {
2111 } else if Some(neighbor) != parent && discovery[neighbor] < discovery[node] {
2112 // RDL❗✔️: edge_elements[next_free_edge_element] = uv_edge;
2113 // RDL❗✔️: RDL_stack_push(edge_stack, &(edge_elements[next_free_edge_element]));
2114 edge_stack.push(edge_id);
2115 // RDL❗✔️: low[u] = low[u] < d[v] ? low[u] : d[v];
2116 low[node] = low[node].min(discovery[neighbor]);
2117 }
2118 // RDL❗✔️: }
2119 }
2120 // RDL❗✔️: }
2121 // END RDL C FUNCTION RDL_tarjanVisit
2122}
2123
2124#[cfg(test)]
2125mod tests {
2126 use super::{
2127 BiconnectedComponents, CycleFamilies, EdgeId, Graph, RingDecomposerError,
2128 RingDecomposition, ShortestPathInfo,
2129 };
2130
2131 #[test]
2132 fn graph_add_undirected_edge_matches_rdl_validation() {
2133 let mut graph = Graph::new(3);
2134
2135 assert_eq!(graph.add_undirected_edge(0, 1).unwrap().index(), 0);
2136 assert_eq!(graph.add_undirected_edge(2, 1).unwrap().index(), 1);
2137 assert!(graph.is_adjacent(0, 1));
2138 assert!(graph.is_adjacent(1, 0));
2139 assert!(graph.is_adjacent(1, 2));
2140 assert_eq!(graph.edge_id(1, 0).unwrap().index(), 0);
2141 assert_eq!(graph.edge_id(1, 2).unwrap().index(), 1);
2142
2143 assert!(matches!(
2144 graph.add_undirected_edge(0, 3),
2145 Err(RingDecomposerError::NodeOutOfRange {
2146 node: 3,
2147 node_count: 3
2148 })
2149 ));
2150 assert!(matches!(
2151 graph.add_undirected_edge(1, 1),
2152 Err(RingDecomposerError::SelfLoop { node: 1 })
2153 ));
2154 assert!(matches!(
2155 graph.add_undirected_edge(1, 0),
2156 Err(RingDecomposerError::DuplicateEdge { from: 1, to: 0 })
2157 ));
2158 }
2159
2160 #[test]
2161 fn graph_connectivity_matches_rdl_depth_first_logic() {
2162 let mut graph = Graph::new(4);
2163 graph.add_undirected_edge(0, 1).unwrap();
2164 graph.add_undirected_edge(1, 2).unwrap();
2165
2166 assert!(!graph.is_connected());
2167
2168 graph.add_undirected_edge(2, 3).unwrap();
2169
2170 assert!(graph.is_connected());
2171 }
2172
2173 #[test]
2174 fn decomposition_handles_acyclic_graph_without_guessing_urf_state() {
2175 let mut graph = Graph::new(3);
2176 graph.add_undirected_edge(0, 1).unwrap();
2177 graph.add_undirected_edge(1, 2).unwrap();
2178
2179 let decomposition = RingDecomposition::calculate(graph).unwrap();
2180
2181 assert_eq!(decomposition.urf_count(), 0);
2182 assert_eq!(decomposition.relevant_cycle_count(), 0.0);
2183 assert!(decomposition.urfs().is_empty());
2184 }
2185
2186 #[test]
2187 fn tarjan_bcc_drops_trivial_edges_like_rdl() {
2188 let mut graph = Graph::new(4);
2189 graph.add_undirected_edge(0, 1).unwrap();
2190 graph.add_undirected_edge(1, 2).unwrap();
2191 graph.add_undirected_edge(2, 3).unwrap();
2192
2193 let bcc = BiconnectedComponents::calculate(&graph);
2194
2195 assert_eq!(bcc.component_count(), 0);
2196 assert_eq!(bcc.edge_component(EdgeId::new(0)), None);
2197 assert_eq!(bcc.edge_component(EdgeId::new(1)), None);
2198 assert_eq!(bcc.edge_component(EdgeId::new(2)), None);
2199 }
2200
2201 #[test]
2202 fn tarjan_bcc_keeps_simple_cycle_as_one_nontrivial_component() {
2203 let mut graph = Graph::new(3);
2204 graph.add_undirected_edge(0, 1).unwrap();
2205 graph.add_undirected_edge(1, 2).unwrap();
2206 graph.add_undirected_edge(2, 0).unwrap();
2207
2208 let bcc = BiconnectedComponents::calculate(&graph);
2209
2210 assert_eq!(bcc.component_count(), 1);
2211 let component = &bcc.components()[0];
2212 assert_eq!(component.original_nodes(), &[0, 1, 2]);
2213 assert_eq!(
2214 component
2215 .original_edges()
2216 .iter()
2217 .map(|edge| edge.index())
2218 .collect::<Vec<_>>(),
2219 vec![0, 1, 2]
2220 );
2221 assert_eq!(component.graph().node_count(), 3);
2222 assert_eq!(component.graph().edge_count(), 3);
2223 assert_eq!(bcc.edge_component(EdgeId::new(0)), Some((0, 0)));
2224 assert_eq!(bcc.edge_component(EdgeId::new(1)), Some((0, 1)));
2225 assert_eq!(bcc.edge_component(EdgeId::new(2)), Some((0, 2)));
2226 assert_eq!(bcc.node_components(0).unwrap(), &[(0, 0)]);
2227 assert_eq!(bcc.node_components(1).unwrap(), &[(0, 1)]);
2228 assert_eq!(bcc.node_components(2).unwrap(), &[(0, 2)]);
2229 }
2230
2231 #[test]
2232 fn tarjan_bcc_splits_two_cycles_sharing_articulation() {
2233 let mut graph = Graph::new(5);
2234 graph.add_undirected_edge(0, 1).unwrap();
2235 graph.add_undirected_edge(1, 2).unwrap();
2236 graph.add_undirected_edge(2, 0).unwrap();
2237 graph.add_undirected_edge(2, 3).unwrap();
2238 graph.add_undirected_edge(3, 4).unwrap();
2239 graph.add_undirected_edge(4, 2).unwrap();
2240
2241 let bcc = BiconnectedComponents::calculate(&graph);
2242
2243 assert_eq!(bcc.component_count(), 2);
2244 assert_eq!(
2245 bcc.components()
2246 .iter()
2247 .map(|component| component.original_edges().len())
2248 .collect::<Vec<_>>(),
2249 vec![3, 3]
2250 );
2251 assert_eq!(bcc.node_components(2).unwrap().len(), 2);
2252 assert_eq!(bcc.components()[0].graph().edge_count(), 3);
2253 assert_eq!(bcc.components()[1].graph().edge_count(), 3);
2254 }
2255
2256 #[test]
2257 fn apsp_records_shortest_distances_and_predecessors() {
2258 let mut graph = Graph::new(4);
2259 graph.add_undirected_edge(0, 1).unwrap();
2260 graph.add_undirected_edge(1, 2).unwrap();
2261 graph.add_undirected_edge(2, 3).unwrap();
2262
2263 let paths = ShortestPathInfo::calculate(&graph);
2264
2265 assert_eq!(paths.distance(0, 0), Some(0));
2266 assert_eq!(paths.distance(0, 3), Some(3));
2267 assert_eq!(paths.predecessor(0, 0), Some(0));
2268 assert_eq!(paths.predecessor(0, 3), Some(2));
2269 assert_eq!(paths.predecessor(3, 0), Some(1));
2270 assert_eq!(paths.distance(0, 99), None);
2271 }
2272
2273 #[test]
2274 fn apsp_reachable_preceding_matches_degree_ordering_rule() {
2275 let mut graph = Graph::new(3);
2276 graph.add_undirected_edge(0, 1).unwrap();
2277 graph.add_undirected_edge(1, 2).unwrap();
2278 graph.add_undirected_edge(2, 0).unwrap();
2279
2280 let paths = ShortestPathInfo::calculate(&graph);
2281
2282 assert!(!paths.reachable_preceding(0, 1));
2283 assert!(!paths.reachable_preceding(0, 2));
2284 assert!(paths.reachable_preceding(1, 0));
2285 assert!(!paths.reachable_preceding(1, 2));
2286 assert!(paths.reachable_preceding(2, 0));
2287 assert!(paths.reachable_preceding(2, 1));
2288 assert_eq!(paths.directed_path_graphs().len(), 3);
2289 }
2290
2291 #[test]
2292 fn cycle_families_find_triangle_family() {
2293 let mut graph = Graph::new(3);
2294 graph.add_undirected_edge(0, 1).unwrap();
2295 graph.add_undirected_edge(1, 2).unwrap();
2296 graph.add_undirected_edge(2, 0).unwrap();
2297 let mut paths = ShortestPathInfo::calculate(&graph);
2298
2299 let cycle_families = CycleFamilies::calculate(&mut graph, &mut paths);
2300
2301 assert_eq!(cycle_families.len(), 1);
2302 let family = &cycle_families.families()[0];
2303 assert_eq!(family.weight(), 3);
2304 assert_eq!(family.x(), None);
2305 assert_eq!(
2306 family
2307 .prototype()
2308 .iter()
2309 .filter(|&&contains| contains)
2310 .count(),
2311 3
2312 );
2313 }
2314
2315 #[test]
2316 fn decomposition_returns_single_urf_for_triangle() {
2317 let mut graph = Graph::new(3);
2318 graph.add_undirected_edge(0, 1).unwrap();
2319 graph.add_undirected_edge(1, 2).unwrap();
2320 graph.add_undirected_edge(2, 0).unwrap();
2321
2322 let decomposition = RingDecomposition::calculate(graph).unwrap();
2323
2324 assert_eq!(decomposition.urf_count(), 1);
2325 assert_eq!(decomposition.relevant_cycle_count(), 1.0);
2326 assert_eq!(decomposition.urfs()[0].nodes(), &[0, 1, 2]);
2327 assert_eq!(
2328 decomposition.urfs()[0]
2329 .edges()
2330 .iter()
2331 .map(|edge| edge.index())
2332 .collect::<Vec<_>>(),
2333 vec![0, 1, 2]
2334 );
2335 }
2336
2337 #[test]
2338 fn decomposition_returns_single_urf_for_square() {
2339 let mut graph = Graph::new(4);
2340 graph.add_undirected_edge(0, 1).unwrap();
2341 graph.add_undirected_edge(1, 2).unwrap();
2342 graph.add_undirected_edge(2, 3).unwrap();
2343 graph.add_undirected_edge(3, 0).unwrap();
2344
2345 let decomposition = RingDecomposition::calculate(graph).unwrap();
2346
2347 assert_eq!(decomposition.urf_count(), 1);
2348 assert_eq!(decomposition.relevant_cycle_count(), 1.0);
2349 assert_eq!(decomposition.urfs()[0].nodes(), &[0, 1, 2, 3]);
2350 assert_eq!(
2351 decomposition.urfs()[0]
2352 .edges()
2353 .iter()
2354 .map(|edge| edge.index())
2355 .collect::<Vec<_>>(),
2356 vec![0, 1, 2, 3]
2357 );
2358 }
2359}