rust_igraph/algorithms/operators/
even_tarjan.rs1use crate::core::{Graph, IgraphError, IgraphResult};
11
12#[derive(Debug, Clone)]
14pub struct EvenTarjanResult {
15 pub graph: Graph,
17 pub capacity: Vec<f64>,
20}
21
22pub fn even_tarjan_reduction(graph: &Graph) -> IgraphResult<EvenTarjanResult> {
57 let n = graph.vcount();
58 let m = graph.ecount();
59
60 let new_n = n.checked_mul(2).ok_or_else(|| {
61 IgraphError::InvalidArgument("even_tarjan_reduction: vertex count overflow".into())
62 })?;
63
64 let edges_from_vertices = n as usize;
65 let edges_from_edges = m.checked_mul(2).ok_or_else(|| {
66 IgraphError::InvalidArgument("even_tarjan_reduction: edge count overflow".into())
67 })?;
68 let total_edges = edges_from_vertices
69 .checked_add(edges_from_edges)
70 .ok_or_else(|| {
71 IgraphError::InvalidArgument("even_tarjan_reduction: total edge count overflow".into())
72 })?;
73
74 let mut new_graph = Graph::new(new_n, true)?;
75 let mut capacity: Vec<f64> = Vec::with_capacity(total_edges);
76
77 for i in 0..n {
79 new_graph.add_edge(i, i + n)?;
80 capacity.push(1.0);
81 }
82
83 for eid in 0..m {
86 let eid32 = u32::try_from(eid).map_err(|_| {
87 IgraphError::InvalidArgument("even_tarjan_reduction: edge id overflow".into())
88 })?;
89 let (from, to) = graph.edge(eid32)?;
90
91 new_graph.add_edge(from + n, to)?;
92 capacity.push(f64::INFINITY);
93
94 new_graph.add_edge(to + n, from)?;
95 capacity.push(f64::INFINITY);
96 }
97
98 Ok(EvenTarjanResult {
99 graph: new_graph,
100 capacity,
101 })
102}
103
104#[cfg(test)]
105#[allow(clippy::float_cmp)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn empty_graph() {
111 let g = Graph::new(0, true).unwrap();
112 let result = even_tarjan_reduction(&g).unwrap();
113 assert_eq!(result.graph.vcount(), 0);
114 assert_eq!(result.graph.ecount(), 0);
115 assert!(result.capacity.is_empty());
116 }
117
118 #[test]
119 fn single_vertex() {
120 let g = Graph::new(1, true).unwrap();
121 let result = even_tarjan_reduction(&g).unwrap();
122 assert_eq!(result.graph.vcount(), 2);
123 assert_eq!(result.graph.ecount(), 1);
124 assert_eq!(result.capacity, vec![1.0]);
125 let (from, to) = result.graph.edge(0).unwrap();
126 assert_eq!((from, to), (0, 1));
127 }
128
129 #[test]
130 fn single_edge() {
131 let mut g = Graph::new(2, true).unwrap();
132 g.add_edge(0, 1).unwrap();
133 let result = even_tarjan_reduction(&g).unwrap();
134 assert_eq!(result.graph.vcount(), 4);
136 assert_eq!(result.graph.ecount(), 4);
138 assert_eq!(
139 result.capacity,
140 vec![1.0, 1.0, f64::INFINITY, f64::INFINITY]
141 );
142
143 assert_eq!(result.graph.edge(0).unwrap(), (0, 2));
145 assert_eq!(result.graph.edge(1).unwrap(), (1, 3));
146 assert_eq!(result.graph.edge(2).unwrap(), (2, 1));
148 assert_eq!(result.graph.edge(3).unwrap(), (3, 0));
149 }
150
151 #[test]
152 fn triangle() {
153 let mut g = Graph::new(3, true).unwrap();
154 g.add_edge(0, 1).unwrap();
155 g.add_edge(1, 2).unwrap();
156 g.add_edge(2, 0).unwrap();
157 let result = even_tarjan_reduction(&g).unwrap();
158 assert_eq!(result.graph.vcount(), 6);
159 assert_eq!(result.graph.ecount(), 9);
161 assert_eq!(result.capacity.len(), 9);
162 for i in 0..3 {
164 assert_eq!(result.capacity[i], 1.0);
165 }
166 for i in 3..9 {
168 assert_eq!(result.capacity[i], f64::INFINITY);
169 }
170 }
171
172 #[test]
173 fn undirected_graph() {
174 let mut g = Graph::with_vertices(3);
176 g.add_edge(0, 1).unwrap();
177 g.add_edge(1, 2).unwrap();
178 let result = even_tarjan_reduction(&g).unwrap();
179 assert_eq!(result.graph.vcount(), 6);
180 assert_eq!(result.graph.ecount(), 7);
182 assert!(result.graph.is_directed());
183 }
184
185 #[test]
186 fn self_loop() {
187 let mut g = Graph::new(2, true).unwrap();
188 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
190 let result = even_tarjan_reduction(&g).unwrap();
191 assert_eq!(result.graph.vcount(), 4);
192 assert_eq!(result.graph.ecount(), 6);
194 }
195
196 #[test]
197 fn capacity_values() {
198 let mut g = Graph::new(4, true).unwrap();
199 g.add_edge(0, 1).unwrap();
200 g.add_edge(2, 3).unwrap();
201 let result = even_tarjan_reduction(&g).unwrap();
202 assert_eq!(result.capacity.len(), 8);
204 let ones: Vec<f64> = result
205 .capacity
206 .iter()
207 .filter(|&&c| c == 1.0)
208 .copied()
209 .collect();
210 let infs: Vec<f64> = result
211 .capacity
212 .iter()
213 .filter(|c| c.is_infinite())
214 .copied()
215 .collect();
216 assert_eq!(ones.len(), 4);
217 assert_eq!(infs.len(), 4);
218 }
219}