use std::collections::HashSet;
use crate::core::Graph;
use crate::core::IgraphResult;
use crate::core::graph::VertexId;
use super::is_simple::{SimpleMode, is_simple_with_mode};
pub fn is_complete(graph: &Graph) -> IgraphResult<bool> {
let n = graph.vcount();
if n <= 1 {
return Ok(true);
}
let n64 = u64::from(n);
let m64 = graph.ecount() as u64;
let directed = graph.is_directed();
let target_undir_x2 = n64.checked_mul(n64 - 1).expect("u64 mul fits");
let target = if directed {
target_undir_x2
} else {
target_undir_x2 / 2
};
if m64 < target {
return Ok(false);
}
if is_simple_with_mode(graph, SimpleMode::DirectedAsDirected)? {
return Ok(m64 == target);
}
let n_us = n as usize;
let mut seen: HashSet<VertexId> = HashSet::with_capacity(n_us);
for v in 0..n {
seen.clear();
if directed {
let eids = graph.incident(v).expect("incident on valid vertex");
for eid in eids {
let nei = graph.edge_other(eid, v).expect("edge_other on valid edge");
if nei != v {
seen.insert(nei);
}
}
} else {
let neis = graph.neighbors(v).expect("neighbors on valid vertex");
for nei in neis {
if nei != v {
seen.insert(nei);
}
}
}
if seen.len() + 1 < n_us {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::Graph;
#[test]
fn null_graph_is_complete() {
let g = Graph::with_vertices(0);
assert!(is_complete(&g).unwrap());
}
#[test]
fn null_directed_is_complete() {
let g = Graph::new(0, true).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn singleton_undirected_is_complete() {
let g = Graph::with_vertices(1);
assert!(is_complete(&g).unwrap());
}
#[test]
fn singleton_directed_is_complete() {
let g = Graph::new(1, true).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn singleton_with_self_loop_is_complete() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn k2_undirected_is_complete() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn k3_undirected_is_complete() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0u32, 1u32), (0, 2), (1, 2)]).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn k4_undirected_is_complete() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0u32, 1u32), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)])
.unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn path_is_not_complete() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0u32, 1u32), (1, 2)]).unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn missing_one_edge_is_not_complete() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0u32, 1u32), (0, 2), (0, 3), (1, 2), (1, 3)])
.unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn k3_directed_needs_both_directions() {
let mut g = Graph::new(3, true).unwrap();
g.add_edges(vec![(0u32, 1u32), (1, 2), (2, 0)]).unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn k3_directed_complete() {
let mut g = Graph::new(3, true).unwrap();
g.add_edges(vec![(0u32, 1u32), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)])
.unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn k2_directed_not_complete_with_only_one_arc() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn k2_directed_complete_with_both_arcs() {
let mut g = Graph::new(2, true).unwrap();
g.add_edges(vec![(0u32, 1u32), (1, 0)]).unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn complete_with_extra_self_loops_still_complete() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0u32, 1u32), (0, 2), (1, 2), (0, 0)])
.unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn complete_with_parallel_edges_still_complete() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0u32, 1u32), (0, 2), (1, 2), (0, 1)])
.unwrap();
assert!(is_complete(&g).unwrap());
}
#[test]
fn parallel_edges_padding_does_not_help_missing_pair() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0u32, 1u32), (0, 2), (0, 3), (1, 2), (1, 2), (1, 2)])
.unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn ecount_too_low_short_circuit() {
let mut g = Graph::with_vertices(10);
g.add_edge(0, 1).unwrap();
assert!(!is_complete(&g).unwrap());
}
#[test]
fn empty_n2_is_not_complete() {
let g = Graph::with_vertices(2);
assert!(!is_complete(&g).unwrap());
}
#[test]
fn multi_edge_padding_to_target_but_missing_pair() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0u32, 1u32), (0, 1), (0, 1)]).unwrap();
assert!(!is_complete(&g).unwrap());
}
}