use crate::core::{Graph, IgraphError, IgraphResult};
pub fn reverse(graph: &Graph) -> IgraphResult<Graph> {
let n = graph.vcount();
let directed = graph.is_directed();
let ecount = graph.ecount();
let mut edges: Vec<(u32, u32)> = Vec::with_capacity(ecount);
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid_u32 = eid as u32;
let (src, tgt) = graph.edge(eid_u32)?;
if directed {
edges.push((tgt, src));
} else {
edges.push((src, tgt));
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn reverse_edges(graph: &Graph, eids: &[u32]) -> IgraphResult<Graph> {
let n = graph.vcount();
let directed = graph.is_directed();
let ecount = graph.ecount();
if !directed {
return reverse_edges_copy_undirected(graph, n, ecount);
}
let mut to_reverse: Vec<bool> = vec![false; ecount];
for &eid in eids {
if eid as usize >= ecount {
return Err(IgraphError::InvalidArgument(format!(
"reverse_edges: edge ID {eid} out of range (graph has {ecount} edges)"
)));
}
to_reverse[eid as usize] = true;
}
let mut result = Graph::new(n, true)?;
for (eid_usize, &rev) in to_reverse.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
let eid = eid_usize as u32;
let (src, tgt) = graph.edge(eid)?;
if rev {
result.add_edge(tgt, src)?;
} else {
result.add_edge(src, tgt)?;
}
}
Ok(result)
}
fn reverse_edges_copy_undirected(graph: &Graph, n: u32, ecount: usize) -> IgraphResult<Graph> {
let mut result = Graph::with_vertices(n);
for eid_usize in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let eid = eid_usize as u32;
let (src, tgt) = graph.edge(eid)?;
result.add_edge(src, tgt)?;
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reverse_directed() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
let rev = reverse(&g).unwrap();
assert!(rev.is_directed());
assert_eq!(rev.vcount(), 4);
assert_eq!(rev.ecount(), 3);
let (s, t) = rev.edge(0).unwrap();
assert_eq!((s, t), (1, 0));
let (s, t) = rev.edge(1).unwrap();
assert_eq!((s, t), (2, 1));
let (s, t) = rev.edge(2).unwrap();
assert_eq!((s, t), (3, 2));
}
#[test]
fn test_reverse_undirected() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let rev = reverse(&g).unwrap();
assert!(!rev.is_directed());
assert_eq!(rev.vcount(), 3);
assert_eq!(rev.ecount(), 2);
}
#[test]
fn test_reverse_empty() {
let g = Graph::new(5, true).unwrap();
let rev = reverse(&g).unwrap();
assert_eq!(rev.vcount(), 5);
assert_eq!(rev.ecount(), 0);
}
#[test]
fn test_reverse_self_loop() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let rev = reverse(&g).unwrap();
let (s, t) = rev.edge(0).unwrap();
assert_eq!((s, t), (0, 0)); let (s, t) = rev.edge(1).unwrap();
assert_eq!((s, t), (1, 0)); }
#[test]
fn test_reverse_involution() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(3, 0).unwrap();
let rev2 = reverse(&reverse(&g).unwrap()).unwrap();
assert_eq!(rev2.ecount(), 3);
for eid in 0..3u32 {
assert_eq!(g.edge(eid).unwrap(), rev2.edge(eid).unwrap());
}
}
#[test]
fn test_reverse_preserves_multi_edges() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
let rev = reverse(&g).unwrap();
assert_eq!(rev.ecount(), 2);
assert_eq!(rev.edge(0).unwrap(), (1, 0));
assert_eq!(rev.edge(1).unwrap(), (1, 0));
}
#[test]
fn test_reverse_edges_subset() {
let mut g = Graph::new(4, true).unwrap();
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();
assert_eq!(rev.edge(0).unwrap(), (1, 0)); assert_eq!(rev.edge(1).unwrap(), (1, 2)); assert_eq!(rev.edge(2).unwrap(), (3, 2)); }
#[test]
fn test_reverse_edges_empty_set() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let rev = reverse_edges(&g, &[]).unwrap();
assert_eq!(rev.edge(0).unwrap(), (0, 1));
assert_eq!(rev.edge(1).unwrap(), (1, 2));
}
#[test]
fn test_reverse_edges_all() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let rev = reverse_edges(&g, &[0, 1]).unwrap();
assert_eq!(rev.edge(0).unwrap(), (1, 0));
assert_eq!(rev.edge(1).unwrap(), (2, 1));
}
#[test]
fn test_reverse_edges_undirected_noop() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let rev = reverse_edges(&g, &[0]).unwrap();
assert!(!rev.is_directed());
assert_eq!(rev.ecount(), 2);
}
#[test]
fn test_reverse_edges_out_of_range() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let err = reverse_edges(&g, &[5]).unwrap_err();
assert!(matches!(err, IgraphError::InvalidArgument(_)));
}
#[test]
fn test_reverse_edges_duplicates() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap();
let rev = reverse_edges(&g, &[0, 0, 0]).unwrap();
assert_eq!(rev.edge(0).unwrap(), (1, 0));
assert_eq!(rev.edge(1).unwrap(), (1, 2));
}
#[test]
fn test_reverse_edges_self_loop() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
let rev = reverse_edges(&g, &[0, 1]).unwrap();
assert_eq!(rev.edge(0).unwrap(), (0, 0)); assert_eq!(rev.edge(1).unwrap(), (1, 0)); }
}