use crate::core::error::IgraphError;
use crate::core::{Graph, IgraphResult, VertexId};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphProductType {
Cartesian,
Lexicographic,
Strong,
Tensor,
Modular,
}
pub fn graph_product(
g1: &Graph,
g2: &Graph,
product_type: GraphProductType,
) -> IgraphResult<Graph> {
match product_type {
GraphProductType::Cartesian => cartesian_product(g1, g2),
GraphProductType::Lexicographic => lexicographic_product(g1, g2),
GraphProductType::Strong => strong_product(g1, g2),
GraphProductType::Tensor => tensor_product(g1, g2),
GraphProductType::Modular => modular_product(g1, g2),
}
}
pub fn cartesian_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
check_same_directedness(g1, g2, "cartesian_product")?;
let n1 = g1.vcount();
let n2 = g2.vcount();
let directed = g1.is_directed();
let n = product_vertex_count(n1, n2)?;
if n == 0 {
return Graph::new(0, directed);
}
let e1 = g1.ecount();
let e2 = g2.ecount();
let total_edges = (n1 as usize)
.checked_mul(e2)
.and_then(|a| a.checked_add((n2 as usize).checked_mul(e1)?))
.ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in cartesian_product".to_string())
})?;
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
for eid in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g1.edge(eid as u32)?;
for j in 0..n2 {
let src = u * n2 + j;
let tgt = v * n2 + j;
edges.push((src, tgt));
}
}
for eid in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g2.edge(eid as u32)?;
for i in 0..n1 {
let src = i * n2 + u;
let tgt = i * n2 + v;
edges.push((src, tgt));
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn tensor_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
check_same_directedness(g1, g2, "tensor_product")?;
let n1 = g1.vcount();
let n2 = g2.vcount();
let directed = g1.is_directed();
let n = product_vertex_count(n1, n2)?;
if n == 0 {
return Graph::new(0, directed);
}
let e1 = g1.ecount();
let e2 = g2.ecount();
let multiplier: usize = if directed { 1 } else { 2 };
let total_edges = e1
.checked_mul(e2)
.and_then(|a| a.checked_mul(multiplier))
.ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in tensor_product".to_string())
})?;
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
for eid1 in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let (u1, v1) = g1.edge(eid1 as u32)?;
for eid2 in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let (u2, v2) = g2.edge(eid2 as u32)?;
let src = u1 * n2 + u2;
let tgt = v1 * n2 + v2;
edges.push((src, tgt));
if !directed {
let src2 = u1 * n2 + v2;
let tgt2 = v1 * n2 + u2;
edges.push((src2, tgt2));
}
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn strong_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
check_same_directedness(g1, g2, "strong_product")?;
let n1 = g1.vcount();
let n2 = g2.vcount();
let directed = g1.is_directed();
let n = product_vertex_count(n1, n2)?;
if n == 0 {
return Graph::new(0, directed);
}
let e1 = g1.ecount();
let e2 = g2.ecount();
let multiplier: usize = if directed { 1 } else { 2 };
let cartesian_count = (n1 as usize) * e2 + (n2 as usize) * e1;
let tensor_count = e1 * e2 * multiplier;
let total_edges = cartesian_count.checked_add(tensor_count).ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in strong_product".to_string())
})?;
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
for eid in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g1.edge(eid as u32)?;
for j in 0..n2 {
edges.push((u * n2 + j, v * n2 + j));
}
}
for eid in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g2.edge(eid as u32)?;
for i in 0..n1 {
edges.push((i * n2 + u, i * n2 + v));
}
}
for eid1 in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let (u1, v1) = g1.edge(eid1 as u32)?;
for eid2 in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let (u2, v2) = g2.edge(eid2 as u32)?;
edges.push((u1 * n2 + u2, v1 * n2 + v2));
if !directed {
edges.push((u1 * n2 + v2, v1 * n2 + u2));
}
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn lexicographic_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
check_same_directedness(g1, g2, "lexicographic_product")?;
let n1 = g1.vcount();
let n2 = g2.vcount();
let directed = g1.is_directed();
let n = product_vertex_count(n1, n2)?;
if n == 0 {
return Graph::new(0, directed);
}
let e1 = g1.ecount();
let e2 = g2.ecount();
let g2_part = (n1 as usize) * e2;
let pairs_per_edge: usize = if directed {
(n2 as usize) * (n2 as usize)
} else {
(n2 as usize) * (n2 as usize)
};
let g1_part = e1.checked_mul(pairs_per_edge).ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in lexicographic_product".to_string())
})?;
let total_edges = g2_part.checked_add(g1_part).ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in lexicographic_product".to_string())
})?;
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
for eid in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g2.edge(eid as u32)?;
for i in 0..n1 {
edges.push((i * n2 + u, i * n2 + v));
}
}
for eid in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let (u, v) = g1.edge(eid as u32)?;
for j in 0..n2 {
for l in 0..n2 {
edges.push((u * n2 + j, v * n2 + l));
}
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn rooted_product(g1: &Graph, g2: &Graph, root: u32) -> IgraphResult<Graph> {
check_same_directedness(g1, g2, "rooted_product")?;
let n1 = g1.vcount();
let n2 = g2.vcount();
if n2 == 0 || root >= n2 {
return Err(IgraphError::InvalidArgument(
"root vertex is not present in the second graph".to_string(),
));
}
let directed = g1.is_directed();
let n = product_vertex_count(n1, n2)?;
if n == 0 {
return Graph::new(0, directed);
}
let e1 = g1.ecount();
let e2 = g2.ecount();
let total_edges = (n1 as usize)
.checked_mul(e2)
.and_then(|v| v.checked_add(e1))
.ok_or_else(|| {
IgraphError::InvalidArgument("edge count overflow in rooted_product".to_string())
})?;
let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(total_edges);
for eid_usize in 0..e1 {
#[allow(clippy::cast_possible_truncation)]
let eid = eid_usize as u32;
let (from, to) = g1.edge(eid)?;
let new_from = from * n2 + root;
let new_to = to * n2 + root;
edges.push((new_from, new_to));
}
for eid_usize in 0..e2 {
#[allow(clippy::cast_possible_truncation)]
let eid = eid_usize as u32;
let (from, to) = g2.edge(eid)?;
for j in 0..n1 {
let new_from = j * n2 + from;
let new_to = j * n2 + to;
edges.push((new_from, new_to));
}
}
let mut result = Graph::new(n, directed)?;
result.add_edges(edges)?;
Ok(result)
}
pub fn modular_product(g1: &Graph, g2: &Graph) -> IgraphResult<Graph> {
use crate::algorithms::operators::complementer::complementer;
use crate::algorithms::operators::union::union;
use crate::algorithms::properties::is_simple::is_simple;
check_same_directedness(g1, g2, "modular_product")?;
let simple1 = is_simple(g1)?;
let simple2 = is_simple(g2)?;
if !simple1 || !simple2 {
return Err(IgraphError::InvalidArgument(
"modular product requires simple graphs as input".to_string(),
));
}
let n1 = g1.vcount();
let n2 = g2.vcount();
if n1 == 0 || n2 == 0 {
let directed = g1.is_directed();
return Graph::new(0, directed);
}
let g1_compl = complementer(g1, false)?;
let g2_compl = complementer(g2, false)?;
let tp_orig = tensor_product(g1, g2)?;
let tp_compl = tensor_product(&g1_compl, &g2_compl)?;
union(&tp_orig, &tp_compl)
}
fn check_same_directedness(g1: &Graph, g2: &Graph, op: &str) -> IgraphResult<()> {
if g1.is_directed() != g2.is_directed() {
return Err(IgraphError::InvalidArgument(format!(
"cannot compute {op} of directed and undirected graphs"
)));
}
Ok(())
}
fn product_vertex_count(n1: u32, n2: u32) -> IgraphResult<u32> {
let count = u64::from(n1) * u64::from(n2);
u32::try_from(count).map_err(|_| {
IgraphError::InvalidArgument("product vertex count exceeds u32::MAX".to_string())
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cartesian_k2_k2() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = cartesian_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 4);
}
#[test]
fn test_cartesian_k2_k3() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(3);
g2.add_edge(0, 1).unwrap();
g2.add_edge(1, 2).unwrap();
g2.add_edge(0, 2).unwrap();
let p = cartesian_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 9);
}
#[test]
fn test_cartesian_empty_graph() {
let g1 = Graph::with_vertices(0);
let g2 = Graph::with_vertices(3);
let p = cartesian_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 0);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_cartesian_isolated_vertices() {
let g1 = Graph::with_vertices(3);
let g2 = Graph::with_vertices(4);
let p = cartesian_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 12);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_cartesian_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(2, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = cartesian_product(&g1, &g2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 4);
}
#[test]
fn test_cartesian_mixed_error() {
let g1 = Graph::new(2, true).unwrap();
let g2 = Graph::with_vertices(2);
assert!(cartesian_product(&g1, &g2).is_err());
}
#[test]
fn test_tensor_k2_k2() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = tensor_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_tensor_path_k2() {
let mut g1 = Graph::with_vertices(3);
g1.add_edge(0, 1).unwrap();
g1.add_edge(1, 2).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = tensor_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 4);
}
#[test]
fn test_tensor_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(2, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = tensor_product(&g1, &g2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 1);
assert_eq!(p.edge(0).unwrap(), (0, 3)); }
#[test]
fn test_tensor_no_edges() {
let g1 = Graph::with_vertices(3);
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = tensor_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_tensor_empty() {
let g1 = Graph::with_vertices(0);
let g2 = Graph::with_vertices(5);
let p = tensor_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 0);
}
#[test]
fn test_strong_k2_k2() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = strong_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 6);
}
#[test]
fn test_strong_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(2, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = strong_product(&g1, &g2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 5);
}
#[test]
fn test_strong_one_edgeless() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let g2 = Graph::with_vertices(3);
let p = strong_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 3);
}
#[test]
fn test_lexicographic_k2_isolated() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let g2 = Graph::with_vertices(3);
let p = lexicographic_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 9);
}
#[test]
fn test_lexicographic_k2_k2() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = lexicographic_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 6);
}
#[test]
fn test_lexicographic_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(3, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = lexicographic_product(&g1, &g2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 11);
}
#[test]
fn test_lexicographic_both_edgeless() {
let g1 = Graph::with_vertices(3);
let g2 = Graph::with_vertices(4);
let p = lexicographic_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 12);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_lexicographic_not_commutative() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let g2 = Graph::with_vertices(3);
let p1 = lexicographic_product(&g1, &g2).unwrap();
let p2 = lexicographic_product(&g2, &g1).unwrap();
assert_eq!(p1.ecount(), 9);
assert_eq!(p2.ecount(), 3);
assert_ne!(p1.ecount(), p2.ecount());
}
#[test]
fn test_rooted_p3_k2() {
let mut g1 = Graph::with_vertices(3);
g1.add_edge(0, 1).unwrap();
g1.add_edge(1, 2).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = rooted_product(&g1, &g2, 0).unwrap();
assert_eq!(p.vcount(), 6); assert_eq!(p.ecount(), 5);
}
#[test]
fn test_rooted_k3_p3() {
let mut g1 = Graph::with_vertices(3);
g1.add_edge(0, 1).unwrap();
g1.add_edge(1, 2).unwrap();
g1.add_edge(0, 2).unwrap();
let mut g2 = Graph::with_vertices(3);
g2.add_edge(0, 1).unwrap();
g2.add_edge(1, 2).unwrap();
let p = rooted_product(&g1, &g2, 1).unwrap();
assert_eq!(p.vcount(), 9); assert_eq!(p.ecount(), 9);
}
#[test]
fn test_rooted_single_vertex_g1() {
let g1 = Graph::with_vertices(1);
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = rooted_product(&g1, &g2, 0).unwrap();
assert_eq!(p.vcount(), 2); assert_eq!(p.ecount(), 1); }
#[test]
fn test_rooted_no_edges_g2() {
let mut g1 = Graph::with_vertices(3);
g1.add_edge(0, 1).unwrap();
g1.add_edge(1, 2).unwrap();
let g2 = Graph::with_vertices(2);
let p = rooted_product(&g1, &g2, 0).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_rooted_no_edges_g1() {
let g1 = Graph::with_vertices(3);
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = rooted_product(&g1, &g2, 0).unwrap();
assert_eq!(p.vcount(), 6);
assert_eq!(p.ecount(), 3);
}
#[test]
fn test_rooted_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(2, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = rooted_product(&g1, &g2, 0).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 3);
}
#[test]
fn test_rooted_invalid_root() {
let g1 = Graph::with_vertices(2);
let g2 = Graph::with_vertices(3);
assert!(rooted_product(&g1, &g2, 3).is_err());
assert!(rooted_product(&g1, &g2, 5).is_err());
}
#[test]
fn test_rooted_directedness_mismatch() {
let g1 = Graph::with_vertices(2);
let g2 = Graph::new(2, true).unwrap();
assert!(rooted_product(&g1, &g2, 0).is_err());
}
#[test]
fn test_rooted_empty_g2() {
let g1 = Graph::with_vertices(2);
let g2 = Graph::with_vertices(0);
assert!(rooted_product(&g1, &g2, 0).is_err());
}
#[test]
fn test_modular_k2_k2() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p = modular_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_modular_p3_p3() {
let mut g1 = Graph::with_vertices(3);
g1.add_edge(0, 1).unwrap();
g1.add_edge(1, 2).unwrap();
let mut g2 = Graph::with_vertices(3);
g2.add_edge(0, 1).unwrap();
g2.add_edge(1, 2).unwrap();
let p = modular_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 9);
assert_eq!(p.ecount(), 10);
}
#[test]
fn test_modular_empty_graphs() {
let g1 = Graph::with_vertices(0);
let g2 = Graph::with_vertices(3);
let p = modular_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 0);
assert_eq!(p.ecount(), 0);
}
#[test]
fn test_modular_edgeless() {
let g1 = Graph::with_vertices(3);
let g2 = Graph::with_vertices(3);
let p = modular_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 9);
assert_eq!(p.ecount(), 18);
}
#[test]
fn test_modular_complete_graphs() {
let mut g1 = Graph::with_vertices(3);
for i in 0..3u32 {
for j in (i + 1)..3 {
g1.add_edge(i, j).unwrap();
}
}
let mut g2 = Graph::with_vertices(3);
for i in 0..3u32 {
for j in (i + 1)..3 {
g2.add_edge(i, j).unwrap();
}
}
let p = modular_product(&g1, &g2).unwrap();
assert_eq!(p.vcount(), 9);
assert_eq!(p.ecount(), 18);
}
#[test]
fn test_modular_directed() {
let mut g1 = Graph::new(2, true).unwrap();
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::new(2, true).unwrap();
g2.add_edge(0, 1).unwrap();
let p = modular_product(&g1, &g2).unwrap();
assert!(p.is_directed());
assert_eq!(p.vcount(), 4);
assert_eq!(p.ecount(), 2);
}
#[test]
fn test_modular_not_simple_error() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
g1.add_edge(0, 1).unwrap();
let g2 = Graph::with_vertices(2);
assert!(modular_product(&g1, &g2).is_err());
}
#[test]
fn test_modular_self_loop_error() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 0).unwrap();
let g2 = Graph::with_vertices(2);
assert!(modular_product(&g1, &g2).is_err());
}
#[test]
fn test_modular_mixed_error() {
let g1 = Graph::new(2, true).unwrap();
let g2 = Graph::with_vertices(2);
assert!(modular_product(&g1, &g2).is_err());
}
#[test]
fn test_graph_product_dispatcher() {
let mut g1 = Graph::with_vertices(2);
g1.add_edge(0, 1).unwrap();
let mut g2 = Graph::with_vertices(2);
g2.add_edge(0, 1).unwrap();
let p_c = graph_product(&g1, &g2, GraphProductType::Cartesian).unwrap();
assert_eq!(p_c.ecount(), cartesian_product(&g1, &g2).unwrap().ecount());
let p_t = graph_product(&g1, &g2, GraphProductType::Tensor).unwrap();
assert_eq!(p_t.ecount(), tensor_product(&g1, &g2).unwrap().ecount());
let p_s = graph_product(&g1, &g2, GraphProductType::Strong).unwrap();
assert_eq!(p_s.ecount(), strong_product(&g1, &g2).unwrap().ecount());
let p_l = graph_product(&g1, &g2, GraphProductType::Lexicographic).unwrap();
assert_eq!(
p_l.ecount(),
lexicographic_product(&g1, &g2).unwrap().ecount()
);
let p_m = graph_product(&g1, &g2, GraphProductType::Modular).unwrap();
assert_eq!(p_m.ecount(), modular_product(&g1, &g2).unwrap().ecount());
}
#[test]
fn test_product_overflow() {
let g1 = Graph::with_vertices(70000);
let g2 = Graph::with_vertices(70000);
assert!(cartesian_product(&g1, &g2).is_err());
assert!(tensor_product(&g1, &g2).is_err());
assert!(strong_product(&g1, &g2).is_err());
assert!(lexicographic_product(&g1, &g2).is_err());
assert!(rooted_product(&g1, &g2, 0).is_err());
}
}