1use std::collections::{HashMap, HashSet};
3use std::fmt;
4
5#[cfg(target_arch = "wasm32")]
6mod wasm;
7
8#[cfg(target_arch = "wasm32")]
9pub use wasm::*;
10
11#[derive(Clone)]
13pub struct Graph {
14 edges: HashMap<usize, HashSet<usize>>,
16 n_vertices: usize,
18 n_edges: usize,
20}
21
22impl fmt::Debug for Graph {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 writeln!(f, "Graph {{")?;
25 writeln!(f, " vertices: {},", self.n_vertices)?;
26 writeln!(f, " edges: {},", self.n_edges)?;
27 writeln!(f, " adjacency list: {{")?;
28 for v in 0..self.n_vertices {
29 let neighbors: Vec<usize> = self.edges.get(&v).unwrap_or(&HashSet::new()).iter().cloned().collect();
30 writeln!(f, " {}: {:?},", v, neighbors)?;
31 }
32 writeln!(f, " }}")?;
33 write!(f, "}}")
34 }
35}
36
37impl Graph {
38 pub fn new(n: usize) -> Self {
40 let mut edges = HashMap::new();
41 for i in 0..n {
42 edges.insert(i, HashSet::new());
43 }
44
45 Graph {
46 edges,
47 n_vertices: n,
48 n_edges: 0,
49 }
50 }
51
52 pub fn add_edge(&mut self, u: usize, v: usize) -> Result<(), &'static str> {
54 if u >= self.n_vertices || v >= self.n_vertices {
55 return Err("Vertex index out of bounds");
56 }
57
58 if u == v {
59 return Err("Self-loops are not allowed");
60 }
61
62 if self.edges.get(&u).unwrap().contains(&v) {
64 return Ok(()); }
66
67 self.edges.get_mut(&u).unwrap().insert(v);
69 self.edges.get_mut(&v).unwrap().insert(u);
70 self.n_edges += 1;
71
72 Ok(())
73 }
74
75 pub fn degree(&self, v: usize) -> Result<usize, &'static str> {
77 if v >= self.n_vertices {
78 return Err("Vertex index out of bounds");
79 }
80
81 Ok(self.edges.get(&v).unwrap().len())
82 }
83
84 pub fn first_zagreb_index(&self) -> usize {
86 let mut sum = 0;
87
88 for v in 0..self.n_vertices {
89 let deg = self.edges.get(&v).unwrap().len();
90 sum += deg * deg;
91 }
92
93 sum
94 }
95
96 pub fn min_degree(&self) -> usize {
98 (0..self.n_vertices)
99 .map(|v| self.edges.get(&v).unwrap().len())
100 .min()
101 .unwrap_or(0)
102 }
103
104 pub fn max_degree(&self) -> usize {
106 (0..self.n_vertices)
107 .map(|v| self.edges.get(&v).unwrap().len())
108 .max()
109 .unwrap_or(0)
110 }
111
112 fn is_petersen(&self) -> bool {
114 if self.n_vertices != 10 || self.n_edges != 15 {
116 return false;
117 }
118
119 if self.min_degree() != 3 || self.max_degree() != 3 {
121 return false;
122 }
123
124 let mut has_triangle = false;
127 let mut has_square = false;
128
129 for u in 0..self.n_vertices {
131 let neighbors_u: Vec<usize> = self.edges.get(&u).unwrap().iter().cloned().collect();
132 for &v in &neighbors_u {
133 for &w in &neighbors_u {
134 if v != w && self.edges.get(&v).unwrap().contains(&w) {
135 has_triangle = true;
136 break;
137 }
138 }
139 if has_triangle {
140 break;
141 }
142 }
143 if has_triangle {
144 break;
145 }
146 }
147
148 if !has_triangle {
150 'outer: for u in 0..self.n_vertices {
151 let neighbors_u: Vec<usize> = self.edges.get(&u).unwrap().iter().cloned().collect();
152 for &v in &neighbors_u {
153 let neighbors_v: Vec<usize> =
154 self.edges.get(&v).unwrap().iter().cloned().collect();
155 for &w in &neighbors_v {
156 if w != u {
157 let neighbors_w: Vec<usize> =
158 self.edges.get(&w).unwrap().iter().cloned().collect();
159 for &x in &neighbors_w {
160 if x != v && x != u && self.edges.get(&x).unwrap().contains(&u) {
161 has_square = true;
162 break 'outer;
163 }
164 }
165 }
166 }
167 }
168 }
169 }
170
171 !has_triangle && !has_square
173 }
174
175 pub fn is_k_connected(&self, k: usize, use_exact: bool) -> bool {
186 if self.is_complete() {
188 return k <= self.n_vertices - 1;
189 }
190
191 if use_exact {
192 self.is_k_connected_exact(k)
193 } else {
194 self.is_k_connected_approx(k)
195 }
196 }
197
198 pub fn is_k_connected_approx(&self, k: usize) -> bool {
201 if k > self.n_vertices - 1 {
203 return false;
204 }
205
206 if self.min_degree() < k {
208 return false;
209 }
210
211 if k == 1 {
213 return self.is_connected();
214 }
215
216 if self.is_complete() {
218 return k <= self.n_vertices - 1;
219 }
220
221 if self.is_cycle() {
223 return k <= 2;
224 }
225
226 if self.is_path() {
228 return k <= 1;
229 }
230
231 if self.is_star() {
233 return k <= 1;
234 }
235
236 let density_threshold = (self.n_vertices - 1) * k / 2 + 1;
239
240 if self.n_edges >= density_threshold {
241 return true;
242 }
243
244 let avg_degree = 2.0 * self.n_edges as f64 / self.n_vertices as f64;
247 let z1 = self.first_zagreb_index();
248
249 z1 as f64 / self.n_edges as f64 >= k as f64 * avg_degree
251 }
252
253 pub fn is_k_connected_exact(&self, k: usize) -> bool {
256 if k > self.n_vertices - 1 {
258 return false;
259 }
260
261 if self.min_degree() < k {
263 return false;
264 }
265
266 if self.is_complete() {
268 return k <= self.n_vertices - 1;
269 }
270
271 if k == 1 {
273 return self.is_connected();
274 }
275
276 self.mengers_theorem_check(k)
278 }
279
280 fn mengers_theorem_check(&self, k: usize) -> bool {
284 if self.n_vertices <= k {
286 return false; }
288
289 if self.min_degree() < k {
291 return false;
292 }
293
294 if k == 1 {
296 return self.is_connected();
297 }
298
299 if self.is_cycle() {
301 return k <= 2; }
303
304 if self.is_complete() {
305 return k <= self.n_vertices - 1; }
307
308 for s in 0..self.n_vertices {
310 for t in (s + 1)..self.n_vertices {
311 let disjoint_paths = self.find_vertex_disjoint_paths(s, t);
312 if disjoint_paths < k {
313 return false;
314 }
315 }
316 }
317
318 true
319 }
320
321 fn is_connected(&self) -> bool {
323 if self.n_vertices == 0 {
324 return true;
325 }
326
327 use std::collections::{HashSet, VecDeque};
328
329 let mut visited = HashSet::new();
330 let mut queue = VecDeque::new();
331
332 visited.insert(0);
334 queue.push_back(0);
335
336 while let Some(v) = queue.pop_front() {
337 for &neighbor in self.edges.get(&v).unwrap() {
338 if !visited.contains(&neighbor) {
339 visited.insert(neighbor);
340 queue.push_back(neighbor);
341 }
342 }
343 }
344
345 visited.len() == self.n_vertices
347 }
348
349 fn find_vertex_disjoint_paths(&self, s: usize, t: usize) -> usize {
352 use std::collections::{HashMap, HashSet};
353
354 if self.is_complete() {
357 return self.n_vertices - 1;
358 }
359
360 if self.is_cycle() {
362 return 2;
363 }
364
365 if self.is_path()
367 && ((s == 0 && t == self.n_vertices - 1) || (t == 0 && s == self.n_vertices - 1))
368 {
369 return 1;
370 }
371
372 if self.edges.get(&s).unwrap().contains(&t) {
374 let s_neighbors: HashSet<_> = self.edges.get(&s).unwrap().iter().cloned().collect();
376 let t_neighbors: HashSet<_> = self.edges.get(&t).unwrap().iter().cloned().collect();
377
378 let mut common = s_neighbors
380 .intersection(&t_neighbors)
381 .cloned()
382 .collect::<HashSet<_>>();
383 common.remove(&s);
384 common.remove(&t);
385
386 let mut modified_edges = HashMap::new();
391 for (vertex, neighbors) in &self.edges {
392 let mut new_neighbors = neighbors.clone();
393 if *vertex == s {
394 new_neighbors.remove(&t);
395 } else if *vertex == t {
396 new_neighbors.remove(&s);
397 }
398 modified_edges.insert(*vertex, new_neighbors);
399 }
400
401 let mut path_count = 0;
403 let mut working_edges = modified_edges.clone();
404
405 let max_possible_paths = std::cmp::min(
407 self.edges.get(&s).unwrap().len(),
408 self.edges.get(&t).unwrap().len(),
409 );
410
411 let max_attempts = 100;
413 let mut attempts = 0;
414
415 while let Some(path) = self.find_path_in_subgraph(&working_edges, s, t) {
417 path_count += 1;
418
419 if path_count >= max_possible_paths - 1 || attempts >= max_attempts {
421 break;
422 }
423
424 attempts += 1;
425
426 for &v in path.iter().skip(1).take(path.len() - 2) {
428 if let Some(neighbors) = working_edges.get(&v) {
430 let neighbors_copy: Vec<usize> = neighbors.iter().cloned().collect();
431
432 for &neighbor in &neighbors_copy {
434 if let Some(edges) = working_edges.get_mut(&v) {
435 edges.remove(&neighbor);
436 }
437 if let Some(edges) = working_edges.get_mut(&neighbor) {
438 edges.remove(&v);
439 }
440 }
441 }
442 }
443 }
444
445 return 1 + path_count;
447 }
448
449 let mut working_edges = HashMap::new();
452 for (vertex, neighbors) in &self.edges {
453 working_edges.insert(*vertex, neighbors.clone());
454 }
455
456 let mut path_count = 0;
457
458 let max_possible_paths = std::cmp::min(
460 self.edges.get(&s).unwrap().len(),
461 self.edges.get(&t).unwrap().len(),
462 );
463
464 let max_attempts = 100;
466 let mut attempts = 0;
467
468 while let Some(path) = self.find_path_in_subgraph(&working_edges, s, t) {
470 path_count += 1;
471
472 if path_count >= max_possible_paths || attempts >= max_attempts {
474 break;
475 }
476
477 attempts += 1;
478
479 for &v in path.iter().skip(1).take(path.len() - 2) {
481 if let Some(neighbors) = working_edges.get(&v) {
483 let neighbors_copy: Vec<usize> = neighbors.iter().cloned().collect();
484
485 for &neighbor in &neighbors_copy {
487 if let Some(edges) = working_edges.get_mut(&v) {
488 edges.remove(&neighbor);
489 }
490 if let Some(edges) = working_edges.get_mut(&neighbor) {
491 edges.remove(&v);
492 }
493 }
494 }
495 }
496 }
497
498 path_count
499 }
500
501 fn find_path_in_subgraph(
503 &self,
504 edges: &HashMap<usize, HashSet<usize>>,
505 s: usize,
506 t: usize,
507 ) -> Option<Vec<usize>> {
508 use std::collections::{HashMap, HashSet, VecDeque};
509
510 let mut visited = HashSet::new();
511 let mut queue = VecDeque::new();
512 let mut parent = HashMap::new();
513
514 visited.insert(s);
515 queue.push_back(s);
516
517 while let Some(u) = queue.pop_front() {
518 if u == t {
519 let mut path = Vec::new();
521 let mut current = t;
522
523 path.push(current);
524 while current != s {
525 current = *parent.get(¤t).unwrap();
526 path.push(current);
527 }
528
529 path.reverse();
530 return Some(path);
531 }
532
533 for &v in edges.get(&u).unwrap() {
534 if !visited.contains(&v) {
535 visited.insert(v);
536 parent.insert(v, u);
537 queue.push_back(v);
538 }
539 }
540 }
541
542 None
543 }
544
545 fn find_path(&self, s: usize, t: usize) -> Option<Vec<usize>> {
548 self.find_path_in_subgraph(&self.edges, s, t)
549 }
550
551 fn is_path_between(&self, s: usize, t: usize) -> bool {
553 self.find_path(s, t).is_some()
554 }
555
556 pub fn independence_number_approx(&self) -> usize {
559 let mut independent_set = HashSet::new();
560 let mut remaining_vertices: HashSet<usize> = (0..self.n_vertices).collect();
561
562 while !remaining_vertices.is_empty() {
563 let min_degree_vertex = *remaining_vertices
565 .iter()
566 .min_by_key(|&&v| {
567 self.edges
568 .get(&v)
569 .unwrap()
570 .iter()
571 .filter(|&&u| remaining_vertices.contains(&u))
572 .count()
573 })
574 .unwrap();
575
576 independent_set.insert(min_degree_vertex);
578
579 remaining_vertices.remove(&min_degree_vertex);
581 for &neighbor in self.edges.get(&min_degree_vertex).unwrap() {
582 remaining_vertices.remove(&neighbor);
583 }
584 }
585
586 independent_set.len()
587 }
588
589 pub fn is_likely_hamiltonian(&self, use_exact_connectivity: bool) -> bool {
595 if self.n_vertices < 3 {
597 return false;
598 }
599
600 if self.is_complete() {
602 return true;
603 }
604
605 if self.is_cycle() {
607 return true;
608 }
609
610 if self.is_star() && self.n_vertices > 3 {
612 return false;
613 }
614
615 if self.is_petersen() {
617 return false;
618 }
619
620 let k = 2;
622 if !self.is_k_connected(k, use_exact_connectivity) {
623 return false;
624 }
625
626 if self.min_degree() >= self.n_vertices / 2 {
628 return true;
629 }
630
631 let delta = self.min_degree();
632 let delta_max = self.max_degree();
633 let n = self.n_vertices;
634 let e = self.n_edges;
635 let z1 = self.first_zagreb_index();
636
637 let part1 = (n - k - 1) * delta_max * delta_max;
639 let part2 = (e * e) / (k + 1);
640 let part3 = ((n - k - 1) as f64).sqrt() - (delta as f64).sqrt();
641 let part3_squared = part3 * part3;
642 let threshold = part1 + part2 + (part3_squared * e as f64) as usize;
643
644 z1 >= threshold
645 }
646
647 pub fn is_likely_traceable(&self, use_exact_connectivity: bool) -> bool {
653 if self.n_vertices < 2 {
655 return false;
656 }
657
658 if self.is_likely_hamiltonian(use_exact_connectivity) {
660 return true;
661 }
662
663 if self.is_complete() {
665 return true;
666 }
667
668 if self.is_path() {
670 return true;
671 }
672
673 if self.is_star() {
675 return true;
676 }
677
678 if self.is_petersen() {
680 return true;
681 }
682
683 let k = 1;
685 if !self.is_k_connected(k, use_exact_connectivity) {
686 return false;
687 }
688
689 if self.min_degree() >= (self.n_vertices - 1) / 2 {
691 return true;
692 }
693
694 if self.n_vertices < 9 {
696 return self.min_degree() >= (self.n_vertices - 1) / 2;
698 }
699
700 let delta = self.min_degree();
701 let delta_max = self.max_degree();
702 let n = self.n_vertices;
703 let e = self.n_edges;
704 let z1 = self.first_zagreb_index();
705
706 let part1 = (n - k - 2) * delta_max * delta_max;
708 let part2 = (e * e) / (k + 2);
709 let part3 = ((n - k - 2) as f64).sqrt() - (delta as f64).sqrt();
710 let part3_squared = part3 * part3;
711 let threshold = part1 + part2 + (part3_squared * e as f64) as usize;
712
713 z1 >= threshold
714 }
715
716 fn is_complete(&self) -> bool {
718 if self.n_vertices <= 1 {
720 return true; }
722
723 let expected_degree = self.n_vertices - 1;
725
726 for v in 0..self.n_vertices {
727 if self.edges.get(&v).unwrap().len() != expected_degree {
728 return false;
729 }
730 }
731
732 let expected_edge_count = self.n_vertices * (self.n_vertices - 1) / 2;
734 if self.n_edges != expected_edge_count {
735 return false;
736 }
737
738 true
739 }
740
741 fn is_cycle(&self) -> bool {
743 self.min_degree() == 2 && self.max_degree() == 2 && self.n_edges == self.n_vertices
745 }
746
747 fn is_star(&self) -> bool {
749 if self.n_vertices <= 1 {
750 return false;
751 }
752
753 let degree_one_count = (0..self.n_vertices)
755 .filter(|&v| self.edges.get(&v).unwrap().len() == 1)
756 .count();
757
758 let degree_n_minus_1_count = (0..self.n_vertices)
760 .filter(|&v| self.edges.get(&v).unwrap().len() == self.n_vertices - 1)
761 .count();
762
763 degree_one_count == self.n_vertices - 1 && degree_n_minus_1_count == 1
765 }
766
767 fn is_path(&self) -> bool {
769 if self.n_edges != self.n_vertices - 1 {
771 return false;
772 }
773
774 let degree_one_count = (0..self.n_vertices)
776 .filter(|&v| self.edges.get(&v).unwrap().len() == 1)
777 .count();
778
779 let degree_two_count = (0..self.n_vertices)
780 .filter(|&v| self.edges.get(&v).unwrap().len() == 2)
781 .count();
782
783 degree_one_count == 2 && degree_two_count == self.n_vertices - 2
784 }
785
786 pub fn zagreb_upper_bound(&self) -> f64 {
788 let beta = self.independence_number_approx();
789 let delta = self.min_degree();
790 let n = self.n_vertices;
791 let e = self.n_edges;
792 let delta_max = self.max_degree();
793
794 let part1 = (n - beta) * delta_max * delta_max;
796 let part2 = (e * e) as f64 / beta as f64;
797 let part3 = ((n - beta) as f64).sqrt() - (delta as f64).sqrt();
798 let part3_squared = part3 * part3;
799
800 part1 as f64 + part2 + part3_squared * e as f64
801 }
802
803 pub fn vertex_count(&self) -> usize {
805 self.n_vertices
806 }
807
808 pub fn edge_count(&self) -> usize {
810 self.n_edges
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use rand::thread_rng;
817 use super::*;
818
819 #[test]
820 fn test_k_connectivity_exact_vs_approx() {
821 let mut complete = Graph::new(6);
825 for i in 0..5 {
826 for j in (i + 1)..6 {
827 complete.add_edge(i, j).unwrap();
828 }
829 }
830
831 assert!(
833 complete.is_complete(),
834 "Complete graph detection should work"
835 );
836
837 for k in 1..=5 {
838 assert_eq!(
839 complete.is_k_connected_exact(k),
840 true,
841 "Complete graph (n=6) should be {}-connected with exact algorithm",
842 k
843 );
844
845 assert_eq!(
846 complete.is_k_connected_approx(k),
847 true,
848 "Complete graph (n=6) should be {}-connected with approximate algorithm",
849 k
850 );
851
852 assert_eq!(
854 complete.is_k_connected(k, true),
855 true,
856 "Complete graph (n=6) should be {}-connected with wrapper (exact)",
857 k
858 );
859
860 assert_eq!(
861 complete.is_k_connected(k, false),
862 true,
863 "Complete graph (n=6) should be {}-connected with wrapper (approx)",
864 k
865 );
866 }
867
868 assert_eq!(
871 complete.is_k_connected(6, false),
872 false,
873 "Complete graph (n=6) should not be 6-connected with wrapper (approx)"
874 );
875
876 assert_eq!(
878 complete.is_k_connected_approx(6),
879 false,
880 "Complete graph (n=6) should not be 6-connected with approximate algorithm"
881 );
882
883 assert_eq!(
884 complete.is_k_connected_exact(6),
885 false,
886 "Complete graph (n=6) should not be 6-connected with exact algorithm"
887 );
888
889 let mut cycle = Graph::new(5);
891 cycle.add_edge(0, 1).unwrap();
892 cycle.add_edge(1, 2).unwrap();
893 cycle.add_edge(2, 3).unwrap();
894 cycle.add_edge(3, 4).unwrap();
895 cycle.add_edge(4, 0).unwrap();
896
897 assert_eq!(
898 cycle.is_k_connected_exact(1),
899 true,
900 "Cycle graph should be 1-connected with exact algorithm"
901 );
902
903 assert_eq!(
904 cycle.is_k_connected_exact(2),
905 true,
906 "Cycle graph should be 2-connected with exact algorithm"
907 );
908
909 assert_eq!(
910 cycle.is_k_connected_exact(3),
911 false,
912 "Cycle graph should not be 3-connected with exact algorithm"
913 );
914
915 assert_eq!(
917 cycle.is_k_connected_approx(1),
918 cycle.is_k_connected_exact(1),
919 "Approximation and exact algorithms should agree for cycle graph with k=1"
920 );
921
922 assert_eq!(
923 cycle.is_k_connected_approx(2),
924 cycle.is_k_connected_exact(2),
925 "Approximation and exact algorithms should agree for cycle graph with k=2"
926 );
927
928 assert_eq!(
929 cycle.is_k_connected_approx(3),
930 cycle.is_k_connected_exact(3),
931 "Approximation and exact algorithms should agree for cycle graph with k=3"
932 );
933
934 let mut path = Graph::new(5);
936 path.add_edge(0, 1).unwrap();
937 path.add_edge(1, 2).unwrap();
938 path.add_edge(2, 3).unwrap();
939 path.add_edge(3, 4).unwrap();
940
941 assert_eq!(
942 path.is_k_connected_exact(1),
943 true,
944 "Path graph should be 1-connected with exact algorithm"
945 );
946
947 assert_eq!(
948 path.is_k_connected_exact(2),
949 false,
950 "Path graph should not be 2-connected with exact algorithm"
951 );
952
953 assert_eq!(
955 path.is_k_connected_approx(1),
956 path.is_k_connected_exact(1),
957 "Approximation and exact algorithms should agree for path graph with k=1"
958 );
959
960 assert_eq!(
961 path.is_k_connected_approx(2),
962 path.is_k_connected_exact(2),
963 "Approximation and exact algorithms should agree for path graph with k=2"
964 );
965
966 let mut test_graph = Graph::new(6);
969 test_graph.add_edge(0, 1).unwrap();
970 test_graph.add_edge(1, 2).unwrap();
971 test_graph.add_edge(2, 0).unwrap();
972 test_graph.add_edge(3, 4).unwrap();
973 test_graph.add_edge(4, 5).unwrap();
974 test_graph.add_edge(5, 3).unwrap();
975 test_graph.add_edge(0, 3).unwrap();
976 test_graph.add_edge(1, 4).unwrap();
977 test_graph.add_edge(2, 5).unwrap();
978
979 assert_eq!(
980 test_graph.is_k_connected_exact(3),
981 true,
982 "Test graph should be 3-connected with exact algorithm"
983 );
984
985 assert_eq!(
986 test_graph.is_k_connected_exact(4),
987 false,
988 "Test graph should not be 4-connected with exact algorithm"
989 );
990 }
991
992 #[test]
993 fn test_find_path() {
994 let mut path_graph = Graph::new(5);
996 path_graph.add_edge(0, 1).unwrap();
997 path_graph.add_edge(1, 2).unwrap();
998 path_graph.add_edge(2, 3).unwrap();
999 path_graph.add_edge(3, 4).unwrap();
1000
1001 let path = path_graph.find_path(0, 4);
1003 assert!(path.is_some(), "Should find a path from 0 to 4");
1004
1005 let path_vertices = path.unwrap();
1006 assert_eq!(path_vertices.len(), 5, "Path should visit 5 vertices");
1007 assert_eq!(path_vertices[0], 0, "Path should start at vertex 0");
1008 assert_eq!(path_vertices[4], 4, "Path should end at vertex 4");
1009
1010 let mut disconnected = Graph::new(5);
1012 disconnected.add_edge(0, 1).unwrap();
1013 disconnected.add_edge(1, 2).unwrap();
1014 let path = disconnected.find_path(0, 4);
1017 assert!(
1018 path.is_none(),
1019 "Should not find a path in disconnected graph"
1020 );
1021
1022 use std::collections::{HashMap, HashSet};
1024
1025 let mut custom_edges = HashMap::new();
1026 for i in 0..5 {
1027 custom_edges.insert(i, HashSet::new());
1028 }
1029
1030 custom_edges.get_mut(&0).unwrap().insert(2);
1032 custom_edges.get_mut(&2).unwrap().insert(0);
1033 custom_edges.get_mut(&2).unwrap().insert(4);
1034 custom_edges.get_mut(&4).unwrap().insert(2);
1035
1036 let custom_path = path_graph.find_path_in_subgraph(&custom_edges, 0, 4);
1037 assert!(custom_path.is_some(), "Should find a custom path");
1038
1039 let custom_path_vertices = custom_path.unwrap();
1040 assert_eq!(
1041 custom_path_vertices.len(),
1042 3,
1043 "Custom path should visit 3 vertices"
1044 );
1045 assert_eq!(
1046 custom_path_vertices[0], 0,
1047 "Custom path should start at vertex 0"
1048 );
1049 assert_eq!(
1050 custom_path_vertices[1], 2,
1051 "Custom path should go through vertex 2"
1052 );
1053 assert_eq!(
1054 custom_path_vertices[2], 4,
1055 "Custom path should end at vertex 4"
1056 );
1057 }
1058
1059 #[test]
1060 fn test_find_vertex_disjoint_paths() {
1061 let mut complete = Graph::new(5);
1063 for i in 0..4 {
1064 for j in (i + 1)..5 {
1065 complete.add_edge(i, j).unwrap();
1066 }
1067 }
1068
1069 let disjoint_paths = complete.find_vertex_disjoint_paths(0, 1);
1072 assert_eq!(
1073 disjoint_paths, 4,
1074 "Complete graph K5 should have 4 vertex-disjoint paths between any two vertices"
1075 );
1076
1077 let mut cycle = Graph::new(5);
1079 cycle.add_edge(0, 1).unwrap();
1080 cycle.add_edge(1, 2).unwrap();
1081 cycle.add_edge(2, 3).unwrap();
1082 cycle.add_edge(3, 4).unwrap();
1083 cycle.add_edge(4, 0).unwrap();
1084
1085 let disjoint_paths = cycle.find_vertex_disjoint_paths(0, 2);
1087 assert_eq!(
1088 disjoint_paths, 2,
1089 "Cycle graph should have 2 vertex-disjoint paths between any two non-adjacent vertices"
1090 );
1091
1092 let disjoint_paths_adj = cycle.find_vertex_disjoint_paths(0, 1);
1094 assert_eq!(
1095 disjoint_paths_adj, 2,
1096 "Cycle graph should handle adjacent vertices correctly"
1097 );
1098
1099 let mut path = Graph::new(5);
1101 path.add_edge(0, 1).unwrap();
1102 path.add_edge(1, 2).unwrap();
1103 path.add_edge(2, 3).unwrap();
1104 path.add_edge(3, 4).unwrap();
1105
1106 let disjoint_paths = path.find_vertex_disjoint_paths(0, 4);
1108 assert_eq!(
1109 disjoint_paths, 1,
1110 "Path graph should have 1 vertex-disjoint path between end vertices"
1111 );
1112
1113 let mut test_graph = Graph::new(6);
1115 test_graph.add_edge(0, 1).unwrap();
1116 test_graph.add_edge(1, 2).unwrap();
1117 test_graph.add_edge(2, 0).unwrap();
1118 test_graph.add_edge(3, 4).unwrap();
1119 test_graph.add_edge(4, 5).unwrap();
1120 test_graph.add_edge(5, 3).unwrap();
1121 test_graph.add_edge(0, 3).unwrap();
1122 test_graph.add_edge(1, 4).unwrap();
1123 test_graph.add_edge(2, 5).unwrap();
1124
1125 let disjoint_paths = test_graph.find_vertex_disjoint_paths(0, 5);
1127 assert_eq!(
1128 disjoint_paths, 3,
1129 "Test graph should have 3 vertex-disjoint paths between vertices 0 and 5"
1130 );
1131 }
1132
1133 #[test]
1134 fn test_cycle_graph() {
1135 let mut graph = Graph::new(5);
1137 graph.add_edge(0, 1).unwrap();
1138 graph.add_edge(1, 2).unwrap();
1139 graph.add_edge(2, 3).unwrap();
1140 graph.add_edge(3, 4).unwrap();
1141 graph.add_edge(4, 0).unwrap();
1142
1143 assert_eq!(graph.first_zagreb_index(), 20); assert_eq!(graph.min_degree(), 2);
1145 assert_eq!(graph.max_degree(), 2);
1146 assert_eq!(graph.edge_count(), 5);
1147
1148 assert!(graph.is_likely_hamiltonian(false));
1150 assert!(graph.is_likely_traceable(false));
1151 }
1152
1153 #[test]
1154 fn test_complete_graph() {
1155 let mut graph = Graph::new(6);
1157 for i in 0..5 {
1158 for j in (i + 1)..6 {
1159 graph.add_edge(i, j).unwrap();
1160 }
1161 }
1162
1163 assert_eq!(graph.first_zagreb_index(), 150);
1165 assert_eq!(graph.min_degree(), 5);
1166 assert_eq!(graph.max_degree(), 5);
1167 assert_eq!(graph.edge_count(), 15);
1168
1169 assert!(graph.is_likely_hamiltonian(false));
1171 assert!(graph.is_likely_traceable(false));
1172 }
1173
1174 #[test]
1175 fn test_star_graph() {
1176 let mut graph = Graph::new(5);
1179 graph.add_edge(0, 1).unwrap();
1180 graph.add_edge(0, 2).unwrap();
1181 graph.add_edge(0, 3).unwrap();
1182 graph.add_edge(0, 4).unwrap();
1183
1184 assert_eq!(graph.first_zagreb_index(), 20);
1186 assert_eq!(graph.min_degree(), 1);
1187 assert_eq!(graph.max_degree(), 4);
1188 assert_eq!(graph.edge_count(), 4);
1189
1190 assert!(!graph.is_likely_hamiltonian(false));
1192 assert!(graph.is_likely_traceable(false));
1194 }
1195
1196 #[test]
1197 fn test_petersen_graph() {
1198 let mut graph = Graph::new(10);
1200
1201 graph.add_edge(0, 1).unwrap();
1203 graph.add_edge(1, 2).unwrap();
1204 graph.add_edge(2, 3).unwrap();
1205 graph.add_edge(3, 4).unwrap();
1206 graph.add_edge(4, 0).unwrap();
1207
1208 graph.add_edge(0, 5).unwrap();
1210 graph.add_edge(1, 6).unwrap();
1211 graph.add_edge(2, 7).unwrap();
1212 graph.add_edge(3, 8).unwrap();
1213 graph.add_edge(4, 9).unwrap();
1214
1215 graph.add_edge(5, 7).unwrap();
1217 graph.add_edge(7, 9).unwrap();
1218 graph.add_edge(9, 6).unwrap();
1219 graph.add_edge(6, 8).unwrap();
1220 graph.add_edge(8, 5).unwrap();
1221
1222 assert_eq!(graph.vertex_count(), 10);
1224 assert_eq!(graph.edge_count(), 15);
1225 assert_eq!(graph.min_degree(), 3); assert_eq!(graph.max_degree(), 3); assert_eq!(graph.first_zagreb_index(), 90);
1230
1231 assert!(graph.is_k_connected(3, false));
1233
1234 assert!(!graph.is_likely_hamiltonian(false));
1236
1237 assert!(graph.is_likely_traceable(false));
1239
1240 let independence_num = graph.independence_number_approx();
1243 assert!(
1244 independence_num >= 4,
1245 "Expected independence number >= 4, got {}",
1246 independence_num
1247 );
1248 }
1249
1250 #[test]
1251 fn test_zagreb_index_calculation() {
1252 let mut complete5 = Graph::new(5);
1254 for i in 0..4 {
1255 for j in (i + 1)..5 {
1256 complete5.add_edge(i, j).unwrap();
1257 }
1258 }
1259 assert_eq!(complete5.first_zagreb_index(), 80);
1260
1261 let mut path5 = Graph::new(5);
1263 path5.add_edge(0, 1).unwrap();
1264 path5.add_edge(1, 2).unwrap();
1265 path5.add_edge(2, 3).unwrap();
1266 path5.add_edge(3, 4).unwrap();
1267 assert_eq!(path5.first_zagreb_index(), 14);
1268
1269 let empty = Graph::new(5);
1271 assert_eq!(empty.first_zagreb_index(), 0);
1272
1273 let single = Graph::new(1);
1275 assert_eq!(single.first_zagreb_index(), 0);
1276 }
1277
1278 #[test]
1279 fn test_hamiltonian_detection() {
1280 let mut complete5 = Graph::new(5);
1282 for i in 0..4 {
1283 for j in (i + 1)..5 {
1284 complete5.add_edge(i, j).unwrap();
1285 }
1286 }
1287 assert!(complete5.is_likely_hamiltonian(true));
1288
1289 let mut cycle5 = Graph::new(5);
1290 cycle5.add_edge(0, 1).unwrap();
1291 cycle5.add_edge(1, 2).unwrap();
1292 cycle5.add_edge(2, 3).unwrap();
1293 cycle5.add_edge(3, 4).unwrap();
1294 cycle5.add_edge(4, 0).unwrap();
1295 assert!(cycle5.is_likely_hamiltonian(true));
1296
1297 let mut star5 = Graph::new(5);
1299 star5.add_edge(0, 1).unwrap();
1300 star5.add_edge(0, 2).unwrap();
1301 star5.add_edge(0, 3).unwrap();
1302 star5.add_edge(0, 4).unwrap();
1303 assert!(!star5.is_likely_hamiltonian(true));
1304
1305 let mut petersen = Graph::new(10);
1307 petersen.add_edge(0, 1).unwrap();
1309 petersen.add_edge(1, 2).unwrap();
1310 petersen.add_edge(2, 3).unwrap();
1311 petersen.add_edge(3, 4).unwrap();
1312 petersen.add_edge(4, 0).unwrap();
1313 petersen.add_edge(0, 5).unwrap();
1315 petersen.add_edge(1, 6).unwrap();
1316 petersen.add_edge(2, 7).unwrap();
1317 petersen.add_edge(3, 8).unwrap();
1318 petersen.add_edge(4, 9).unwrap();
1319 petersen.add_edge(5, 7).unwrap();
1321 petersen.add_edge(7, 9).unwrap();
1322 petersen.add_edge(9, 6).unwrap();
1323 petersen.add_edge(6, 8).unwrap();
1324 petersen.add_edge(8, 5).unwrap();
1325 assert!(!petersen.is_likely_hamiltonian(true));
1326 }
1327
1328 #[test]
1329 fn test_traceable_detection() {
1330 let mut path = Graph::new(5);
1332 path.add_edge(0, 1).unwrap();
1333 path.add_edge(1, 2).unwrap();
1334 path.add_edge(2, 3).unwrap();
1335 path.add_edge(3, 4).unwrap();
1336 assert!(path.is_likely_traceable(true));
1337
1338 let mut star = Graph::new(5);
1340 star.add_edge(0, 1).unwrap();
1341 star.add_edge(0, 2).unwrap();
1342 star.add_edge(0, 3).unwrap();
1343 star.add_edge(0, 4).unwrap();
1344 assert!(star.is_likely_traceable(true));
1345
1346 let mut petersen = Graph::new(10);
1348 petersen.add_edge(0, 1).unwrap();
1350 petersen.add_edge(1, 2).unwrap();
1351 petersen.add_edge(2, 3).unwrap();
1352 petersen.add_edge(3, 4).unwrap();
1353 petersen.add_edge(4, 0).unwrap();
1354 petersen.add_edge(0, 5).unwrap();
1356 petersen.add_edge(1, 6).unwrap();
1357 petersen.add_edge(2, 7).unwrap();
1358 petersen.add_edge(3, 8).unwrap();
1359 petersen.add_edge(4, 9).unwrap();
1360 petersen.add_edge(5, 7).unwrap();
1362 petersen.add_edge(7, 9).unwrap();
1363 petersen.add_edge(9, 6).unwrap();
1364 petersen.add_edge(6, 8).unwrap();
1365 petersen.add_edge(8, 5).unwrap();
1366 assert!(petersen.is_likely_traceable(true));
1367 }
1368
1369 #[test]
1370 fn test_zagreb_upper_bound() {
1371 let mut cycle = Graph::new(5);
1373 cycle.add_edge(0, 1).unwrap();
1374 cycle.add_edge(1, 2).unwrap();
1375 cycle.add_edge(2, 3).unwrap();
1376 cycle.add_edge(3, 4).unwrap();
1377 cycle.add_edge(4, 0).unwrap();
1378
1379 let mut complete = Graph::new(5);
1380 for i in 0..4 {
1381 for j in (i + 1)..5 {
1382 complete.add_edge(i, j).unwrap();
1383 }
1384 }
1385
1386 let mut star = Graph::new(5);
1387 star.add_edge(0, 1).unwrap();
1388 star.add_edge(0, 2).unwrap();
1389 star.add_edge(0, 3).unwrap();
1390 star.add_edge(0, 4).unwrap();
1391
1392 assert!(cycle.first_zagreb_index() as f64 <= cycle.zagreb_upper_bound());
1394 assert!(complete.first_zagreb_index() as f64 <= complete.zagreb_upper_bound());
1395 assert!(star.first_zagreb_index() as f64 <= star.zagreb_upper_bound());
1396 }
1397
1398 #[test]
1399 fn test_graph_type_detection() {
1400 let mut complete = Graph::new(5);
1402 for i in 0..4 {
1403 for j in (i + 1)..5 {
1404 complete.add_edge(i, j).unwrap();
1405 }
1406 }
1407 assert!(complete.is_complete());
1408
1409 let mut cycle = Graph::new(5);
1411 cycle.add_edge(0, 1).unwrap();
1412 cycle.add_edge(1, 2).unwrap();
1413 cycle.add_edge(2, 3).unwrap();
1414 cycle.add_edge(3, 4).unwrap();
1415 cycle.add_edge(4, 0).unwrap();
1416 assert!(cycle.is_cycle());
1417
1418 let mut star = Graph::new(5);
1420 star.add_edge(0, 1).unwrap();
1421 star.add_edge(0, 2).unwrap();
1422 star.add_edge(0, 3).unwrap();
1423 star.add_edge(0, 4).unwrap();
1424 assert!(star.is_star());
1425
1426 let mut path = Graph::new(5);
1428 path.add_edge(0, 1).unwrap();
1429 path.add_edge(1, 2).unwrap();
1430 path.add_edge(2, 3).unwrap();
1431 path.add_edge(3, 4).unwrap();
1432 assert!(path.is_path());
1433
1434 assert!(!cycle.is_complete());
1436 assert!(!star.is_cycle());
1437 assert!(!path.is_star());
1438 assert!(!complete.is_path());
1439 }
1440
1441 #[test]
1442 fn test_theorem_implementations() {
1443 let mut graph = Graph::new(10);
1445 }
1455
1456 #[test]
1457 fn test_independence_number() {
1458 let mut path = Graph::new(5);
1460 path.add_edge(0, 1).unwrap();
1461 path.add_edge(1, 2).unwrap();
1462 path.add_edge(2, 3).unwrap();
1463 path.add_edge(3, 4).unwrap();
1464 assert_eq!(path.independence_number_approx(), 3);
1465
1466 let mut cycle = Graph::new(5);
1468 cycle.add_edge(0, 1).unwrap();
1469 cycle.add_edge(1, 2).unwrap();
1470 cycle.add_edge(2, 3).unwrap();
1471 cycle.add_edge(3, 4).unwrap();
1472 cycle.add_edge(4, 0).unwrap();
1473 assert_eq!(cycle.independence_number_approx(), 2);
1474
1475 let mut complete = Graph::new(5);
1477 for i in 0..4 {
1478 for j in (i + 1)..5 {
1479 complete.add_edge(i, j).unwrap();
1480 }
1481 }
1482 assert_eq!(complete.independence_number_approx(), 1);
1483 }
1484
1485 #[test]
1486 fn test_theorem_1_implementation() {
1487 let mut complete5 = Graph::new(5);
1491 for i in 0..4 {
1492 for j in (i+1)..5 {
1493 complete5.add_edge(i, j).unwrap();
1494 }
1495 }
1496 assert!(complete5.is_likely_hamiltonian(false),
1497 "Complete graph K5 should be identified as Hamiltonian");
1498
1499 let mut cycle6 = Graph::new(6);
1500 for i in 0..6 {
1501 cycle6.add_edge(i, (i+1) % 6).unwrap();
1502 }
1503 assert!(cycle6.is_likely_hamiltonian(false),
1504 "Cycle graph C6 should be identified as Hamiltonian");
1505
1506 let mut graph1 = Graph::new(8);
1509 for i in 0..8 {
1511 graph1.add_edge(i, (i+1) % 8).unwrap();
1512 }
1513 graph1.add_edge(0, 2).unwrap();
1515 graph1.add_edge(0, 3).unwrap();
1516 graph1.add_edge(0, 4).unwrap();
1517 graph1.add_edge(1, 3).unwrap();
1518 graph1.add_edge(1, 4).unwrap();
1519 graph1.add_edge(1, 5).unwrap();
1520 graph1.add_edge(2, 4).unwrap();
1521 graph1.add_edge(2, 5).unwrap();
1522 graph1.add_edge(2, 6).unwrap();
1523 graph1.add_edge(3, 5).unwrap();
1524 graph1.add_edge(3, 6).unwrap();
1525 graph1.add_edge(3, 7).unwrap();
1526 graph1.add_edge(4, 6).unwrap();
1527 graph1.add_edge(4, 7).unwrap();
1528 graph1.add_edge(5, 7).unwrap();
1529
1530 let k = 2;
1531 let n = graph1.vertex_count();
1532 let e = graph1.edge_count();
1533 let delta = graph1.min_degree();
1534 let delta_max = graph1.max_degree();
1535 let z1 = graph1.first_zagreb_index();
1536
1537 let part1 = (n - k - 1) * delta_max * delta_max;
1539 let part2 = (e * e) / (k + 1);
1540 let part3 = ((n - k - 1) as f64).sqrt() - (delta as f64).sqrt();
1541 let part3_squared = part3 * part3;
1542 let threshold = part1 + part2 + (part3_squared * e as f64) as usize;
1543
1544 println!("Theorem 1 test: n={}, k={}, e={}, delta={}, delta_max={}",
1545 n, k, e, delta, delta_max);
1546 println!("Theorem 1 test: Zagreb index = {}, threshold = {}", z1, threshold);
1547
1548 let hamiltonian_by_property = graph1.is_likely_hamiltonian(false);
1551 println!("Is Hamiltonian according to implementation: {}", hamiltonian_by_property);
1552
1553 assert!(hamiltonian_by_property,
1555 "The graph should be identified as Hamiltonian");
1556
1557 let mut bipartite = Graph::new(5);
1562 bipartite.add_edge(0, 2).unwrap();
1564 bipartite.add_edge(0, 3).unwrap();
1565 bipartite.add_edge(0, 4).unwrap();
1566 bipartite.add_edge(1, 2).unwrap();
1567 bipartite.add_edge(1, 3).unwrap();
1568 bipartite.add_edge(1, 4).unwrap();
1569
1570 let bipartite_hamiltonian = bipartite.is_likely_hamiltonian(false);
1571 println!("K_{{2,3}} bipartite graph is Hamiltonian according to implementation: {}",
1572 bipartite_hamiltonian);
1573
1574 let special_case_handled = bipartite.is_k_connected(k, false) &&
1579 !bipartite_hamiltonian;
1580
1581 println!("K_{{2,3}} is k-connected: {}", bipartite.is_k_connected(k, false));
1582 println!("Special case K_{{k,k+1}} handled: {}", special_case_handled);
1583
1584 if special_case_handled {
1587 assert!(!bipartite_hamiltonian,
1588 "K_{{2,3}} bipartite graph should be identified as non-Hamiltonian if special cases are handled");
1589 }
1590 }
1591
1592 #[test]
1593 fn test_theorem_2_implementation() {
1594 let mut path5 = Graph::new(5);
1598 for i in 0..4 {
1599 path5.add_edge(i, i+1).unwrap();
1600 }
1601 assert!(path5.is_likely_traceable(false),
1602 "Path graph P5 should be identified as traceable");
1603
1604 let mut star5 = Graph::new(5);
1605 for i in 1..5 {
1606 star5.add_edge(0, i).unwrap();
1607 }
1608 assert!(star5.is_likely_traceable(false),
1609 "Star graph K_{{1,4}} should be identified as traceable");
1610
1611 let mut simple_path = Graph::new(10);
1614 for i in 0..9 {
1615 simple_path.add_edge(i, i+1).unwrap();
1616 }
1617
1618 let simple_path_traceable = simple_path.is_likely_traceable(false);
1619 println!("Simple path P10 is traceable according to implementation: {}",
1620 simple_path_traceable);
1621
1622 assert!(simple_path_traceable,
1623 "A simple path graph P10 should be identified as traceable");
1624
1625 let mut complex_path = Graph::new(10);
1628
1629 for i in 0..9 {
1631 complex_path.add_edge(i, i+1).unwrap();
1632 }
1633
1634 complex_path.add_edge(0, 2).unwrap();
1636 complex_path.add_edge(2, 4).unwrap();
1637 complex_path.add_edge(4, 6).unwrap();
1638 complex_path.add_edge(6, 8).unwrap();
1639
1640 let k = 1;
1641 let n = complex_path.vertex_count();
1642 let e = complex_path.edge_count();
1643 let delta = complex_path.min_degree();
1644 let delta_max = complex_path.max_degree();
1645 let z1 = complex_path.first_zagreb_index();
1646
1647 let part1 = (n - k - 2) * delta_max * delta_max;
1649 let part2 = (e * e) / (k + 2);
1650 let part3 = ((n - k - 2) as f64).sqrt() - (delta as f64).sqrt();
1651 let part3_squared = part3 * part3;
1652 let threshold = part1 + part2 + (part3_squared * e as f64) as usize;
1653
1654 println!("Theorem 2 test with complex path: n={}, k={}, e={}, delta={}, delta_max={}",
1655 n, k, e, delta, delta_max);
1656 println!("Theorem 2 test: Zagreb index = {}, threshold = {}", z1, threshold);
1657
1658 let complex_path_traceable = complex_path.is_likely_traceable(false);
1659 println!("Complex path is traceable according to implementation: {}",
1660 complex_path_traceable);
1661
1662 let complex_path_traceable_exact = complex_path.is_likely_traceable(true);
1664 println!("Complex path is traceable with exact connectivity check: {}",
1665 complex_path_traceable_exact);
1666
1667 println!("Complex path is 1-connected: {}", complex_path.is_k_connected(1, false));
1669 println!("Complex path is identified as a path: {}", complex_path.is_path());
1670
1671 if !complex_path_traceable {
1674 println!("WARNING: The implementation doesn't identify a complex path as traceable");
1675 println!("This may indicate an issue with the traceable detection algorithm");
1676 }
1677
1678 let mut small_bipartite = Graph::new(4);
1681 small_bipartite.add_edge(0, 1).unwrap();
1682 small_bipartite.add_edge(0, 2).unwrap();
1683 small_bipartite.add_edge(0, 3).unwrap();
1684
1685 let small_bipartite_traceable = small_bipartite.is_likely_traceable(false);
1686 println!("K_{{1,3}} bipartite graph is traceable according to implementation: {}",
1687 small_bipartite_traceable);
1688
1689 assert!(small_bipartite_traceable,
1690 "K_{{1,3}} bipartite graph should be identified as traceable");
1691
1692 let mut bipartite = Graph::new(6);
1694 for i in 0..2 {
1696 for j in 2..6 {
1697 bipartite.add_edge(i, j).unwrap();
1698 }
1699 }
1700
1701 let bipartite_traceable = bipartite.is_likely_traceable(false);
1702 println!("K_{{2,4}} bipartite graph is traceable according to implementation: {}",
1703 bipartite_traceable);
1704
1705 println!("K_{{2,4}} is 2-connected: {}", bipartite.is_k_connected(2, false));
1707
1708 let mut cycle = Graph::new(10);
1710 for i in 0..10 {
1711 cycle.add_edge(i, (i+1) % 10).unwrap();
1712 }
1713
1714 let cycle_traceable = cycle.is_likely_traceable(false);
1715 println!("Cycle C10 is traceable according to implementation: {}", cycle_traceable);
1716
1717 assert!(cycle_traceable, "Cycle graph C10 should be identified as traceable");
1718 }
1719
1720 #[test]
1721 fn test_theorem_3_upper_bound() {
1722 let mut complete = Graph::new(5);
1728 for i in 0..4 {
1729 for j in (i+1)..5 {
1730 complete.add_edge(i, j).unwrap();
1731 }
1732 }
1733
1734 let z1_complete = complete.first_zagreb_index();
1736
1737 let upper_bound_complete = complete.zagreb_upper_bound();
1739
1740 assert!(z1_complete as f64 <= upper_bound_complete,
1742 "Zagreb index {} should not exceed upper bound {} for complete graph",
1743 z1_complete, upper_bound_complete);
1744
1745 println!("K_5: Zagreb index = {}, upper bound = {}",
1746 z1_complete, upper_bound_complete);
1747
1748 let mut cycle = Graph::new(6);
1750 for i in 0..6 {
1751 cycle.add_edge(i, (i+1) % 6).unwrap();
1752 }
1753
1754 let z1_cycle = cycle.first_zagreb_index();
1755 let upper_bound_cycle = cycle.zagreb_upper_bound();
1756
1757 assert!(z1_cycle as f64 <= upper_bound_cycle,
1759 "Zagreb index {} should not exceed upper bound {} for cycle graph",
1760 z1_cycle, upper_bound_cycle);
1761
1762 println!("C_6: Zagreb index = {}, upper bound = {}",
1763 z1_cycle, upper_bound_cycle);
1764
1765 let mut star = Graph::new(6);
1767 for i in 1..6 {
1768 star.add_edge(0, i).unwrap();
1769 }
1770
1771 let z1_star = star.first_zagreb_index();
1772 let upper_bound_star = star.zagreb_upper_bound();
1773
1774 assert!(z1_star as f64 <= upper_bound_star,
1776 "Zagreb index {} should not exceed upper bound {} for star graph",
1777 z1_star, upper_bound_star);
1778
1779 println!("K_{{1,5}}: Zagreb index = {}, upper bound = {}",
1780 z1_star, upper_bound_star);
1781
1782 let mut bipartite = Graph::new(6);
1784 for i in 0..2 {
1786 for j in 2..6 {
1787 bipartite.add_edge(i, j).unwrap();
1788 }
1789 }
1790
1791 let z1_bipartite = bipartite.first_zagreb_index();
1792 let upper_bound_bipartite = bipartite.zagreb_upper_bound();
1793
1794 assert!(z1_bipartite as f64 <= upper_bound_bipartite,
1796 "Zagreb index {} should not exceed upper bound {} for bipartite graph",
1797 z1_bipartite, upper_bound_bipartite);
1798
1799 println!("K_{{2,4}}: Zagreb index = {}, upper bound = {}",
1800 z1_bipartite, upper_bound_bipartite);
1801
1802 let mut petersen = Graph::new(10);
1804 petersen.add_edge(0, 1).unwrap();
1806 petersen.add_edge(1, 2).unwrap();
1807 petersen.add_edge(2, 3).unwrap();
1808 petersen.add_edge(3, 4).unwrap();
1809 petersen.add_edge(4, 0).unwrap();
1810 petersen.add_edge(0, 5).unwrap();
1812 petersen.add_edge(1, 6).unwrap();
1813 petersen.add_edge(2, 7).unwrap();
1814 petersen.add_edge(3, 8).unwrap();
1815 petersen.add_edge(4, 9).unwrap();
1816 petersen.add_edge(5, 7).unwrap();
1818 petersen.add_edge(7, 9).unwrap();
1819 petersen.add_edge(9, 6).unwrap();
1820 petersen.add_edge(6, 8).unwrap();
1821 petersen.add_edge(8, 5).unwrap();
1822
1823 let z1_petersen = petersen.first_zagreb_index();
1824 let upper_bound_petersen = petersen.zagreb_upper_bound();
1825
1826 assert!(z1_petersen as f64 <= upper_bound_petersen,
1828 "Zagreb index {} should not exceed upper bound {} for Petersen graph",
1829 z1_petersen, upper_bound_petersen);
1830
1831 println!("Petersen: Zagreb index = {}, upper bound = {}",
1832 z1_petersen, upper_bound_petersen);
1833 }
1834
1835 #[test]
1836 fn test_graph_properties() {
1837 let mut complete5 = Graph::new(5);
1841 for i in 0..4 {
1842 for j in (i+1)..5 {
1843 complete5.add_edge(i, j).unwrap();
1844 }
1845 }
1846
1847 let is_complete = complete5.is_complete();
1849 let is_hamiltonian = complete5.is_likely_hamiltonian(false);
1850 let is_traceable = complete5.is_likely_traceable(false);
1851
1852 println!("K_5: is_complete={}, is_hamiltonian={}, is_traceable={}",
1853 is_complete, is_hamiltonian, is_traceable);
1854
1855 assert!(is_complete, "K_5 should be identified as a complete graph");
1856 assert!(is_hamiltonian, "K_5 should be identified as Hamiltonian");
1857 assert!(is_traceable, "K_5 should be identified as traceable");
1858
1859 let mut cycle6 = Graph::new(6);
1861 for i in 0..6 {
1862 cycle6.add_edge(i, (i+1) % 6).unwrap();
1863 }
1864
1865 let is_cycle = cycle6.is_cycle();
1867 let cycle_hamiltonian = cycle6.is_likely_hamiltonian(false);
1868 let cycle_traceable = cycle6.is_likely_traceable(false);
1869
1870 println!("C_6: is_cycle={}, is_hamiltonian={}, is_traceable={}",
1871 is_cycle, cycle_hamiltonian, cycle_traceable);
1872
1873 assert!(is_cycle, "C_6 should be identified as a cycle graph");
1874 assert!(cycle_hamiltonian, "C_6 should be identified as Hamiltonian");
1875 assert!(cycle_traceable, "C_6 should be identified as traceable");
1876
1877 let mut path5 = Graph::new(5);
1879 for i in 0..4 {
1880 path5.add_edge(i, i+1).unwrap();
1881 }
1882
1883 let is_path = path5.is_path();
1885 let path_hamiltonian = path5.is_likely_hamiltonian(false);
1886 let path_traceable = path5.is_likely_traceable(false);
1887
1888 println!("P_5: is_path={}, is_hamiltonian={}, is_traceable={}",
1889 is_path, path_hamiltonian, path_traceable);
1890
1891 assert!(is_path, "P_5 should be identified as a path graph");
1892 assert!(!path_hamiltonian, "P_5 should not be identified as Hamiltonian");
1893 assert!(path_traceable, "P_5 should be identified as traceable");
1894
1895 let mut star5 = Graph::new(5);
1897 for i in 1..5 {
1898 star5.add_edge(0, i).unwrap();
1899 }
1900
1901 let is_star = star5.is_star();
1903 let star_hamiltonian = star5.is_likely_hamiltonian(false);
1904 let star_traceable = star5.is_likely_traceable(false);
1905
1906 println!("K_{{1,4}}: is_star={}, is_hamiltonian={}, is_traceable={}",
1907 is_star, star_hamiltonian, star_traceable);
1908
1909 assert!(is_star, "K_{{1,4}} should be identified as a star graph");
1910 assert!(!star_hamiltonian, "K_{{1,4}} should not be identified as Hamiltonian");
1911 assert!(star_traceable, "K_{{1,4}} should be identified as traceable");
1912
1913 let mut petersen = Graph::new(10);
1915 petersen.add_edge(0, 1).unwrap();
1917 petersen.add_edge(1, 2).unwrap();
1918 petersen.add_edge(2, 3).unwrap();
1919 petersen.add_edge(3, 4).unwrap();
1920 petersen.add_edge(4, 0).unwrap();
1921 petersen.add_edge(0, 5).unwrap();
1923 petersen.add_edge(1, 6).unwrap();
1924 petersen.add_edge(2, 7).unwrap();
1925 petersen.add_edge(3, 8).unwrap();
1926 petersen.add_edge(4, 9).unwrap();
1927 petersen.add_edge(5, 7).unwrap();
1929 petersen.add_edge(7, 9).unwrap();
1930 petersen.add_edge(9, 6).unwrap();
1931 petersen.add_edge(6, 8).unwrap();
1932 petersen.add_edge(8, 5).unwrap();
1933
1934 let is_petersen = petersen.is_petersen();
1936 let petersen_hamiltonian = petersen.is_likely_hamiltonian(false);
1937 let petersen_traceable = petersen.is_likely_traceable(false);
1938
1939 println!("Petersen: is_petersen={}, is_hamiltonian={}, is_traceable={}",
1940 is_petersen, petersen_hamiltonian, petersen_traceable);
1941
1942 assert!(is_petersen, "Petersen graph should be identified as such");
1945
1946 if is_petersen {
1948 assert!(!petersen_hamiltonian, "Petersen graph should not be identified as Hamiltonian");
1949 assert!(petersen_traceable, "Petersen graph should be identified as traceable");
1950 }
1951
1952 let mut cube = Graph::new(8);
1954 cube.add_edge(0, 1).unwrap();
1956 cube.add_edge(1, 2).unwrap();
1957 cube.add_edge(2, 3).unwrap();
1958 cube.add_edge(3, 0).unwrap();
1959 cube.add_edge(4, 5).unwrap();
1961 cube.add_edge(5, 6).unwrap();
1962 cube.add_edge(6, 7).unwrap();
1963 cube.add_edge(7, 4).unwrap();
1964 cube.add_edge(0, 4).unwrap();
1966 cube.add_edge(1, 5).unwrap();
1967 cube.add_edge(2, 6).unwrap();
1968 cube.add_edge(3, 7).unwrap();
1969
1970 let cube_hamiltonian = cube.is_likely_hamiltonian(false);
1972 let cube_traceable = cube.is_likely_traceable(false);
1973 let cube_z1 = cube.first_zagreb_index();
1974
1975 println!("Cube graph: Zagreb index={}, is_hamiltonian={}, is_traceable={}",
1976 cube_z1, cube_hamiltonian, cube_traceable);
1977
1978 assert_eq!(cube_z1, 72, "Cube graph Zagreb index should be 8 * 3² = 72");
1981
1982 println!("Implementation identifies cube graph as Hamiltonian: {}", cube_hamiltonian);
1984 }
1985}