1use crate::core::error::IgraphError;
8use crate::core::{Graph, IgraphResult, VertexId};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum GraphProductType {
16 Cartesian,
19 Lexicographic,
22 Strong,
24 Tensor,
27 Modular,
31}
32
33pub fn graph_product(
59 g1: &Graph,
60 g2: &Graph,
61 product_type: GraphProductType,
62) -> IgraphResult<Graph> {
63 match product_type {
64 GraphProductType::Cartesian => cartesian_product(g1, g2),
65 GraphProductType::Lexicographic => lexicographic_product(g1, g2),
66 GraphProductType::Strong => strong_product(g1, g2),
67 GraphProductType::Tensor => tensor_product(g1, g2),
68 GraphProductType::Modular => modular_product(g1, g2),
69 }
70}
71
72pub fn cartesian_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
108 check_same_directedness(g1, g2, "cartesian_product")?;
109
110 let n1 = g1.vcount();
111 let n2 = g2.vcount();
112 let directed = g1.is_directed();
113
114 let n = product_vertex_count(n1, n2)?;
115
116 if n == 0 {
117 return Graph::new(0, directed);
118 }
119
120 let e1 = g1.ecount();
121 let e2 = g2.ecount();
122
123 let total_edges = (n1 as usize)
124 .checked_mul(e2)
125 .and_then(|a| a.checked_add((n2 as usize).checked_mul(e1)?))
126 .ok_or_else(|| {
127 IgraphError::InvalidArgument("edge count overflow in cartesian_product".to_string())
128 })?;
129
130 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
131
132 for eid in 0..e1 {
134 #[allow(clippy::cast_possible_truncation)]
135 let (u, v) = g1.edge(eid as u32)?;
136 for j in 0..n2 {
137 let src = u * n2 + j;
138 let tgt = v * n2 + j;
139 edges.push((src, tgt));
140 }
141 }
142
143 for eid in 0..e2 {
145 #[allow(clippy::cast_possible_truncation)]
146 let (u, v) = g2.edge(eid as u32)?;
147 for i in 0..n1 {
148 let src = i * n2 + u;
149 let tgt = i * n2 + v;
150 edges.push((src, tgt));
151 }
152 }
153
154 let mut result = Graph::new(n, directed)?;
155 result.add_edges(edges)?;
156 Ok(result)
157}
158
159pub fn tensor_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
198 check_same_directedness(g1, g2, "tensor_product")?;
199
200 let n1 = g1.vcount();
201 let n2 = g2.vcount();
202 let directed = g1.is_directed();
203
204 let n = product_vertex_count(n1, n2)?;
205
206 if n == 0 {
207 return Graph::new(0, directed);
208 }
209
210 let e1 = g1.ecount();
211 let e2 = g2.ecount();
212
213 let multiplier: usize = if directed { 1 } else { 2 };
214 let total_edges = e1
215 .checked_mul(e2)
216 .and_then(|a| a.checked_mul(multiplier))
217 .ok_or_else(|| {
218 IgraphError::InvalidArgument("edge count overflow in tensor_product".to_string())
219 })?;
220
221 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
222
223 for eid1 in 0..e1 {
224 #[allow(clippy::cast_possible_truncation)]
225 let (u1, v1) = g1.edge(eid1 as u32)?;
226 for eid2 in 0..e2 {
227 #[allow(clippy::cast_possible_truncation)]
228 let (u2, v2) = g2.edge(eid2 as u32)?;
229
230 let src = u1 * n2 + u2;
232 let tgt = v1 * n2 + v2;
233 edges.push((src, tgt));
234
235 if !directed {
236 let src2 = u1 * n2 + v2;
238 let tgt2 = v1 * n2 + u2;
239 edges.push((src2, tgt2));
240 }
241 }
242 }
243
244 let mut result = Graph::new(n, directed)?;
245 result.add_edges(edges)?;
246 Ok(result)
247}
248
249pub fn strong_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
290 check_same_directedness(g1, g2, "strong_product")?;
291
292 let n1 = g1.vcount();
293 let n2 = g2.vcount();
294 let directed = g1.is_directed();
295
296 let n = product_vertex_count(n1, n2)?;
297
298 if n == 0 {
299 return Graph::new(0, directed);
300 }
301
302 let e1 = g1.ecount();
303 let e2 = g2.ecount();
304
305 let multiplier: usize = if directed { 1 } else { 2 };
306 let cartesian_count = (n1 as usize) * e2 + (n2 as usize) * e1;
307 let tensor_count = e1 * e2 * multiplier;
308 let total_edges = cartesian_count.checked_add(tensor_count).ok_or_else(|| {
309 IgraphError::InvalidArgument("edge count overflow in strong_product".to_string())
310 })?;
311
312 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
313
314 for eid in 0..e1 {
316 #[allow(clippy::cast_possible_truncation)]
317 let (u, v) = g1.edge(eid as u32)?;
318 for j in 0..n2 {
319 edges.push((u * n2 + j, v * n2 + j));
320 }
321 }
322
323 for eid in 0..e2 {
325 #[allow(clippy::cast_possible_truncation)]
326 let (u, v) = g2.edge(eid as u32)?;
327 for i in 0..n1 {
328 edges.push((i * n2 + u, i * n2 + v));
329 }
330 }
331
332 for eid1 in 0..e1 {
334 #[allow(clippy::cast_possible_truncation)]
335 let (u1, v1) = g1.edge(eid1 as u32)?;
336 for eid2 in 0..e2 {
337 #[allow(clippy::cast_possible_truncation)]
338 let (u2, v2) = g2.edge(eid2 as u32)?;
339 edges.push((u1 * n2 + u2, v1 * n2 + v2));
340 if !directed {
341 edges.push((u1 * n2 + v2, v1 * n2 + u2));
342 }
343 }
344 }
345
346 let mut result = Graph::new(n, directed)?;
347 result.add_edges(edges)?;
348 Ok(result)
349}
350
351pub fn lexicographic_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
388 check_same_directedness(g1, g2, "lexicographic_product")?;
389
390 let n1 = g1.vcount();
391 let n2 = g2.vcount();
392 let directed = g1.is_directed();
393
394 let n = product_vertex_count(n1, n2)?;
395
396 if n == 0 {
397 return Graph::new(0, directed);
398 }
399
400 let e1 = g1.ecount();
401 let e2 = g2.ecount();
402
403 let g2_part = (n1 as usize) * e2;
405
406 let pairs_per_edge: usize = if directed {
408 (n2 as usize) * (n2 as usize)
409 } else {
410 (n2 as usize) * (n2 as usize)
415 };
416 let g1_part = e1.checked_mul(pairs_per_edge).ok_or_else(|| {
417 IgraphError::InvalidArgument("edge count overflow in lexicographic_product".to_string())
418 })?;
419
420 let total_edges = g2_part.checked_add(g1_part).ok_or_else(|| {
421 IgraphError::InvalidArgument("edge count overflow in lexicographic_product".to_string())
422 })?;
423
424 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
425
426 for eid in 0..e2 {
428 #[allow(clippy::cast_possible_truncation)]
429 let (u, v) = g2.edge(eid as u32)?;
430 for i in 0..n1 {
431 edges.push((i * n2 + u, i * n2 + v));
432 }
433 }
434
435 for eid in 0..e1 {
437 #[allow(clippy::cast_possible_truncation)]
438 let (u, v) = g1.edge(eid as u32)?;
439 for j in 0..n2 {
440 for l in 0..n2 {
441 edges.push((u * n2 + j, v * n2 + l));
442 }
443 }
444 }
445
446 let mut result = Graph::new(n, directed)?;
447 result.add_edges(edges)?;
448 Ok(result)
449}
450
451pub fn rooted_product(g1: &Graph, g2: &Graph, root: u32) -> IgraphResult<Graph> {
495 check_same_directedness(g1, g2, "rooted_product")?;
496
497 let n1 = g1.vcount();
498 let n2 = g2.vcount();
499
500 if n2 == 0 || root >= n2 {
501 return Err(IgraphError::InvalidArgument(
502 "root vertex is not present in the second graph".to_string(),
503 ));
504 }
505
506 let directed = g1.is_directed();
507 let n = product_vertex_count(n1, n2)?;
508
509 if n == 0 {
510 return Graph::new(0, directed);
511 }
512
513 let e1 = g1.ecount();
514 let e2 = g2.ecount();
515
516 let total_edges = (n1 as usize)
518 .checked_mul(e2)
519 .and_then(|v| v.checked_add(e1))
520 .ok_or_else(|| {
521 IgraphError::InvalidArgument("edge count overflow in rooted_product".to_string())
522 })?;
523
524 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
525
526 for eid_usize in 0..e1 {
529 #[allow(clippy::cast_possible_truncation)]
530 let eid = eid_usize as u32;
531 let (from, to) = g1.edge(eid)?;
532 let new_from = from * n2 + root;
533 let new_to = to * n2 + root;
534 edges.push((new_from, new_to));
535 }
536
537 for eid_usize in 0..e2 {
540 #[allow(clippy::cast_possible_truncation)]
541 let eid = eid_usize as u32;
542 let (from, to) = g2.edge(eid)?;
543 for j in 0..n1 {
544 let new_from = j * n2 + from;
545 let new_to = j * n2 + to;
546 edges.push((new_from, new_to));
547 }
548 }
549
550 let mut result = Graph::new(n, directed)?;
551 result.add_edges(edges)?;
552 Ok(result)
553}
554
555pub fn modular_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
597 use crate::algorithms::operators::complementer::complementer;
598 use crate::algorithms::operators::union::union;
599 use crate::algorithms::properties::is_simple::is_simple;
600
601 check_same_directedness(g1, g2, "modular_product")?;
602
603 let simple1 = is_simple(g1)?;
604 let simple2 = is_simple(g2)?;
605 if !simple1 || !simple2 {
606 return Err(IgraphError::InvalidArgument(
607 "modular product requires simple graphs as input".to_string(),
608 ));
609 }
610
611 let n1 = g1.vcount();
612 let n2 = g2.vcount();
613
614 if n1 == 0 || n2 == 0 {
615 let directed = g1.is_directed();
616 return Graph::new(0, directed);
617 }
618
619 let g1_compl = complementer(g1, false)?;
620 let g2_compl = complementer(g2, false)?;
621
622 let tp_orig = tensor_product(g1, g2)?;
623 let tp_compl = tensor_product(&g1_compl, &g2_compl)?;
624
625 union(&tp_orig, &tp_compl)
626}
627
628fn check_same_directedness(g1: &Graph, g2: &Graph, op: &str) -> IgraphResult<()> {
629 if g1.is_directed() != g2.is_directed() {
630 return Err(IgraphError::InvalidArgument(format!(
631 "cannot compute {op} of directed and undirected graphs"
632 )));
633 }
634 Ok(())
635}
636
637fn product_vertex_count(n1: u32, n2: u32) -> IgraphResult<u32> {
638 let count = u64::from(n1) * u64::from(n2);
639 u32::try_from(count).map_err(|_| {
640 IgraphError::InvalidArgument("product vertex count exceeds u32::MAX".to_string())
641 })
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647
648 #[test]
651 fn test_cartesian_k2_k2() {
652 let mut g1 = Graph::with_vertices(2);
653 g1.add_edge(0, 1).unwrap();
654 let mut g2 = Graph::with_vertices(2);
655 g2.add_edge(0, 1).unwrap();
656
657 let p = cartesian_product(&g1, &g2).unwrap();
658 assert_eq!(p.vcount(), 4);
659 assert_eq!(p.ecount(), 4);
661 }
662
663 #[test]
664 fn test_cartesian_k2_k3() {
665 let mut g1 = Graph::with_vertices(2);
666 g1.add_edge(0, 1).unwrap();
667
668 let mut g2 = Graph::with_vertices(3);
669 g2.add_edge(0, 1).unwrap();
670 g2.add_edge(1, 2).unwrap();
671 g2.add_edge(0, 2).unwrap();
672
673 let p = cartesian_product(&g1, &g2).unwrap();
674 assert_eq!(p.vcount(), 6);
675 assert_eq!(p.ecount(), 9);
677 }
678
679 #[test]
680 fn test_cartesian_empty_graph() {
681 let g1 = Graph::with_vertices(0);
682 let g2 = Graph::with_vertices(3);
683 let p = cartesian_product(&g1, &g2).unwrap();
684 assert_eq!(p.vcount(), 0);
685 assert_eq!(p.ecount(), 0);
686 }
687
688 #[test]
689 fn test_cartesian_isolated_vertices() {
690 let g1 = Graph::with_vertices(3);
691 let g2 = Graph::with_vertices(4);
692 let p = cartesian_product(&g1, &g2).unwrap();
693 assert_eq!(p.vcount(), 12);
694 assert_eq!(p.ecount(), 0);
695 }
696
697 #[test]
698 fn test_cartesian_directed() {
699 let mut g1 = Graph::new(2, true).unwrap();
700 g1.add_edge(0, 1).unwrap();
701 let mut g2 = Graph::new(2, true).unwrap();
702 g2.add_edge(0, 1).unwrap();
703
704 let p = cartesian_product(&g1, &g2).unwrap();
705 assert!(p.is_directed());
706 assert_eq!(p.vcount(), 4);
707 assert_eq!(p.ecount(), 4);
709 }
710
711 #[test]
712 fn test_cartesian_mixed_error() {
713 let g1 = Graph::new(2, true).unwrap();
714 let g2 = Graph::with_vertices(2);
715 assert!(cartesian_product(&g1, &g2).is_err());
716 }
717
718 #[test]
721 fn test_tensor_k2_k2() {
722 let mut g1 = Graph::with_vertices(2);
723 g1.add_edge(0, 1).unwrap();
724 let mut g2 = Graph::with_vertices(2);
725 g2.add_edge(0, 1).unwrap();
726
727 let p = tensor_product(&g1, &g2).unwrap();
728 assert_eq!(p.vcount(), 4);
729 assert_eq!(p.ecount(), 2);
731 }
732
733 #[test]
734 fn test_tensor_path_k2() {
735 let mut g1 = Graph::with_vertices(3);
736 g1.add_edge(0, 1).unwrap();
737 g1.add_edge(1, 2).unwrap();
738
739 let mut g2 = Graph::with_vertices(2);
740 g2.add_edge(0, 1).unwrap();
741
742 let p = tensor_product(&g1, &g2).unwrap();
743 assert_eq!(p.vcount(), 6);
744 assert_eq!(p.ecount(), 4);
746 }
747
748 #[test]
749 fn test_tensor_directed() {
750 let mut g1 = Graph::new(2, true).unwrap();
751 g1.add_edge(0, 1).unwrap();
752 let mut g2 = Graph::new(2, true).unwrap();
753 g2.add_edge(0, 1).unwrap();
754
755 let p = tensor_product(&g1, &g2).unwrap();
756 assert!(p.is_directed());
757 assert_eq!(p.vcount(), 4);
758 assert_eq!(p.ecount(), 1);
760 assert_eq!(p.edge(0).unwrap(), (0, 3)); }
762
763 #[test]
764 fn test_tensor_no_edges() {
765 let g1 = Graph::with_vertices(3);
766 let mut g2 = Graph::with_vertices(2);
767 g2.add_edge(0, 1).unwrap();
768
769 let p = tensor_product(&g1, &g2).unwrap();
770 assert_eq!(p.vcount(), 6);
771 assert_eq!(p.ecount(), 0);
772 }
773
774 #[test]
775 fn test_tensor_empty() {
776 let g1 = Graph::with_vertices(0);
777 let g2 = Graph::with_vertices(5);
778 let p = tensor_product(&g1, &g2).unwrap();
779 assert_eq!(p.vcount(), 0);
780 }
781
782 #[test]
785 fn test_strong_k2_k2() {
786 let mut g1 = Graph::with_vertices(2);
787 g1.add_edge(0, 1).unwrap();
788 let mut g2 = Graph::with_vertices(2);
789 g2.add_edge(0, 1).unwrap();
790
791 let p = strong_product(&g1, &g2).unwrap();
792 assert_eq!(p.vcount(), 4);
793 assert_eq!(p.ecount(), 6);
795 }
796
797 #[test]
798 fn test_strong_directed() {
799 let mut g1 = Graph::new(2, true).unwrap();
800 g1.add_edge(0, 1).unwrap();
801 let mut g2 = Graph::new(2, true).unwrap();
802 g2.add_edge(0, 1).unwrap();
803
804 let p = strong_product(&g1, &g2).unwrap();
805 assert!(p.is_directed());
806 assert_eq!(p.vcount(), 4);
807 assert_eq!(p.ecount(), 5);
809 }
810
811 #[test]
812 fn test_strong_one_edgeless() {
813 let mut g1 = Graph::with_vertices(2);
814 g1.add_edge(0, 1).unwrap();
815 let g2 = Graph::with_vertices(3);
816
817 let p = strong_product(&g1, &g2).unwrap();
818 assert_eq!(p.vcount(), 6);
819 assert_eq!(p.ecount(), 3);
821 }
822
823 #[test]
826 fn test_lexicographic_k2_isolated() {
827 let mut g1 = Graph::with_vertices(2);
828 g1.add_edge(0, 1).unwrap();
829 let g2 = Graph::with_vertices(3);
830
831 let p = lexicographic_product(&g1, &g2).unwrap();
832 assert_eq!(p.vcount(), 6);
833 assert_eq!(p.ecount(), 9);
835 }
836
837 #[test]
838 fn test_lexicographic_k2_k2() {
839 let mut g1 = Graph::with_vertices(2);
840 g1.add_edge(0, 1).unwrap();
841 let mut g2 = Graph::with_vertices(2);
842 g2.add_edge(0, 1).unwrap();
843
844 let p = lexicographic_product(&g1, &g2).unwrap();
845 assert_eq!(p.vcount(), 4);
846 assert_eq!(p.ecount(), 6);
848 }
849
850 #[test]
851 fn test_lexicographic_directed() {
852 let mut g1 = Graph::new(2, true).unwrap();
853 g1.add_edge(0, 1).unwrap();
854 let mut g2 = Graph::new(3, true).unwrap();
855 g2.add_edge(0, 1).unwrap();
856
857 let p = lexicographic_product(&g1, &g2).unwrap();
858 assert!(p.is_directed());
859 assert_eq!(p.vcount(), 6);
860 assert_eq!(p.ecount(), 11);
862 }
863
864 #[test]
865 fn test_lexicographic_both_edgeless() {
866 let g1 = Graph::with_vertices(3);
867 let g2 = Graph::with_vertices(4);
868
869 let p = lexicographic_product(&g1, &g2).unwrap();
870 assert_eq!(p.vcount(), 12);
871 assert_eq!(p.ecount(), 0);
872 }
873
874 #[test]
875 fn test_lexicographic_not_commutative() {
876 let mut g1 = Graph::with_vertices(2);
877 g1.add_edge(0, 1).unwrap();
878 let g2 = Graph::with_vertices(3);
879
880 let p1 = lexicographic_product(&g1, &g2).unwrap();
881 let p2 = lexicographic_product(&g2, &g1).unwrap();
882 assert_eq!(p1.ecount(), 9);
885 assert_eq!(p2.ecount(), 3);
886 assert_ne!(p1.ecount(), p2.ecount());
887 }
888
889 #[test]
892 fn test_rooted_p3_k2() {
893 let mut g1 = Graph::with_vertices(3);
895 g1.add_edge(0, 1).unwrap();
896 g1.add_edge(1, 2).unwrap();
897
898 let mut g2 = Graph::with_vertices(2);
899 g2.add_edge(0, 1).unwrap();
900
901 let p = rooted_product(&g1, &g2, 0).unwrap();
902 assert_eq!(p.vcount(), 6); assert_eq!(p.ecount(), 5);
905 }
906
907 #[test]
908 fn test_rooted_k3_p3() {
909 let mut g1 = Graph::with_vertices(3);
911 g1.add_edge(0, 1).unwrap();
912 g1.add_edge(1, 2).unwrap();
913 g1.add_edge(0, 2).unwrap();
914
915 let mut g2 = Graph::with_vertices(3);
916 g2.add_edge(0, 1).unwrap();
917 g2.add_edge(1, 2).unwrap();
918
919 let p = rooted_product(&g1, &g2, 1).unwrap();
920 assert_eq!(p.vcount(), 9); assert_eq!(p.ecount(), 9);
923 }
924
925 #[test]
926 fn test_rooted_single_vertex_g1() {
927 let g1 = Graph::with_vertices(1);
929 let mut g2 = Graph::with_vertices(2);
930 g2.add_edge(0, 1).unwrap();
931
932 let p = rooted_product(&g1, &g2, 0).unwrap();
933 assert_eq!(p.vcount(), 2); assert_eq!(p.ecount(), 1); }
936
937 #[test]
938 fn test_rooted_no_edges_g2() {
939 let mut g1 = Graph::with_vertices(3);
941 g1.add_edge(0, 1).unwrap();
942 g1.add_edge(1, 2).unwrap();
943
944 let g2 = Graph::with_vertices(2);
945
946 let p = rooted_product(&g1, &g2, 0).unwrap();
947 assert_eq!(p.vcount(), 6);
948 assert_eq!(p.ecount(), 2);
950 }
951
952 #[test]
953 fn test_rooted_no_edges_g1() {
954 let g1 = Graph::with_vertices(3);
956 let mut g2 = Graph::with_vertices(2);
957 g2.add_edge(0, 1).unwrap();
958
959 let p = rooted_product(&g1, &g2, 0).unwrap();
960 assert_eq!(p.vcount(), 6);
961 assert_eq!(p.ecount(), 3);
963 }
964
965 #[test]
966 fn test_rooted_directed() {
967 let mut g1 = Graph::new(2, true).unwrap();
968 g1.add_edge(0, 1).unwrap();
969
970 let mut g2 = Graph::new(2, true).unwrap();
971 g2.add_edge(0, 1).unwrap();
972
973 let p = rooted_product(&g1, &g2, 0).unwrap();
974 assert!(p.is_directed());
975 assert_eq!(p.vcount(), 4);
976 assert_eq!(p.ecount(), 3);
978 }
979
980 #[test]
981 fn test_rooted_invalid_root() {
982 let g1 = Graph::with_vertices(2);
983 let g2 = Graph::with_vertices(3);
984
985 assert!(rooted_product(&g1, &g2, 3).is_err());
986 assert!(rooted_product(&g1, &g2, 5).is_err());
987 }
988
989 #[test]
990 fn test_rooted_directedness_mismatch() {
991 let g1 = Graph::with_vertices(2);
992 let g2 = Graph::new(2, true).unwrap();
993
994 assert!(rooted_product(&g1, &g2, 0).is_err());
995 }
996
997 #[test]
998 fn test_rooted_empty_g2() {
999 let g1 = Graph::with_vertices(2);
1000 let g2 = Graph::with_vertices(0);
1001
1002 assert!(rooted_product(&g1, &g2, 0).is_err());
1003 }
1004
1005 #[test]
1008 fn test_modular_k2_k2() {
1009 let mut g1 = Graph::with_vertices(2);
1010 g1.add_edge(0, 1).unwrap();
1011 let mut g2 = Graph::with_vertices(2);
1012 g2.add_edge(0, 1).unwrap();
1013
1014 let p = modular_product(&g1, &g2).unwrap();
1015 assert_eq!(p.vcount(), 4);
1016 assert_eq!(p.ecount(), 2);
1020 }
1021
1022 #[test]
1023 fn test_modular_p3_p3() {
1024 let mut g1 = Graph::with_vertices(3);
1026 g1.add_edge(0, 1).unwrap();
1027 g1.add_edge(1, 2).unwrap();
1028 let mut g2 = Graph::with_vertices(3);
1029 g2.add_edge(0, 1).unwrap();
1030 g2.add_edge(1, 2).unwrap();
1031
1032 let p = modular_product(&g1, &g2).unwrap();
1033 assert_eq!(p.vcount(), 9);
1034 assert_eq!(p.ecount(), 10);
1039 }
1040
1041 #[test]
1042 fn test_modular_empty_graphs() {
1043 let g1 = Graph::with_vertices(0);
1044 let g2 = Graph::with_vertices(3);
1045 let p = modular_product(&g1, &g2).unwrap();
1046 assert_eq!(p.vcount(), 0);
1047 assert_eq!(p.ecount(), 0);
1048 }
1049
1050 #[test]
1051 fn test_modular_edgeless() {
1052 let g1 = Graph::with_vertices(3);
1055 let g2 = Graph::with_vertices(3);
1056
1057 let p = modular_product(&g1, &g2).unwrap();
1058 assert_eq!(p.vcount(), 9);
1059 assert_eq!(p.ecount(), 18);
1063 }
1064
1065 #[test]
1066 fn test_modular_complete_graphs() {
1067 let mut g1 = Graph::with_vertices(3);
1069 for i in 0..3u32 {
1070 for j in (i + 1)..3 {
1071 g1.add_edge(i, j).unwrap();
1072 }
1073 }
1074 let mut g2 = Graph::with_vertices(3);
1075 for i in 0..3u32 {
1076 for j in (i + 1)..3 {
1077 g2.add_edge(i, j).unwrap();
1078 }
1079 }
1080
1081 let p = modular_product(&g1, &g2).unwrap();
1082 assert_eq!(p.vcount(), 9);
1083 assert_eq!(p.ecount(), 18);
1087 }
1088
1089 #[test]
1090 fn test_modular_directed() {
1091 let mut g1 = Graph::new(2, true).unwrap();
1092 g1.add_edge(0, 1).unwrap();
1093 let mut g2 = Graph::new(2, true).unwrap();
1094 g2.add_edge(0, 1).unwrap();
1095
1096 let p = modular_product(&g1, &g2).unwrap();
1097 assert!(p.is_directed());
1098 assert_eq!(p.vcount(), 4);
1099 assert_eq!(p.ecount(), 2);
1104 }
1105
1106 #[test]
1107 fn test_modular_not_simple_error() {
1108 let mut g1 = Graph::with_vertices(2);
1110 g1.add_edge(0, 1).unwrap();
1111 g1.add_edge(0, 1).unwrap();
1112 let g2 = Graph::with_vertices(2);
1113
1114 assert!(modular_product(&g1, &g2).is_err());
1115 }
1116
1117 #[test]
1118 fn test_modular_self_loop_error() {
1119 let mut g1 = Graph::with_vertices(2);
1120 g1.add_edge(0, 0).unwrap();
1121 let g2 = Graph::with_vertices(2);
1122
1123 assert!(modular_product(&g1, &g2).is_err());
1124 }
1125
1126 #[test]
1127 fn test_modular_mixed_error() {
1128 let g1 = Graph::new(2, true).unwrap();
1129 let g2 = Graph::with_vertices(2);
1130 assert!(modular_product(&g1, &g2).is_err());
1131 }
1132
1133 #[test]
1136 fn test_graph_product_dispatcher() {
1137 let mut g1 = Graph::with_vertices(2);
1138 g1.add_edge(0, 1).unwrap();
1139 let mut g2 = Graph::with_vertices(2);
1140 g2.add_edge(0, 1).unwrap();
1141
1142 let p_c = graph_product(&g1, &g2, GraphProductType::Cartesian).unwrap();
1143 assert_eq!(p_c.ecount(), cartesian_product(&g1, &g2).unwrap().ecount());
1144
1145 let p_t = graph_product(&g1, &g2, GraphProductType::Tensor).unwrap();
1146 assert_eq!(p_t.ecount(), tensor_product(&g1, &g2).unwrap().ecount());
1147
1148 let p_s = graph_product(&g1, &g2, GraphProductType::Strong).unwrap();
1149 assert_eq!(p_s.ecount(), strong_product(&g1, &g2).unwrap().ecount());
1150
1151 let p_l = graph_product(&g1, &g2, GraphProductType::Lexicographic).unwrap();
1152 assert_eq!(
1153 p_l.ecount(),
1154 lexicographic_product(&g1, &g2).unwrap().ecount()
1155 );
1156
1157 let p_m = graph_product(&g1, &g2, GraphProductType::Modular).unwrap();
1158 assert_eq!(p_m.ecount(), modular_product(&g1, &g2).unwrap().ecount());
1159 }
1160
1161 #[test]
1164 fn test_product_overflow() {
1165 let g1 = Graph::with_vertices(70000);
1167 let g2 = Graph::with_vertices(70000);
1168 assert!(cartesian_product(&g1, &g2).is_err());
1169 assert!(tensor_product(&g1, &g2).is_err());
1170 assert!(strong_product(&g1, &g2).is_err());
1171 assert!(lexicographic_product(&g1, &g2).is_err());
1172 assert!(rooted_product(&g1, &g2, 0).is_err());
1173 }
1174}