use std::collections::VecDeque;
use crate::core::{Graph, IgraphResult, VertexId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BipartiteResult {
pub is_bipartite: bool,
pub types: Vec<bool>,
}
pub fn is_bipartite(graph: &Graph) -> IgraphResult<BipartiteResult> {
let n = graph.vcount();
if n == 0 {
return Ok(BipartiteResult {
is_bipartite: true,
types: Vec::new(),
});
}
let mut color: Vec<u8> = vec![0; n as usize];
let mut queue: VecDeque<VertexId> = VecDeque::new();
let mut bipartite = true;
'outer: for start in 0..n {
if color[start as usize] != 0 {
continue;
}
color[start as usize] = 1;
queue.push_back(start);
while let Some(v) = queue.pop_front() {
let v_color = color[v as usize];
let neighbors = get_all_neighbors(graph, v)?;
for nei in neighbors {
if color[nei as usize] == 0 {
color[nei as usize] = 3 - v_color;
queue.push_back(nei);
} else if color[nei as usize] == v_color {
bipartite = false;
break 'outer;
}
}
}
}
if bipartite {
let types: Vec<bool> = color.iter().map(|&c| c == 2).collect();
Ok(BipartiteResult {
is_bipartite: true,
types,
})
} else {
Ok(BipartiteResult {
is_bipartite: false,
types: Vec::new(),
})
}
}
fn get_all_neighbors(graph: &Graph, v: VertexId) -> IgraphResult<Vec<VertexId>> {
let mut neighbors = Vec::new();
let out_edges = graph.incident(v)?;
for eid in &out_edges {
let (from, to) = graph.edge(*eid)?;
let nei = if from == v { to } else { from };
neighbors.push(nei);
}
if graph.is_directed() {
let in_edges = graph.incident_in(v)?;
for eid in &in_edges {
if !out_edges.contains(eid) {
let (from, to) = graph.edge(*eid)?;
let nei = if from == v { to } else { from };
neighbors.push(nei);
}
}
}
Ok(neighbors)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert!(result.types.is_empty());
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert_eq!(result.types.len(), 1);
}
#[test]
fn isolated_vertices() {
let g = Graph::with_vertices(5);
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert_eq!(result.types.len(), 5);
}
#[test]
fn single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert_ne!(result.types[0], result.types[1]);
}
#[test]
fn path_graph() {
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();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
for eid in 0..g.ecount() {
#[allow(clippy::cast_possible_truncation)]
let (from, to) = g.edge(eid as u32).unwrap();
assert_ne!(
result.types[from as usize], result.types[to as usize],
"edge ({from}, {to}) violates bipartiteness"
);
}
}
#[test]
fn even_cycle() {
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();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
}
#[test]
fn odd_cycle_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();
let result = is_bipartite(&g).unwrap();
assert!(!result.is_bipartite);
assert!(result.types.is_empty());
}
#[test]
fn odd_cycle_5() {
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();
let result = is_bipartite(&g).unwrap();
assert!(!result.is_bipartite);
}
#[test]
fn complete_bipartite_k23() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(0, 4).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(1, 3).unwrap();
g.add_edge(1, 4).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert_eq!(result.types[0], result.types[1]);
assert_eq!(result.types[2], result.types[3]);
assert_eq!(result.types[3], result.types[4]);
assert_ne!(result.types[0], result.types[2]);
}
#[test]
fn disconnected_bipartite() {
let mut g = Graph::with_vertices(6);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(3, 4).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
}
#[test]
fn disconnected_not_bipartite() {
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();
let result = is_bipartite(&g).unwrap();
assert!(!result.is_bipartite);
}
#[test]
fn self_loop_not_bipartite() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(!result.is_bipartite);
}
#[test]
fn directed_ignores_direction() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(2, 1).unwrap();
g.add_edge(2, 3).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
}
#[test]
fn k4_not_bipartite() {
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();
let result = is_bipartite(&g).unwrap();
assert!(!result.is_bipartite);
}
#[test]
fn star_bipartite() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(0, 4).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
assert_ne!(result.types[0], result.types[1]);
assert_eq!(result.types[1], result.types[2]);
assert_eq!(result.types[2], result.types[3]);
assert_eq!(result.types[3], result.types[4]);
}
#[test]
fn cube_bipartite() {
let mut g = Graph::with_vertices(8);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(0, 4).unwrap();
g.add_edge(1, 3).unwrap();
g.add_edge(1, 5).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(2, 6).unwrap();
g.add_edge(3, 7).unwrap();
g.add_edge(4, 5).unwrap();
g.add_edge(4, 6).unwrap();
g.add_edge(5, 7).unwrap();
g.add_edge(6, 7).unwrap();
let result = is_bipartite(&g).unwrap();
assert!(result.is_bipartite);
}
}