rust_igraph/algorithms/operators/
complementer.rs1use std::collections::BTreeSet;
15
16use crate::core::graph::EdgeId;
17use crate::core::{Graph, IgraphResult, VertexId};
18
19pub fn complementer(graph: &Graph, loops: bool) -> IgraphResult<Graph> {
44 let n = graph.vcount();
45 let directed = graph.is_directed();
46 let mut g = Graph::new(n, directed)?;
47 if n == 0 {
48 return Ok(g);
49 }
50
51 let m = u32::try_from(graph.ecount())
56 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
57 let mut existing: BTreeSet<(VertexId, VertexId)> = BTreeSet::new();
58 for e in 0..m {
59 existing.insert(graph.edge(e as EdgeId)?);
60 }
61
62 let mut new_edges: Vec<(VertexId, VertexId)> = Vec::new();
63 if directed {
64 for u in 0..n {
65 for v in 0..n {
66 if u == v && !loops {
67 continue;
68 }
69 if !existing.contains(&(u, v)) {
70 new_edges.push((u, v));
71 }
72 }
73 }
74 } else {
75 for u in 0..n {
78 let lo = if loops { u } else { u + 1 };
79 for v in lo..n {
80 if !existing.contains(&(u, v)) {
81 new_edges.push((u, v));
82 }
83 }
84 }
85 }
86
87 g.add_edges(new_edges)?;
88 Ok(g)
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 fn sorted_edges(g: &Graph) -> Vec<(VertexId, VertexId)> {
96 let m = u32::try_from(g.ecount()).unwrap();
97 let mut v: Vec<_> = (0..m).map(|e| g.edge(e).unwrap()).collect();
98 v.sort_unstable();
99 v
100 }
101
102 #[test]
103 fn empty_graph_yields_empty() {
104 let g = Graph::with_vertices(0);
105 let c = complementer(&g, false).unwrap();
106 assert_eq!(c.vcount(), 0);
107 assert_eq!(c.ecount(), 0);
108 }
109
110 #[test]
111 fn no_edges_no_loops_is_complete_graph() {
112 let g = Graph::with_vertices(3);
114 let c = complementer(&g, false).unwrap();
115 assert_eq!(sorted_edges(&c), vec![(0, 1), (0, 2), (1, 2)]);
116 }
117
118 #[test]
119 fn no_edges_with_loops_adds_self_loops() {
120 let g = Graph::with_vertices(2);
121 let c = complementer(&g, true).unwrap();
122 assert_eq!(sorted_edges(&c), vec![(0, 0), (0, 1), (1, 1)]);
124 }
125
126 #[test]
127 fn complete_graph_complementer_is_empty() {
128 let mut g = Graph::with_vertices(3);
130 g.add_edge(0, 1).unwrap();
131 g.add_edge(0, 2).unwrap();
132 g.add_edge(1, 2).unwrap();
133 let c = complementer(&g, false).unwrap();
134 assert_eq!(c.ecount(), 0);
135 }
136
137 #[test]
138 fn path_complementer_is_single_chord() {
139 let mut g = Graph::with_vertices(3);
140 g.add_edge(0, 1).unwrap();
141 g.add_edge(1, 2).unwrap();
142 let c = complementer(&g, false).unwrap();
143 assert_eq!(c.ecount(), 1);
144 assert_eq!(c.edge(0).unwrap(), (0, 2));
145 }
146
147 #[test]
148 fn directed_complementer_includes_reverses() {
149 let mut g = Graph::new(3, true).unwrap();
152 g.add_edge(0, 1).unwrap();
153 let c = complementer(&g, false).unwrap();
154 assert!(c.is_directed());
155 assert_eq!(
156 sorted_edges(&c),
157 vec![(0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
158 );
159 }
160
161 #[test]
162 fn directed_with_loops_adds_self_loops() {
163 let mut g = Graph::new(2, true).unwrap();
164 g.add_edge(0, 1).unwrap();
165 let c = complementer(&g, true).unwrap();
166 assert_eq!(sorted_edges(&c), vec![(0, 0), (1, 0), (1, 1)]);
167 }
168
169 #[test]
170 fn double_complement_recovers_original_for_simple_undirected() {
171 let mut g = Graph::with_vertices(4);
173 g.add_edge(0, 1).unwrap();
174 g.add_edge(1, 2).unwrap();
175 g.add_edge(2, 3).unwrap();
176 let c = complementer(&g, false).unwrap();
177 let cc = complementer(&c, false).unwrap();
178 assert_eq!(sorted_edges(&cc), sorted_edges(&g));
179 }
180
181 #[test]
182 fn singleton_with_loops() {
183 let g = Graph::with_vertices(1);
184 let c = complementer(&g, true).unwrap();
185 assert_eq!(sorted_edges(&c), vec![(0, 0)]);
186 }
187
188 #[test]
189 fn singleton_without_loops() {
190 let g = Graph::with_vertices(1);
191 let c = complementer(&g, false).unwrap();
192 assert_eq!(c.ecount(), 0);
193 }
194
195 #[test]
196 fn parallel_edges_in_input_collapse() {
197 let mut g = Graph::with_vertices(3);
200 g.add_edge(0, 1).unwrap();
201 g.add_edge(0, 1).unwrap();
202 let c = complementer(&g, false).unwrap();
203 assert_eq!(sorted_edges(&c), vec![(0, 2), (1, 2)]);
205 }
206}