use crate::algorithms::connectivity::components::connected_components;
use crate::core::{Graph, IgraphResult};
pub fn is_cluster_graph(graph: &Graph) -> IgraphResult<bool> {
if graph.is_directed() {
return Ok(false);
}
let n = graph.vcount();
if n == 0 {
return Ok(true);
}
let cc = connected_components(graph)?;
let comp_count = cc.count as usize;
let mut comp_verts = vec![0u64; comp_count];
let mut comp_edges = vec![0u64; comp_count];
for v in 0..n {
let c = cc.membership[v as usize] as usize;
comp_verts[c] = comp_verts[c].saturating_add(1);
}
let ecount = graph.ecount();
for eid in 0..ecount {
let eid_u32 = u32::try_from(eid).unwrap_or(u32::MAX);
let (u, v) = graph.edge(eid_u32)?;
if u == v {
return Ok(false);
}
let c = cc.membership[u as usize] as usize;
comp_edges[c] = comp_edges[c].saturating_add(1);
}
for c in 0..comp_count {
let k = comp_verts[c];
let expected = k.saturating_mul(k.saturating_sub(1)) / 2;
if comp_edges[c] != expected {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn edgeless_graph() {
let g = Graph::with_vertices(5);
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn triangle() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn k4() {
let mut g = Graph::with_vertices(4);
for u in 0..4u32 {
for v in (u + 1)..4 {
g.add_edge(u, v).unwrap();
}
}
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn k3_plus_k2_plus_isolated() {
let mut g = Graph::with_vertices(6);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
g.add_edge(3, 4).unwrap();
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn two_disjoint_k3() {
let mut g = Graph::with_vertices(6);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
g.add_edge(3, 4).unwrap();
g.add_edge(4, 5).unwrap();
g.add_edge(5, 3).unwrap();
assert!(is_cluster_graph(&g).unwrap());
}
#[test]
fn path_3_not_cluster() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn path_4_not_cluster() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn star_not_cluster() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn cycle_4_not_cluster() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 0).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn k4_minus_edge_not_cluster() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(1, 3).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn directed_returns_false() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn self_loop_returns_false() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
#[test]
fn parallel_edges_returns_false() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
assert!(!is_cluster_graph(&g).unwrap());
}
}