Skip to main content

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}