use crate::algorithms::connectivity::connected_components;
use crate::algorithms::operators::complementer::complementer;
use crate::algorithms::properties::is_simple::is_simple;
use crate::core::{Graph, IgraphResult};
pub fn is_complete_multipartite(graph: &Graph) -> IgraphResult<Option<Vec<u32>>> {
if graph.is_directed() {
return Ok(None);
}
let n = graph.vcount();
if n == 0 {
return Ok(Some(vec![]));
}
if !is_simple(graph)? {
return Ok(None);
}
if graph.ecount() == 0 {
return Ok(Some(vec![n]));
}
let comp = complementer(graph, false)?;
let comps = connected_components(&comp)?;
let membership = &comps.membership;
let num_comps = comps.count as usize;
let mut parts: Vec<Vec<u32>> = vec![vec![]; num_comps];
for (v, &comp_id) in membership.iter().enumerate() {
parts[comp_id as usize].push(u32::try_from(v).unwrap_or(u32::MAX));
}
for part in &parts {
if part.len() > 1 {
let subgraph_comp = is_clique_in_graph(&comp, part)?;
if !subgraph_comp {
return Ok(None);
}
}
}
let mut expected_edges: u64 = 0;
let part_sizes: Vec<u32> = parts
.iter()
.map(|p| u32::try_from(p.len()).unwrap_or(u32::MAX))
.collect();
let total_n = u64::from(n);
for &size in &part_sizes {
let s = u64::from(size);
expected_edges = expected_edges.saturating_add(s.saturating_mul(total_n.saturating_sub(s)));
}
expected_edges /= 2;
if graph.ecount() as u64 != expected_edges {
return Ok(None);
}
let mut result: Vec<u32> = part_sizes;
result.sort_unstable();
Ok(Some(result))
}
fn is_clique_in_graph(graph: &Graph, vertices: &[u32]) -> IgraphResult<bool> {
let k = vertices.len();
if k <= 1 {
return Ok(true);
}
for (i, &vi) in vertices.iter().enumerate() {
let nbrs = graph.neighbors(vi)?;
let count = vertices
.iter()
.enumerate()
.filter(|&(j, &vj)| i != j && nbrs.contains(&vj))
.count();
if count != k - 1 {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![]));
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1]));
}
#[test]
fn edgeless_3() {
let g = Graph::with_vertices(3);
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![3]));
}
#[test]
fn single_edge_k11() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 1]));
}
#[test]
fn triangle_k111() {
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_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 1, 1]));
}
#[test]
fn k23() {
let mut g = Graph::with_vertices(5);
for i in 0..2u32 {
for j in 2..5u32 {
g.add_edge(i, j).unwrap();
}
}
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 3]));
}
#[test]
fn k22() {
let mut g = Graph::with_vertices(4);
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_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2]));
}
#[test]
fn k_1_1_1_1() {
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();
g.add_edge(2, 3).unwrap();
assert_eq!(
is_complete_multipartite(&g).unwrap(),
Some(vec![1, 1, 1, 1])
);
}
#[test]
fn star_k13() {
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_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 3]));
}
#[test]
fn k_2_2_2() {
let mut g = Graph::with_vertices(6);
let parts = [vec![0u32, 1], vec![2, 3], vec![4, 5]];
for i in 0..3 {
for j in (i + 1)..3 {
for &u in &parts[i] {
for &v in &parts[j] {
g.add_edge(u, v).unwrap();
}
}
}
}
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2, 2]));
}
#[test]
fn path_p3_is_k12() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 2]));
}
#[test]
fn path_p4_not_multipartite() {
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_eq!(is_complete_multipartite(&g).unwrap(), None);
}
#[test]
fn cycle_c4_is_k22() {
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_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2]));
}
#[test]
fn cycle_c5_not_multipartite() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
g.add_edge(4, 0).unwrap();
assert_eq!(is_complete_multipartite(&g).unwrap(), None);
}
#[test]
fn directed_returns_none() {
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_eq!(is_complete_multipartite(&g).unwrap(), None);
}
#[test]
fn disconnected_not_multipartite() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
assert_eq!(is_complete_multipartite(&g).unwrap(), None);
}
#[test]
fn petersen_not_multipartite() {
let mut g = Graph::with_vertices(10);
for i in 0..5u32 {
g.add_edge(i, (i + 1) % 5).unwrap();
}
for i in 0..5u32 {
g.add_edge(i + 5, 5 + (i + 2) % 5).unwrap();
}
for i in 0..5u32 {
g.add_edge(i, i + 5).unwrap();
}
assert_eq!(is_complete_multipartite(&g).unwrap(), None);
}
}