rust_igraph/algorithms/operators/
to_directed.rs1use crate::core::{Graph, IgraphResult};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ToDirectedMode {
11 Mutual,
13 Arbitrary,
16}
17
18pub fn to_directed(graph: &Graph, mode: ToDirectedMode) -> IgraphResult<Graph> {
49 let n = graph.vcount();
50
51 if graph.is_directed() {
52 let mut result = Graph::new(n, true)?;
54 let ecount = graph.ecount();
55 for eid in 0..ecount {
56 #[allow(clippy::cast_possible_truncation)]
57 let (src, tgt) = graph.edge(eid as u32)?;
58 result.add_edge(src, tgt)?;
59 }
60 return Ok(result);
61 }
62
63 let mut result = Graph::new(n, true)?;
64 let ecount = graph.ecount();
65
66 match mode {
67 ToDirectedMode::Mutual => {
68 for eid in 0..ecount {
69 #[allow(clippy::cast_possible_truncation)]
70 let (src, tgt) = graph.edge(eid as u32)?;
71 result.add_edge(src, tgt)?;
72 if src != tgt {
73 result.add_edge(tgt, src)?;
74 }
75 }
76 }
77 ToDirectedMode::Arbitrary => {
78 for eid in 0..ecount {
79 #[allow(clippy::cast_possible_truncation)]
80 let (src, tgt) = graph.edge(eid as u32)?;
81 result.add_edge(src, tgt)?;
83 }
84 }
85 }
86
87 Ok(result)
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_already_directed() {
96 let mut g = Graph::new(3, true).unwrap();
97 g.add_edge(0, 1).unwrap();
98 g.add_edge(1, 2).unwrap();
99 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
100 assert!(d.is_directed());
101 assert_eq!(d.vcount(), 3);
102 assert_eq!(d.ecount(), 2);
103 }
104
105 #[test]
106 fn test_mutual_mode_single_edge() {
107 let mut g = Graph::with_vertices(2);
108 g.add_edge(0, 1).unwrap();
109 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
110 assert!(d.is_directed());
111 assert_eq!(d.ecount(), 2);
112 }
113
114 #[test]
115 fn test_mutual_mode_triangle() {
116 let mut g = Graph::with_vertices(3);
117 g.add_edge(0, 1).unwrap();
118 g.add_edge(1, 2).unwrap();
119 g.add_edge(0, 2).unwrap();
120 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
121 assert_eq!(d.ecount(), 6); }
123
124 #[test]
125 fn test_arbitrary_mode_single_edge() {
126 let mut g = Graph::with_vertices(2);
127 g.add_edge(0, 1).unwrap();
128 let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
129 assert!(d.is_directed());
130 assert_eq!(d.ecount(), 1);
131 }
132
133 #[test]
134 fn test_arbitrary_mode_path() {
135 let mut g = Graph::with_vertices(4);
136 g.add_edge(0, 1).unwrap();
137 g.add_edge(1, 2).unwrap();
138 g.add_edge(2, 3).unwrap();
139 let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
140 assert_eq!(d.ecount(), 3);
141 }
142
143 #[test]
144 fn test_empty_graph() {
145 let g = Graph::with_vertices(0);
146 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
147 assert!(d.is_directed());
148 assert_eq!(d.vcount(), 0);
149 assert_eq!(d.ecount(), 0);
150 }
151
152 #[test]
153 fn test_no_edges() {
154 let g = Graph::with_vertices(5);
155 let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
156 assert_eq!(d.vcount(), 5);
157 assert_eq!(d.ecount(), 0);
158 }
159
160 #[test]
161 fn test_self_loop_mutual() {
162 let mut g = Graph::with_vertices(2);
163 g.add_edge(0, 0).unwrap();
164 g.add_edge(0, 1).unwrap();
165 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
166 assert_eq!(d.ecount(), 3);
168 }
169
170 #[test]
171 fn test_self_loop_arbitrary() {
172 let mut g = Graph::with_vertices(2);
173 g.add_edge(0, 0).unwrap();
174 g.add_edge(0, 1).unwrap();
175 let d = to_directed(&g, ToDirectedMode::Arbitrary).unwrap();
176 assert_eq!(d.ecount(), 2);
177 }
178
179 #[test]
180 fn test_vcount_preserved() {
181 let mut g = Graph::with_vertices(10);
182 g.add_edge(3, 7).unwrap();
183 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
184 assert_eq!(d.vcount(), 10);
185 }
186
187 #[test]
188 fn test_mutual_then_to_undirected_roundtrip() {
189 let mut g = Graph::with_vertices(4);
192 g.add_edge(0, 1).unwrap();
193 g.add_edge(1, 2).unwrap();
194 g.add_edge(2, 3).unwrap();
195 let d = to_directed(&g, ToDirectedMode::Mutual).unwrap();
196 assert_eq!(d.ecount(), 6);
197
198 let u = super::super::to_undirected::to_undirected(
200 &d,
201 super::super::to_undirected::ToUndirectedMode::Collapse,
202 )
203 .unwrap();
204 assert_eq!(u.ecount(), 3);
205 }
206}