rust_igraph/algorithms/properties/
forgotten_coindex.rs1#![allow(
10 clippy::cast_possible_truncation,
11 clippy::cast_precision_loss,
12 clippy::many_single_char_names,
13 clippy::needless_range_loop,
14 clippy::too_many_lines
15)]
16
17use crate::core::{Graph, IgraphResult};
18
19pub fn forgotten_coindex(graph: &Graph) -> IgraphResult<u64> {
40 let n = graph.vcount() as usize;
41 if n < 2 {
42 return Ok(0);
43 }
44
45 let mut sum_d2 = 0_u64;
46 let mut f_index = 0_u64;
47
48 for v in 0..n {
49 let d = graph.degree(v as u32)? as u64;
50 sum_d2 = sum_d2.saturating_add(d.saturating_mul(d));
51 }
52
53 for (u, v) in graph.edges() {
54 if u == v {
55 continue;
56 }
57 let du = graph.degree(u)? as u64;
58 let dv = graph.degree(v)? as u64;
59 f_index =
60 f_index.saturating_add(du.saturating_mul(du).saturating_add(dv.saturating_mul(dv)));
61 }
62
63 let n_minus_1 = (n as u64).saturating_sub(1);
64 Ok(n_minus_1.saturating_mul(sum_d2).saturating_sub(f_index))
65}
66
67pub fn first_hyper_zagreb_coindex(graph: &Graph) -> IgraphResult<u64> {
93 let n = graph.vcount() as usize;
94 if n < 2 {
95 return Ok(0);
96 }
97
98 let mut degrees = Vec::with_capacity(n);
99 for v in 0..n {
100 degrees.push(graph.degree(v as u32)? as u64);
101 }
102
103 let mut total = 0_u64;
104 for u in 0..n {
105 for v in (u + 1)..n {
106 let s = degrees[u].saturating_add(degrees[v]);
107 total = total.saturating_add(s.saturating_mul(s));
108 }
109 }
110
111 let mut edge_sum = 0_u64;
112 for (u, v) in graph.edges() {
113 if u == v {
114 continue;
115 }
116 let s = degrees[u as usize].saturating_add(degrees[v as usize]);
117 edge_sum = edge_sum.saturating_add(s.saturating_mul(s));
118 }
119
120 Ok(total.saturating_sub(edge_sum))
121}
122
123pub fn second_hyper_zagreb_coindex(graph: &Graph) -> IgraphResult<u64> {
141 let n = graph.vcount() as usize;
142 if n < 2 {
143 return Ok(0);
144 }
145
146 let mut degrees = Vec::with_capacity(n);
147 for v in 0..n {
148 degrees.push(graph.degree(v as u32)? as u64);
149 }
150
151 let mut total = 0_u64;
152 for u in 0..n {
153 for v in (u + 1)..n {
154 let p = degrees[u].saturating_mul(degrees[v]);
155 total = total.saturating_add(p.saturating_mul(p));
156 }
157 }
158
159 let mut edge_sum = 0_u64;
160 for (u, v) in graph.edges() {
161 if u == v {
162 continue;
163 }
164 let p = degrees[u as usize].saturating_mul(degrees[v as usize]);
165 edge_sum = edge_sum.saturating_add(p.saturating_mul(p));
166 }
167
168 Ok(total.saturating_sub(edge_sum))
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn single_edge() -> Graph {
176 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
177 }
178
179 fn path3() -> Graph {
180 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
181 }
182
183 fn path4() -> Graph {
184 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
185 }
186
187 fn k3() -> Graph {
188 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
189 }
190
191 fn k4() -> Graph {
192 Graph::from_edges(
193 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
194 false,
195 Some(4),
196 )
197 .unwrap()
198 }
199
200 fn cycle4() -> Graph {
201 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
202 }
203
204 fn cycle5() -> Graph {
205 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
206 }
207
208 fn star5() -> Graph {
209 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
210 }
211
212 fn paw() -> Graph {
213 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
214 }
215
216 #[test]
219 fn fco_empty() {
220 let g = Graph::with_vertices(0);
221 assert_eq!(forgotten_coindex(&g).unwrap(), 0);
222 }
223
224 #[test]
225 fn fco_isolated() {
226 let g = Graph::with_vertices(5);
227 assert_eq!(forgotten_coindex(&g).unwrap(), 0);
228 }
229
230 #[test]
231 fn fco_single_edge() {
232 assert_eq!(forgotten_coindex(&single_edge()).unwrap(), 0);
234 }
235
236 #[test]
237 fn fco_k3() {
238 assert_eq!(forgotten_coindex(&k3()).unwrap(), 0);
239 }
240
241 #[test]
242 fn fco_k4() {
243 assert_eq!(forgotten_coindex(&k4()).unwrap(), 0);
244 }
245
246 #[test]
247 fn fco_path3() {
248 assert_eq!(forgotten_coindex(&path3()).unwrap(), 2);
250 }
251
252 #[test]
253 fn fco_path4() {
254 assert_eq!(forgotten_coindex(&path4()).unwrap(), 12);
256 }
257
258 #[test]
259 fn fco_cycle4() {
260 assert_eq!(forgotten_coindex(&cycle4()).unwrap(), 16);
262 }
263
264 #[test]
265 fn fco_cycle5() {
266 assert_eq!(forgotten_coindex(&cycle5()).unwrap(), 40);
269 }
270
271 #[test]
272 fn fco_star5() {
273 assert_eq!(forgotten_coindex(&star5()).unwrap(), 12);
275 }
276
277 #[test]
278 fn fco_paw() {
279 assert_eq!(forgotten_coindex(&paw()).unwrap(), 10);
281 }
282
283 #[test]
286 fn hm1co_empty() {
287 let g = Graph::with_vertices(0);
288 assert_eq!(first_hyper_zagreb_coindex(&g).unwrap(), 0);
289 }
290
291 #[test]
292 fn hm1co_isolated() {
293 let g = Graph::with_vertices(5);
294 assert_eq!(first_hyper_zagreb_coindex(&g).unwrap(), 0);
295 }
296
297 #[test]
298 fn hm1co_single_edge() {
299 assert_eq!(first_hyper_zagreb_coindex(&single_edge()).unwrap(), 0);
300 }
301
302 #[test]
303 fn hm1co_k3() {
304 assert_eq!(first_hyper_zagreb_coindex(&k3()).unwrap(), 0);
305 }
306
307 #[test]
308 fn hm1co_k4() {
309 assert_eq!(first_hyper_zagreb_coindex(&k4()).unwrap(), 0);
310 }
311
312 #[test]
313 fn hm1co_path3() {
314 assert_eq!(first_hyper_zagreb_coindex(&path3()).unwrap(), 4);
316 }
317
318 #[test]
319 fn hm1co_path4() {
320 assert_eq!(first_hyper_zagreb_coindex(&path4()).unwrap(), 22);
322 }
323
324 #[test]
325 fn hm1co_cycle4() {
326 assert_eq!(first_hyper_zagreb_coindex(&cycle4()).unwrap(), 32);
328 }
329
330 #[test]
331 fn hm1co_cycle5() {
332 assert_eq!(first_hyper_zagreb_coindex(&cycle5()).unwrap(), 80);
334 }
335
336 #[test]
337 fn hm1co_star5() {
338 assert_eq!(first_hyper_zagreb_coindex(&star5()).unwrap(), 24);
340 }
341
342 #[test]
343 fn hm1co_paw() {
344 assert_eq!(first_hyper_zagreb_coindex(&paw()).unwrap(), 18);
346 }
347
348 #[test]
351 fn hm2co_empty() {
352 let g = Graph::with_vertices(0);
353 assert_eq!(second_hyper_zagreb_coindex(&g).unwrap(), 0);
354 }
355
356 #[test]
357 fn hm2co_isolated() {
358 let g = Graph::with_vertices(5);
359 assert_eq!(second_hyper_zagreb_coindex(&g).unwrap(), 0);
360 }
361
362 #[test]
363 fn hm2co_single_edge() {
364 assert_eq!(second_hyper_zagreb_coindex(&single_edge()).unwrap(), 0);
365 }
366
367 #[test]
368 fn hm2co_k3() {
369 assert_eq!(second_hyper_zagreb_coindex(&k3()).unwrap(), 0);
370 }
371
372 #[test]
373 fn hm2co_k4() {
374 assert_eq!(second_hyper_zagreb_coindex(&k4()).unwrap(), 0);
375 }
376
377 #[test]
378 fn hm2co_path3() {
379 assert_eq!(second_hyper_zagreb_coindex(&path3()).unwrap(), 1);
381 }
382
383 #[test]
384 fn hm2co_path4() {
385 assert_eq!(second_hyper_zagreb_coindex(&path4()).unwrap(), 9);
387 }
388
389 #[test]
390 fn hm2co_cycle4() {
391 assert_eq!(second_hyper_zagreb_coindex(&cycle4()).unwrap(), 32);
393 }
394
395 #[test]
396 fn hm2co_cycle5() {
397 assert_eq!(second_hyper_zagreb_coindex(&cycle5()).unwrap(), 80);
399 }
400
401 #[test]
402 fn hm2co_star5() {
403 assert_eq!(second_hyper_zagreb_coindex(&star5()).unwrap(), 6);
405 }
406
407 #[test]
408 fn hm2co_paw() {
409 assert_eq!(second_hyper_zagreb_coindex(&paw()).unwrap(), 8);
411 }
412
413 #[test]
416 fn complete_graphs_all_zero() {
417 for g in &[k3(), k4()] {
418 assert_eq!(forgotten_coindex(g).unwrap(), 0);
419 assert_eq!(first_hyper_zagreb_coindex(g).unwrap(), 0);
420 assert_eq!(second_hyper_zagreb_coindex(g).unwrap(), 0);
421 }
422 }
423
424 #[test]
425 fn all_positive_for_incomplete() {
426 for g in &[path3(), path4(), cycle4(), star5(), paw()] {
427 assert!(forgotten_coindex(g).unwrap() > 0);
428 assert!(first_hyper_zagreb_coindex(g).unwrap() > 0);
429 assert!(second_hyper_zagreb_coindex(g).unwrap() > 0);
430 }
431 }
432
433 #[test]
434 fn hm1co_ge_fco() {
435 for g in &[path3(), path4(), cycle4(), cycle5(), star5(), paw()] {
437 let fco = forgotten_coindex(g).unwrap();
438 let hm1co = first_hyper_zagreb_coindex(g).unwrap();
439 assert!(hm1co >= fco);
440 }
441 }
442
443 #[test]
444 fn fco_via_direct_sum() {
445 for g in &[path3(), path4(), cycle4(), star5(), paw()] {
447 let n = g.vcount() as usize;
448 let mut degrees = vec![0_u64; n];
449 for v in 0..n {
450 degrees[v] = g.degree(v as u32).unwrap() as u64;
451 }
452
453 let adj: std::collections::HashSet<(usize, usize)> = g
454 .edges()
455 .flat_map(|(u, v)| {
456 let a = u as usize;
457 let b = v as usize;
458 vec![(a, b), (b, a)]
459 })
460 .collect();
461
462 let mut direct = 0_u64;
463 for u in 0..n {
464 for v in (u + 1)..n {
465 if !adj.contains(&(u, v)) {
466 direct += degrees[u] * degrees[u] + degrees[v] * degrees[v];
467 }
468 }
469 }
470
471 assert_eq!(forgotten_coindex(g).unwrap(), direct);
472 }
473 }
474}