use crate::core::{Graph, IgraphResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToDirectedMode {
Mutual,
Arbitrary,
}
pub fn to_directed(graph: &Graph, mode: ToDirectedMode) -> IgraphResult<Graph> {
let n = graph.vcount();
if graph.is_directed() {
let mut result = Graph::new(n, true)?;
let ecount = graph.ecount();
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let (src, tgt) = graph.edge(eid as u32)?;
result.add_edge(src, tgt)?;
}
return Ok(result);
}
let mut result = Graph::new(n, true)?;
let ecount = graph.ecount();
match mode {
ToDirectedMode::Mutual => {
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let (src, tgt) = graph.edge(eid as u32)?;
result.add_edge(src, tgt)?;
if src != tgt {
result.add_edge(tgt, src)?;
}
}
}
ToDirectedMode::Arbitrary => {
for eid in 0..ecount {
#[allow(clippy::cast_possible_truncation)]
let (src, tgt) = graph.edge(eid as u32)?;
result.add_edge(src, tgt)?;
}
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_already_directed() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert!(d.is_directed());
assert_eq!(d.vcount(), 3);
assert_eq!(d.ecount(), 2);
}
#[test]
fn test_mutual_mode_single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert!(d.is_directed());
assert_eq!(d.ecount(), 2);
}
#[test]
fn test_mutual_mode_triangle() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(0, 2).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert_eq!(d.ecount(), 6); }
#[test]
fn test_arbitrary_mode_single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
assert!(d.is_directed());
assert_eq!(d.ecount(), 1);
}
#[test]
fn test_arbitrary_mode_path() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
assert_eq!(d.ecount(), 3);
}
#[test]
fn test_empty_graph() {
let g = Graph::with_vertices(0);
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert!(d.is_directed());
assert_eq!(d.vcount(), 0);
assert_eq!(d.ecount(), 0);
}
#[test]
fn test_no_edges() {
let g = Graph::with_vertices(5);
let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
assert_eq!(d.vcount(), 5);
assert_eq!(d.ecount(), 0);
}
#[test]
fn test_self_loop_mutual() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert_eq!(d.ecount(), 3);
}
#[test]
fn test_self_loop_arbitrary() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
assert_eq!(d.ecount(), 2);
}
#[test]
fn test_vcount_preserved() {
let mut g = Graph::with_vertices(10);
g.add_edge(3, 7).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert_eq!(d.vcount(), 10);
}
#[test]
fn test_mutual_then_to_undirected_roundtrip() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
assert_eq!(d.ecount(), 6);
let u = super::super::to_undirected::to_undirected(
&d,
super::super::to_undirected::ToUndirectedMode::Collapse,
)
.unwrap();
assert_eq!(u.ecount(), 3);
}
}