rust_igraph/algorithms/properties/
is_threshold.rs1use crate::algorithms::properties::degree::{DegreeMode, degree_sequence};
20use crate::algorithms::properties::is_simple::is_simple;
21use crate::core::{Graph, IgraphResult};
22
23pub fn is_threshold_graph(graph: &Graph) -> IgraphResult<bool> {
56 if graph.is_directed() {
57 return Ok(false);
58 }
59
60 let n = graph.vcount() as usize;
61 if n <= 1 {
62 return Ok(true);
63 }
64
65 if !is_simple(graph)? {
66 return Ok(false);
67 }
68
69 let mut degrees = degree_sequence(graph, DegreeMode::All)?;
70 degrees.sort_unstable_by(|a, b| b.cmp(a));
71
72 let mut remaining = n;
73 let mut degs = &mut degrees[..];
74
75 while remaining > 0 {
76 let top = degs[0];
77 let last_idx = remaining.saturating_sub(1);
78
79 if top == 0 {
80 return Ok(true);
81 }
82
83 let top_usize = top as usize;
84 if top_usize == last_idx {
85 degs = &mut degs[1..];
87 remaining = remaining.saturating_sub(1);
88 for d in &mut degs[..top_usize.min(remaining)] {
89 *d = d.saturating_sub(1);
90 }
91 degs.sort_unstable_by(|a, b| b.cmp(a));
92 } else if degs[last_idx] == 0 {
93 remaining = remaining.saturating_sub(1);
95 } else {
96 return Ok(false);
97 }
98 }
99
100 Ok(true)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn empty_graph() {
109 let g = Graph::with_vertices(0);
110 assert!(is_threshold_graph(&g).unwrap());
111 }
112
113 #[test]
114 fn single_vertex() {
115 let g = Graph::with_vertices(1);
116 assert!(is_threshold_graph(&g).unwrap());
117 }
118
119 #[test]
120 fn edgeless_graph() {
121 let g = Graph::with_vertices(5);
122 assert!(is_threshold_graph(&g).unwrap());
123 }
124
125 #[test]
126 fn single_edge() {
127 let mut g = Graph::with_vertices(2);
128 g.add_edge(0, 1).unwrap();
129 assert!(is_threshold_graph(&g).unwrap());
130 }
131
132 #[test]
133 fn complete_k3() {
134 let mut g = Graph::with_vertices(3);
135 g.add_edge(0, 1).unwrap();
136 g.add_edge(1, 2).unwrap();
137 g.add_edge(2, 0).unwrap();
138 assert!(is_threshold_graph(&g).unwrap());
139 }
140
141 #[test]
142 fn complete_k4() {
143 let mut g = Graph::with_vertices(4);
144 for u in 0..4u32 {
145 for v in (u + 1)..4 {
146 g.add_edge(u, v).unwrap();
147 }
148 }
149 assert!(is_threshold_graph(&g).unwrap());
150 }
151
152 #[test]
153 fn star_is_threshold() {
154 let mut g = Graph::with_vertices(5);
155 for i in 1..5u32 {
156 g.add_edge(0, i).unwrap();
157 }
158 assert!(is_threshold_graph(&g).unwrap());
159 }
160
161 #[test]
162 fn path_3_is_threshold() {
163 let mut g = Graph::with_vertices(3);
166 g.add_edge(0, 1).unwrap();
167 g.add_edge(1, 2).unwrap();
168 assert!(is_threshold_graph(&g).unwrap());
169 }
170
171 #[test]
172 fn path_4_not_threshold() {
173 let mut g = Graph::with_vertices(4);
176 g.add_edge(0, 1).unwrap();
177 g.add_edge(1, 2).unwrap();
178 g.add_edge(2, 3).unwrap();
179 assert!(!is_threshold_graph(&g).unwrap());
180 }
181
182 #[test]
183 fn cycle_4_not_threshold() {
184 let mut g = Graph::with_vertices(4);
186 g.add_edge(0, 1).unwrap();
187 g.add_edge(1, 2).unwrap();
188 g.add_edge(2, 3).unwrap();
189 g.add_edge(3, 0).unwrap();
190 assert!(!is_threshold_graph(&g).unwrap());
191 }
192
193 #[test]
194 fn cycle_5_not_threshold() {
195 let mut g = Graph::with_vertices(5);
196 g.add_edge(0, 1).unwrap();
197 g.add_edge(1, 2).unwrap();
198 g.add_edge(2, 3).unwrap();
199 g.add_edge(3, 4).unwrap();
200 g.add_edge(4, 0).unwrap();
201 assert!(!is_threshold_graph(&g).unwrap());
202 }
203
204 #[test]
205 fn two_k2_not_threshold() {
206 let mut g = Graph::with_vertices(4);
209 g.add_edge(0, 1).unwrap();
210 g.add_edge(2, 3).unwrap();
211 assert!(!is_threshold_graph(&g).unwrap());
212 }
213
214 #[test]
215 fn threshold_build_sequence() {
216 let mut g = Graph::with_vertices(4);
220 g.add_edge(0, 1).unwrap();
221 g.add_edge(3, 0).unwrap();
222 g.add_edge(3, 1).unwrap();
223 g.add_edge(3, 2).unwrap();
224 assert!(is_threshold_graph(&g).unwrap());
225 }
226
227 #[test]
228 fn k4_minus_edge_is_threshold() {
229 let mut g = Graph::with_vertices(4);
232 g.add_edge(0, 1).unwrap();
233 g.add_edge(0, 2).unwrap();
234 g.add_edge(0, 3).unwrap();
235 g.add_edge(1, 2).unwrap();
236 g.add_edge(1, 3).unwrap();
237 assert!(is_threshold_graph(&g).unwrap());
238 }
239
240 #[test]
241 fn directed_returns_false() {
242 let mut g = Graph::new(3, true).unwrap();
243 g.add_edge(0, 1).unwrap();
244 g.add_edge(1, 2).unwrap();
245 assert!(!is_threshold_graph(&g).unwrap());
246 }
247
248 #[test]
249 fn self_loop_returns_false() {
250 let mut g = Graph::with_vertices(2);
251 g.add_edge(0, 0).unwrap();
252 g.add_edge(0, 1).unwrap();
253 assert!(!is_threshold_graph(&g).unwrap());
254 }
255
256 #[test]
257 fn parallel_edges_returns_false() {
258 let mut g = Graph::with_vertices(2);
259 g.add_edge(0, 1).unwrap();
260 g.add_edge(0, 1).unwrap();
261 assert!(!is_threshold_graph(&g).unwrap());
262 }
263
264 #[test]
265 fn large_star_threshold() {
266 let mut g = Graph::with_vertices(10);
267 for i in 1..10u32 {
268 g.add_edge(0, i).unwrap();
269 }
270 assert!(is_threshold_graph(&g).unwrap());
271 }
272
273 #[test]
274 fn bipartite_k23_not_threshold() {
275 let mut g = Graph::with_vertices(5);
277 g.add_edge(0, 2).unwrap();
278 g.add_edge(0, 3).unwrap();
279 g.add_edge(0, 4).unwrap();
280 g.add_edge(1, 2).unwrap();
281 g.add_edge(1, 3).unwrap();
282 g.add_edge(1, 4).unwrap();
283 assert!(!is_threshold_graph(&g).unwrap());
284 }
285
286 #[test]
287 fn k_plus_isolated() {
288 let mut g = Graph::with_vertices(5);
292 g.add_edge(0, 1).unwrap();
293 g.add_edge(1, 2).unwrap();
294 g.add_edge(2, 0).unwrap();
295 assert!(is_threshold_graph(&g).unwrap());
296 }
297}