1#![allow(
14 clippy::cast_possible_truncation,
15 clippy::cast_precision_loss,
16 clippy::many_single_char_names,
17 clippy::needless_range_loop,
18 clippy::too_many_lines
19)]
20
21use crate::core::{Graph, IgraphResult};
22
23pub fn platt_index(graph: &Graph) -> IgraphResult<u64> {
40 let mut f = 0_u64;
41
42 for (u, v) in graph.edges() {
43 if u == v {
44 continue;
45 }
46 let du = graph.degree(u)? as u64;
47 let dv = graph.degree(v)? as u64;
48 f = f.saturating_add(du + dv - 2);
49 }
50
51 Ok(f)
52}
53
54pub fn gordon_scantlebury_index(graph: &Graph) -> IgraphResult<u64> {
73 let mut gs = 0_u64;
74
75 let n = graph.vcount();
76 for v in 0..n {
77 let d = graph.degree(v)? as u64;
78 if d >= 2 {
79 gs = gs.saturating_add(d * (d - 1) / 2);
80 }
81 }
82
83 Ok(gs)
84}
85
86pub fn bertz_complexity_index(graph: &Graph) -> IgraphResult<u64> {
103 let mut b = 0_u64;
104
105 for (u, v) in graph.edges() {
106 if u == v {
107 continue;
108 }
109 let du = graph.degree(u)? as u64;
110 let dv = graph.degree(v)? as u64;
111 let edge_deg = du + dv - 2;
112 if edge_deg >= 2 {
113 b = b.saturating_add(edge_deg * (edge_deg - 1) / 2);
114 }
115 }
116
117 Ok(b)
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 fn single_edge() -> Graph {
125 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
126 }
127
128 fn path3() -> Graph {
129 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
130 }
131
132 fn path4() -> Graph {
133 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
134 }
135
136 fn path5() -> Graph {
137 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], false, Some(5)).unwrap()
138 }
139
140 fn k3() -> Graph {
141 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
142 }
143
144 fn k4() -> Graph {
145 Graph::from_edges(
146 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
147 false,
148 Some(4),
149 )
150 .unwrap()
151 }
152
153 fn cycle4() -> Graph {
154 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
155 }
156
157 fn cycle5() -> Graph {
158 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
159 }
160
161 fn star5() -> Graph {
162 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
163 }
164
165 fn paw() -> Graph {
166 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
167 }
168
169 fn diamond() -> Graph {
170 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)], false, Some(4)).unwrap()
171 }
172
173 #[test]
176 fn platt_empty() {
177 let g = Graph::with_vertices(0);
178 assert_eq!(platt_index(&g).unwrap(), 0);
179 }
180
181 #[test]
182 fn platt_isolated() {
183 let g = Graph::with_vertices(5);
184 assert_eq!(platt_index(&g).unwrap(), 0);
185 }
186
187 #[test]
188 fn platt_single_edge() {
189 assert_eq!(platt_index(&single_edge()).unwrap(), 0);
191 }
192
193 #[test]
194 fn platt_path3() {
195 assert_eq!(platt_index(&path3()).unwrap(), 2);
197 }
198
199 #[test]
200 fn platt_path4() {
201 assert_eq!(platt_index(&path4()).unwrap(), 4);
203 }
204
205 #[test]
206 fn platt_path5() {
207 assert_eq!(platt_index(&path5()).unwrap(), 6);
209 }
210
211 #[test]
212 fn platt_k3() {
213 assert_eq!(platt_index(&k3()).unwrap(), 6);
215 }
216
217 #[test]
218 fn platt_k4() {
219 assert_eq!(platt_index(&k4()).unwrap(), 24);
221 }
222
223 #[test]
224 fn platt_cycle4() {
225 assert_eq!(platt_index(&cycle4()).unwrap(), 8);
227 }
228
229 #[test]
230 fn platt_cycle5() {
231 assert_eq!(platt_index(&cycle5()).unwrap(), 10);
233 }
234
235 #[test]
236 fn platt_star5() {
237 assert_eq!(platt_index(&star5()).unwrap(), 12);
239 }
240
241 #[test]
242 fn platt_paw() {
243 assert_eq!(platt_index(&paw()).unwrap(), 10);
247 }
248
249 #[test]
250 fn platt_diamond() {
251 assert_eq!(platt_index(&diamond()).unwrap(), 16);
255 }
256
257 #[test]
258 fn platt_equals_first_zagreb_minus_2m() {
259 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
261 let m = g.ecount() as u64;
262 let mut m1 = 0_u64;
263 for v in 0..g.vcount() {
264 let d = g.degree(v).unwrap() as u64;
265 m1 += d * d;
266 }
267 assert_eq!(platt_index(g).unwrap(), m1 - 2 * m);
268 }
269 }
270
271 #[test]
272 fn platt_regular_formula() {
273 for g in &[k3(), k4(), cycle4(), cycle5()] {
275 let m = g.ecount() as u64;
276 let r = g.degree(0).unwrap() as u64;
277 assert_eq!(platt_index(g).unwrap(), 2 * m * (r - 1));
278 }
279 }
280
281 #[test]
284 fn gs_empty() {
285 let g = Graph::with_vertices(0);
286 assert_eq!(gordon_scantlebury_index(&g).unwrap(), 0);
287 }
288
289 #[test]
290 fn gs_isolated() {
291 let g = Graph::with_vertices(5);
292 assert_eq!(gordon_scantlebury_index(&g).unwrap(), 0);
293 }
294
295 #[test]
296 fn gs_single_edge() {
297 assert_eq!(gordon_scantlebury_index(&single_edge()).unwrap(), 0);
299 }
300
301 #[test]
302 fn gs_path3() {
303 assert_eq!(gordon_scantlebury_index(&path3()).unwrap(), 1);
305 }
306
307 #[test]
308 fn gs_path4() {
309 assert_eq!(gordon_scantlebury_index(&path4()).unwrap(), 2);
311 }
312
313 #[test]
314 fn gs_path5() {
315 assert_eq!(gordon_scantlebury_index(&path5()).unwrap(), 3);
317 }
318
319 #[test]
320 fn gs_k3() {
321 assert_eq!(gordon_scantlebury_index(&k3()).unwrap(), 3);
323 }
324
325 #[test]
326 fn gs_k4() {
327 assert_eq!(gordon_scantlebury_index(&k4()).unwrap(), 12);
329 }
330
331 #[test]
332 fn gs_cycle4() {
333 assert_eq!(gordon_scantlebury_index(&cycle4()).unwrap(), 4);
335 }
336
337 #[test]
338 fn gs_cycle5() {
339 assert_eq!(gordon_scantlebury_index(&cycle5()).unwrap(), 5);
341 }
342
343 #[test]
344 fn gs_star5() {
345 assert_eq!(gordon_scantlebury_index(&star5()).unwrap(), 6);
347 }
348
349 #[test]
350 fn gs_paw() {
351 assert_eq!(gordon_scantlebury_index(&paw()).unwrap(), 5);
353 }
354
355 #[test]
356 fn gs_diamond() {
357 assert_eq!(gordon_scantlebury_index(&diamond()).unwrap(), 8);
359 }
360
361 #[test]
362 fn gs_equals_platt_half() {
363 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
365 assert_eq!(
366 gordon_scantlebury_index(g).unwrap(),
367 platt_index(g).unwrap() / 2
368 );
369 }
370 }
371
372 #[test]
373 fn gs_regular_formula() {
374 for g in &[k3(), k4(), cycle4(), cycle5()] {
376 let n = u64::from(g.vcount());
377 let r = g.degree(0).unwrap() as u64;
378 assert_eq!(gordon_scantlebury_index(g).unwrap(), n * r * (r - 1) / 2);
379 }
380 }
381
382 #[test]
385 fn bertz_empty() {
386 let g = Graph::with_vertices(0);
387 assert_eq!(bertz_complexity_index(&g).unwrap(), 0);
388 }
389
390 #[test]
391 fn bertz_isolated() {
392 let g = Graph::with_vertices(5);
393 assert_eq!(bertz_complexity_index(&g).unwrap(), 0);
394 }
395
396 #[test]
397 fn bertz_single_edge() {
398 assert_eq!(bertz_complexity_index(&single_edge()).unwrap(), 0);
400 }
401
402 #[test]
403 fn bertz_path3() {
404 assert_eq!(bertz_complexity_index(&path3()).unwrap(), 0);
406 }
407
408 #[test]
409 fn bertz_path4() {
410 assert_eq!(bertz_complexity_index(&path4()).unwrap(), 1);
412 }
413
414 #[test]
415 fn bertz_path5() {
416 assert_eq!(bertz_complexity_index(&path5()).unwrap(), 2);
418 }
419
420 #[test]
421 fn bertz_k3() {
422 assert_eq!(bertz_complexity_index(&k3()).unwrap(), 3);
424 }
425
426 #[test]
427 fn bertz_k4() {
428 assert_eq!(bertz_complexity_index(&k4()).unwrap(), 36);
430 }
431
432 #[test]
433 fn bertz_cycle4() {
434 assert_eq!(bertz_complexity_index(&cycle4()).unwrap(), 4);
436 }
437
438 #[test]
439 fn bertz_cycle5() {
440 assert_eq!(bertz_complexity_index(&cycle5()).unwrap(), 5);
442 }
443
444 #[test]
445 fn bertz_star5() {
446 assert_eq!(bertz_complexity_index(&star5()).unwrap(), 12);
448 }
449
450 #[test]
451 fn bertz_paw() {
452 assert_eq!(bertz_complexity_index(&paw()).unwrap(), 8);
454 }
455
456 #[test]
457 fn bertz_diamond() {
458 assert_eq!(bertz_complexity_index(&diamond()).unwrap(), 18);
461 }
462
463 #[test]
464 fn bertz_regular_formula() {
465 for g in &[k3(), k4(), cycle4(), cycle5()] {
467 let m = g.ecount() as u64;
468 let r = g.degree(0).unwrap() as u64;
469 let ed = 2 * r - 2;
470 assert_eq!(bertz_complexity_index(g).unwrap(), m * ed * (ed - 1) / 2);
471 }
472 }
473
474 #[test]
477 fn platt_geq_zero() {
478 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
479 let _ = platt_index(g).unwrap();
480 }
481 }
482
483 #[test]
484 fn gs_leq_platt() {
485 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
486 assert!(gordon_scantlebury_index(g).unwrap() <= platt_index(g).unwrap());
487 }
488 }
489
490 #[test]
491 fn bertz_leq_gs_squared_approx() {
492 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
494 let _ = bertz_complexity_index(g).unwrap();
495 }
496 }
497}