1#![allow(
12 clippy::cast_possible_truncation,
13 clippy::cast_precision_loss,
14 clippy::many_single_char_names,
15 clippy::needless_range_loop,
16 clippy::too_many_lines,
17 clippy::unnecessary_wraps
18)]
19
20use crate::core::{Graph, IgraphResult};
21
22fn second_degrees(graph: &Graph) -> IgraphResult<Vec<u32>> {
23 let n = graph.vcount() as usize;
24 let mut d2 = vec![0_u32; n];
25
26 for s in 0..n {
27 let mut dist = vec![u32::MAX; n];
28 dist[s] = 0;
29 let mut queue = std::collections::VecDeque::new();
30 queue.push_back(s);
31 while let Some(u) = queue.pop_front() {
32 let d_u = dist[u];
33 if d_u >= 2 {
34 continue;
35 }
36 if let Ok(nbs) = graph.neighbors(u as u32) {
37 for nb in nbs {
38 let idx = nb as usize;
39 if dist[idx] == u32::MAX {
40 dist[idx] = d_u + 1;
41 queue.push_back(idx);
42 }
43 }
44 }
45 }
46 let mut count = 0_u32;
47 for &d in &dist {
48 if d == 2 {
49 count += 1;
50 }
51 }
52 d2[s] = count;
53 }
54
55 Ok(d2)
56}
57
58pub fn first_leap_zagreb(graph: &Graph) -> IgraphResult<u64> {
74 let d2 = second_degrees(graph)?;
75 let mut lm1 = 0_u64;
76
77 for &dv in &d2 {
78 let dv64 = u64::from(dv);
79 lm1 = lm1.saturating_add(dv64.saturating_mul(dv64));
80 }
81
82 Ok(lm1)
83}
84
85pub fn second_leap_zagreb(graph: &Graph) -> IgraphResult<u64> {
102 let d2 = second_degrees(graph)?;
103 let mut lm2 = 0_u64;
104
105 for (u, v) in graph.edges() {
106 if u == v {
107 continue;
108 }
109 let du = u64::from(d2[u as usize]);
110 let dv = u64::from(d2[v as usize]);
111 lm2 = lm2.saturating_add(du.saturating_mul(dv));
112 }
113
114 Ok(lm2)
115}
116
117pub fn third_leap_zagreb(graph: &Graph) -> IgraphResult<u64> {
133 let d2 = second_degrees(graph)?;
134 let n = graph.vcount() as usize;
135 let mut lm3 = 0_u64;
136
137 for i in 0..n {
138 let deg = graph.degree(i as u32)? as u64;
139 let d2v = u64::from(d2[i]);
140 lm3 = lm3.saturating_add(deg.saturating_mul(d2v));
141 }
142
143 Ok(lm3)
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 fn single_edge() -> Graph {
151 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
152 }
153
154 fn path3() -> Graph {
155 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
156 }
157
158 fn path4() -> Graph {
159 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
160 }
161
162 fn k3() -> Graph {
163 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
164 }
165
166 fn k4() -> Graph {
167 Graph::from_edges(
168 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
169 false,
170 Some(4),
171 )
172 .unwrap()
173 }
174
175 fn cycle4() -> Graph {
176 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
177 }
178
179 fn cycle5() -> Graph {
180 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
181 }
182
183 fn star5() -> Graph {
184 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
185 }
186
187 fn paw() -> Graph {
188 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
189 }
190
191 fn petersen() -> Graph {
192 Graph::from_edges(
193 &[
194 (0, 1),
195 (1, 2),
196 (2, 3),
197 (3, 4),
198 (4, 0),
199 (0, 5),
200 (1, 6),
201 (2, 7),
202 (3, 8),
203 (4, 9),
204 (5, 7),
205 (5, 8),
206 (6, 8),
207 (6, 9),
208 (7, 9),
209 ],
210 false,
211 Some(10),
212 )
213 .unwrap()
214 }
215
216 fn d2(g: &Graph) -> Vec<u32> {
217 second_degrees(g).unwrap()
218 }
219
220 #[test]
223 fn d2_empty() {
224 let g = Graph::with_vertices(0);
225 assert_eq!(d2(&g), Vec::<u32>::new());
226 }
227
228 #[test]
229 fn d2_isolated() {
230 let g = Graph::with_vertices(3);
231 assert_eq!(d2(&g), vec![0, 0, 0]);
232 }
233
234 #[test]
235 fn d2_single_edge() {
236 assert_eq!(d2(&single_edge()), vec![0, 0]);
238 }
239
240 #[test]
241 fn d2_path3() {
242 assert_eq!(d2(&path3()), vec![1, 0, 1]);
244 }
245
246 #[test]
247 fn d2_path4() {
248 assert_eq!(d2(&path4()), vec![1, 1, 1, 1]);
250 }
251
252 #[test]
253 fn d2_k3() {
254 assert_eq!(d2(&k3()), vec![0, 0, 0]);
256 }
257
258 #[test]
259 fn d2_k4() {
260 assert_eq!(d2(&k4()), vec![0, 0, 0, 0]);
261 }
262
263 #[test]
264 fn d2_cycle4() {
265 assert_eq!(d2(&cycle4()), vec![1, 1, 1, 1]);
267 }
268
269 #[test]
270 fn d2_cycle5() {
271 assert_eq!(d2(&cycle5()), vec![2, 2, 2, 2, 2]);
273 }
274
275 #[test]
276 fn d2_star5() {
277 assert_eq!(d2(&star5()), vec![0, 3, 3, 3, 3]);
280 }
281
282 #[test]
283 fn d2_paw() {
284 assert_eq!(d2(&paw()), vec![1, 1, 0, 2]);
287 }
288
289 #[test]
290 fn d2_petersen() {
291 let d2v = d2(&petersen());
294 for &v in &d2v {
295 assert_eq!(v, 6);
296 }
297 }
298
299 #[test]
302 fn lm1_empty() {
303 let g = Graph::with_vertices(0);
304 assert_eq!(first_leap_zagreb(&g).unwrap(), 0);
305 }
306
307 #[test]
308 fn lm1_isolated() {
309 let g = Graph::with_vertices(5);
310 assert_eq!(first_leap_zagreb(&g).unwrap(), 0);
311 }
312
313 #[test]
314 fn lm1_single_edge() {
315 assert_eq!(first_leap_zagreb(&single_edge()).unwrap(), 0);
316 }
317
318 #[test]
319 fn lm1_path3() {
320 assert_eq!(first_leap_zagreb(&path3()).unwrap(), 2);
322 }
323
324 #[test]
325 fn lm1_path4() {
326 assert_eq!(first_leap_zagreb(&path4()).unwrap(), 4);
328 }
329
330 #[test]
331 fn lm1_k3() {
332 assert_eq!(first_leap_zagreb(&k3()).unwrap(), 0);
333 }
334
335 #[test]
336 fn lm1_k4() {
337 assert_eq!(first_leap_zagreb(&k4()).unwrap(), 0);
338 }
339
340 #[test]
341 fn lm1_cycle4() {
342 assert_eq!(first_leap_zagreb(&cycle4()).unwrap(), 4);
344 }
345
346 #[test]
347 fn lm1_cycle5() {
348 assert_eq!(first_leap_zagreb(&cycle5()).unwrap(), 20);
350 }
351
352 #[test]
353 fn lm1_star5() {
354 assert_eq!(first_leap_zagreb(&star5()).unwrap(), 36);
356 }
357
358 #[test]
359 fn lm1_paw() {
360 assert_eq!(first_leap_zagreb(&paw()).unwrap(), 6);
362 }
363
364 #[test]
365 fn lm1_petersen() {
366 assert_eq!(first_leap_zagreb(&petersen()).unwrap(), 360);
368 }
369
370 #[test]
371 fn lm1_complete_is_zero() {
372 for g in &[k3(), k4()] {
374 assert_eq!(first_leap_zagreb(g).unwrap(), 0);
375 }
376 }
377
378 #[test]
381 fn lm2_empty() {
382 let g = Graph::with_vertices(0);
383 assert_eq!(second_leap_zagreb(&g).unwrap(), 0);
384 }
385
386 #[test]
387 fn lm2_single_edge() {
388 assert_eq!(second_leap_zagreb(&single_edge()).unwrap(), 0);
389 }
390
391 #[test]
392 fn lm2_path3() {
393 assert_eq!(second_leap_zagreb(&path3()).unwrap(), 0);
395 }
396
397 #[test]
398 fn lm2_path4() {
399 assert_eq!(second_leap_zagreb(&path4()).unwrap(), 3);
401 }
402
403 #[test]
404 fn lm2_k3() {
405 assert_eq!(second_leap_zagreb(&k3()).unwrap(), 0);
406 }
407
408 #[test]
409 fn lm2_k4() {
410 assert_eq!(second_leap_zagreb(&k4()).unwrap(), 0);
411 }
412
413 #[test]
414 fn lm2_cycle4() {
415 assert_eq!(second_leap_zagreb(&cycle4()).unwrap(), 4);
417 }
418
419 #[test]
420 fn lm2_cycle5() {
421 assert_eq!(second_leap_zagreb(&cycle5()).unwrap(), 20);
423 }
424
425 #[test]
426 fn lm2_star5() {
427 assert_eq!(second_leap_zagreb(&star5()).unwrap(), 0);
429 }
430
431 #[test]
432 fn lm2_paw() {
433 assert_eq!(second_leap_zagreb(&paw()).unwrap(), 1);
435 }
436
437 #[test]
438 fn lm2_petersen() {
439 assert_eq!(second_leap_zagreb(&petersen()).unwrap(), 540);
441 }
442
443 #[test]
446 fn lm3_empty() {
447 let g = Graph::with_vertices(0);
448 assert_eq!(third_leap_zagreb(&g).unwrap(), 0);
449 }
450
451 #[test]
452 fn lm3_isolated() {
453 let g = Graph::with_vertices(5);
454 assert_eq!(third_leap_zagreb(&g).unwrap(), 0);
455 }
456
457 #[test]
458 fn lm3_single_edge() {
459 assert_eq!(third_leap_zagreb(&single_edge()).unwrap(), 0);
461 }
462
463 #[test]
464 fn lm3_path3() {
465 assert_eq!(third_leap_zagreb(&path3()).unwrap(), 2);
467 }
468
469 #[test]
470 fn lm3_path4() {
471 assert_eq!(third_leap_zagreb(&path4()).unwrap(), 6);
473 }
474
475 #[test]
476 fn lm3_k3() {
477 assert_eq!(third_leap_zagreb(&k3()).unwrap(), 0);
478 }
479
480 #[test]
481 fn lm3_k4() {
482 assert_eq!(third_leap_zagreb(&k4()).unwrap(), 0);
483 }
484
485 #[test]
486 fn lm3_cycle4() {
487 assert_eq!(third_leap_zagreb(&cycle4()).unwrap(), 8);
489 }
490
491 #[test]
492 fn lm3_cycle5() {
493 assert_eq!(third_leap_zagreb(&cycle5()).unwrap(), 20);
495 }
496
497 #[test]
498 fn lm3_star5() {
499 assert_eq!(third_leap_zagreb(&star5()).unwrap(), 12);
501 }
502
503 #[test]
504 fn lm3_paw() {
505 assert_eq!(third_leap_zagreb(&paw()).unwrap(), 6);
507 }
508
509 #[test]
510 fn lm3_petersen() {
511 assert_eq!(third_leap_zagreb(&petersen()).unwrap(), 180);
513 }
514
515 #[test]
518 fn lm3_equals_sum_of_d_times_d2() {
519 for g in &[path3(), path4(), cycle4(), cycle5(), star5(), paw()] {
521 let d2v = d2(g);
522 let n = g.vcount() as usize;
523 let mut expected = 0_u64;
524 for i in 0..n {
525 let deg = g.degree(i as u32).unwrap() as u64;
526 expected += deg * u64::from(d2v[i]);
527 }
528 assert_eq!(third_leap_zagreb(g).unwrap(), expected);
529 }
530 }
531
532 #[test]
533 fn all_complete_graphs_have_zero_leap_zagreb() {
534 for g in &[k3(), k4()] {
535 assert_eq!(first_leap_zagreb(g).unwrap(), 0);
536 assert_eq!(second_leap_zagreb(g).unwrap(), 0);
537 assert_eq!(third_leap_zagreb(g).unwrap(), 0);
538 }
539 }
540
541 #[test]
542 fn all_positive_for_nonclique_connected() {
543 for g in &[path3(), path4(), cycle4(), cycle5(), star5(), paw()] {
546 assert!(first_leap_zagreb(g).unwrap() > 0);
547 assert!(third_leap_zagreb(g).unwrap() > 0);
548 }
549 }
550}