rust_igraph/algorithms/properties/
is_split.rs1use crate::algorithms::properties::degree::{DegreeMode, degree_sequence};
14use crate::algorithms::properties::is_simple::is_simple;
15use crate::core::{Graph, IgraphResult};
16
17pub fn is_split_graph(graph: &Graph) -> IgraphResult<bool> {
50 if graph.is_directed() {
51 return Ok(false);
52 }
53
54 let n = graph.vcount() as usize;
55 if n == 0 {
56 return Ok(true);
57 }
58
59 if !is_simple(graph)? {
60 return Ok(false);
61 }
62
63 let mut degrees = degree_sequence(graph, DegreeMode::All)?;
64 degrees.sort_unstable_by(|a, b| b.cmp(a));
65
66 let m = find_m(°rees);
67 if m == 0 {
68 return Ok(true);
69 }
70
71 let sum_top: u64 = degrees[..m].iter().map(|&d| u64::from(d)).sum();
72 let sum_bottom: u64 = degrees[m..].iter().map(|&d| u64::from(d)).sum();
73 let m64 = m as u64;
74 let target = m64
75 .saturating_mul(m64.saturating_sub(1))
76 .saturating_add(sum_bottom);
77
78 Ok(sum_top == target)
79}
80
81fn find_m(sorted_desc: &[u32]) -> usize {
82 let mut m = 0usize;
83 for (i, &d) in sorted_desc.iter().enumerate() {
84 let idx = u32::try_from(i).unwrap_or(u32::MAX);
85 if d >= idx {
86 m = i.saturating_add(1);
87 }
88 }
89 m
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn empty_graph() {
98 let g = Graph::with_vertices(0);
99 assert!(is_split_graph(&g).unwrap());
100 }
101
102 #[test]
103 fn single_vertex() {
104 let g = Graph::with_vertices(1);
105 assert!(is_split_graph(&g).unwrap());
106 }
107
108 #[test]
109 fn edgeless_graph() {
110 let g = Graph::with_vertices(5);
111 assert!(is_split_graph(&g).unwrap());
112 }
113
114 #[test]
115 fn single_edge() {
116 let mut g = Graph::with_vertices(2);
117 g.add_edge(0, 1).unwrap();
118 assert!(is_split_graph(&g).unwrap());
119 }
120
121 #[test]
122 fn path_3_is_split() {
123 let mut g = Graph::with_vertices(3);
124 g.add_edge(0, 1).unwrap();
125 g.add_edge(1, 2).unwrap();
126 assert!(is_split_graph(&g).unwrap());
127 }
128
129 #[test]
130 fn triangle_is_split() {
131 let mut g = Graph::with_vertices(3);
133 g.add_edge(0, 1).unwrap();
134 g.add_edge(1, 2).unwrap();
135 g.add_edge(2, 0).unwrap();
136 assert!(is_split_graph(&g).unwrap());
137 }
138
139 #[test]
140 fn complete_k4_is_split() {
141 let mut g = Graph::with_vertices(4);
142 for u in 0..4u32 {
143 for v in (u + 1)..4 {
144 g.add_edge(u, v).unwrap();
145 }
146 }
147 assert!(is_split_graph(&g).unwrap());
148 }
149
150 #[test]
151 fn star_is_split() {
152 let mut g = Graph::with_vertices(5);
154 for i in 1..5u32 {
155 g.add_edge(0, i).unwrap();
156 }
157 assert!(is_split_graph(&g).unwrap());
158 }
159
160 #[test]
161 fn cycle_4_is_split() {
162 let mut g = Graph::with_vertices(4);
179 g.add_edge(0, 1).unwrap();
180 g.add_edge(1, 2).unwrap();
181 g.add_edge(2, 3).unwrap();
182 g.add_edge(3, 0).unwrap();
183 assert!(!is_split_graph(&g).unwrap());
184 }
185
186 #[test]
187 fn cycle_5_not_split() {
188 let mut g = Graph::with_vertices(5);
189 g.add_edge(0, 1).unwrap();
190 g.add_edge(1, 2).unwrap();
191 g.add_edge(2, 3).unwrap();
192 g.add_edge(3, 4).unwrap();
193 g.add_edge(4, 0).unwrap();
194 assert!(!is_split_graph(&g).unwrap());
195 }
196
197 #[test]
198 fn k4_minus_edge_is_split() {
199 let mut g = Graph::with_vertices(4);
204 g.add_edge(0, 1).unwrap();
205 g.add_edge(0, 2).unwrap();
206 g.add_edge(0, 3).unwrap();
207 g.add_edge(1, 2).unwrap();
208 g.add_edge(1, 3).unwrap();
209 assert!(is_split_graph(&g).unwrap());
211 }
212
213 #[test]
214 fn bipartite_k23_not_split() {
215 let mut g = Graph::with_vertices(5);
219 g.add_edge(0, 2).unwrap();
220 g.add_edge(0, 3).unwrap();
221 g.add_edge(0, 4).unwrap();
222 g.add_edge(1, 2).unwrap();
223 g.add_edge(1, 3).unwrap();
224 g.add_edge(1, 4).unwrap();
225 assert!(!is_split_graph(&g).unwrap());
226 }
227
228 #[test]
229 fn directed_returns_false() {
230 let mut g = Graph::new(3, true).unwrap();
231 g.add_edge(0, 1).unwrap();
232 g.add_edge(1, 2).unwrap();
233 assert!(!is_split_graph(&g).unwrap());
234 }
235
236 #[test]
237 fn self_loop_returns_false() {
238 let mut g = Graph::with_vertices(2);
239 g.add_edge(0, 0).unwrap();
240 g.add_edge(0, 1).unwrap();
241 assert!(!is_split_graph(&g).unwrap());
242 }
243
244 #[test]
245 fn parallel_edges_returns_false() {
246 let mut g = Graph::with_vertices(2);
247 g.add_edge(0, 1).unwrap();
248 g.add_edge(0, 1).unwrap();
249 assert!(!is_split_graph(&g).unwrap());
250 }
251
252 #[test]
253 fn threshold_graph_is_split() {
254 let mut g = Graph::with_vertices(4);
261 g.add_edge(0, 1).unwrap();
262 g.add_edge(3, 0).unwrap();
263 g.add_edge(3, 1).unwrap();
264 g.add_edge(3, 2).unwrap();
265 assert!(is_split_graph(&g).unwrap());
266 }
267}