rust_igraph/algorithms/operators/
reverse.rs1use crate::core::{Graph, IgraphError, IgraphResult};
6
7pub fn reverse(graph: &Graph) -> IgraphResult<Graph> {
27 let n = graph.vcount();
28 let directed = graph.is_directed();
29 let ecount = graph.ecount();
30
31 let mut edges: Vec<(u32, u32)> = Vec::with_capacity(ecount);
32
33 for eid in 0..ecount {
34 #[allow(clippy::cast_possible_truncation)]
35 let eid_u32 = eid as u32;
36 let (src, tgt) = graph.edge(eid_u32)?;
37 if directed {
38 edges.push((tgt, src));
39 } else {
40 edges.push((src, tgt));
41 }
42 }
43
44 let mut result = Graph::new(n, directed)?;
45 result.add_edges(edges)?;
46 Ok(result)
47}
48
49pub fn reverse_edges(graph: &Graph, eids: &[u32]) -> IgraphResult<Graph> {
77 let n = graph.vcount();
78 let directed = graph.is_directed();
79 let ecount = graph.ecount();
80
81 if !directed {
82 return reverse_edges_copy_undirected(graph, n, ecount);
84 }
85
86 let mut to_reverse: Vec<bool> = vec![false; ecount];
88 for &eid in eids {
89 if eid as usize >= ecount {
90 return Err(IgraphError::InvalidArgument(format!(
91 "reverse_edges: edge ID {eid} out of range (graph has {ecount} edges)"
92 )));
93 }
94 to_reverse[eid as usize] = true;
95 }
96
97 let mut result = Graph::new(n, true)?;
98 for (eid_usize, &rev) in to_reverse.iter().enumerate() {
99 #[allow(clippy::cast_possible_truncation)]
100 let eid = eid_usize as u32;
101 let (src, tgt) = graph.edge(eid)?;
102 if rev {
103 result.add_edge(tgt, src)?;
104 } else {
105 result.add_edge(src, tgt)?;
106 }
107 }
108
109 Ok(result)
110}
111
112fn reverse_edges_copy_undirected(graph: &Graph, n: u32, ecount: usize) -> IgraphResult<Graph> {
113 let mut result = Graph::with_vertices(n);
114 for eid_usize in 0..ecount {
115 #[allow(clippy::cast_possible_truncation)]
116 let eid = eid_usize as u32;
117 let (src, tgt) = graph.edge(eid)?;
118 result.add_edge(src, tgt)?;
119 }
120 Ok(result)
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_reverse_directed() {
129 let mut g = Graph::new(4, true).unwrap();
130 g.add_edge(0, 1).unwrap();
131 g.add_edge(1, 2).unwrap();
132 g.add_edge(2, 3).unwrap();
133
134 let rev = reverse(&g).unwrap();
135 assert!(rev.is_directed());
136 assert_eq!(rev.vcount(), 4);
137 assert_eq!(rev.ecount(), 3);
138
139 let (s, t) = rev.edge(0).unwrap();
141 assert_eq!((s, t), (1, 0));
142 let (s, t) = rev.edge(1).unwrap();
143 assert_eq!((s, t), (2, 1));
144 let (s, t) = rev.edge(2).unwrap();
145 assert_eq!((s, t), (3, 2));
146 }
147
148 #[test]
149 fn test_reverse_undirected() {
150 let mut g = Graph::with_vertices(3);
151 g.add_edge(0, 1).unwrap();
152 g.add_edge(1, 2).unwrap();
153
154 let rev = reverse(&g).unwrap();
155 assert!(!rev.is_directed());
156 assert_eq!(rev.vcount(), 3);
157 assert_eq!(rev.ecount(), 2);
158 }
159
160 #[test]
161 fn test_reverse_empty() {
162 let g = Graph::new(5, true).unwrap();
163 let rev = reverse(&g).unwrap();
164 assert_eq!(rev.vcount(), 5);
165 assert_eq!(rev.ecount(), 0);
166 }
167
168 #[test]
169 fn test_reverse_self_loop() {
170 let mut g = Graph::new(2, true).unwrap();
171 g.add_edge(0, 0).unwrap();
172 g.add_edge(0, 1).unwrap();
173
174 let rev = reverse(&g).unwrap();
175 let (s, t) = rev.edge(0).unwrap();
176 assert_eq!((s, t), (0, 0)); let (s, t) = rev.edge(1).unwrap();
178 assert_eq!((s, t), (1, 0)); }
180
181 #[test]
182 fn test_reverse_involution() {
183 let mut g = Graph::new(4, true).unwrap();
185 g.add_edge(0, 1).unwrap();
186 g.add_edge(1, 2).unwrap();
187 g.add_edge(3, 0).unwrap();
188
189 let rev2 = reverse(&reverse(&g).unwrap()).unwrap();
190 assert_eq!(rev2.ecount(), 3);
191 for eid in 0..3u32 {
192 assert_eq!(g.edge(eid).unwrap(), rev2.edge(eid).unwrap());
193 }
194 }
195
196 #[test]
197 fn test_reverse_preserves_multi_edges() {
198 let mut g = Graph::new(2, true).unwrap();
199 g.add_edge(0, 1).unwrap();
200 g.add_edge(0, 1).unwrap();
201
202 let rev = reverse(&g).unwrap();
203 assert_eq!(rev.ecount(), 2);
204 assert_eq!(rev.edge(0).unwrap(), (1, 0));
205 assert_eq!(rev.edge(1).unwrap(), (1, 0));
206 }
207
208 #[test]
211 fn test_reverse_edges_subset() {
212 let mut g = Graph::new(4, true).unwrap();
213 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); let rev = reverse_edges(&g, &[0, 2]).unwrap();
218 assert_eq!(rev.edge(0).unwrap(), (1, 0)); assert_eq!(rev.edge(1).unwrap(), (1, 2)); assert_eq!(rev.edge(2).unwrap(), (3, 2)); }
222
223 #[test]
224 fn test_reverse_edges_empty_set() {
225 let mut g = Graph::new(3, true).unwrap();
226 g.add_edge(0, 1).unwrap();
227 g.add_edge(1, 2).unwrap();
228
229 let rev = reverse_edges(&g, &[]).unwrap();
230 assert_eq!(rev.edge(0).unwrap(), (0, 1));
231 assert_eq!(rev.edge(1).unwrap(), (1, 2));
232 }
233
234 #[test]
235 fn test_reverse_edges_all() {
236 let mut g = Graph::new(3, true).unwrap();
237 g.add_edge(0, 1).unwrap();
238 g.add_edge(1, 2).unwrap();
239
240 let rev = reverse_edges(&g, &[0, 1]).unwrap();
241 assert_eq!(rev.edge(0).unwrap(), (1, 0));
242 assert_eq!(rev.edge(1).unwrap(), (2, 1));
243 }
244
245 #[test]
246 fn test_reverse_edges_undirected_noop() {
247 let mut g = Graph::with_vertices(3);
248 g.add_edge(0, 1).unwrap();
249 g.add_edge(1, 2).unwrap();
250
251 let rev = reverse_edges(&g, &[0]).unwrap();
252 assert!(!rev.is_directed());
253 assert_eq!(rev.ecount(), 2);
254 }
255
256 #[test]
257 fn test_reverse_edges_out_of_range() {
258 let mut g = Graph::new(2, true).unwrap();
259 g.add_edge(0, 1).unwrap();
260
261 let err = reverse_edges(&g, &[5]).unwrap_err();
262 assert!(matches!(err, IgraphError::InvalidArgument(_)));
263 }
264
265 #[test]
266 fn test_reverse_edges_duplicates() {
267 let mut g = Graph::new(3, true).unwrap();
268 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let rev = reverse_edges(&g, &[0, 0, 0]).unwrap();
273 assert_eq!(rev.edge(0).unwrap(), (1, 0));
274 assert_eq!(rev.edge(1).unwrap(), (1, 2));
275 }
276
277 #[test]
278 fn test_reverse_edges_self_loop() {
279 let mut g = Graph::new(2, true).unwrap();
280 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
282
283 let rev = reverse_edges(&g, &[0, 1]).unwrap();
284 assert_eq!(rev.edge(0).unwrap(), (0, 0)); assert_eq!(rev.edge(1).unwrap(), (1, 0)); }
287}