use std::collections::{HashMap, VecDeque};
use crate::graph::capability::{Bigraph, StableNode};
use crate::graph::Graph;
pub fn is_bipartite<G>(graph: &G) -> bool
where
G: Graph + Bigraph + ?Sized,
G::Endpoints: for<'scope> crate::graph::edge::Map<
crate::graph::context::NodeIx<'scope, G::NodeIx>,
Mapped = [crate::graph::context::NodeIx<'scope, G::NodeIx>; 2],
>,
{
graph.scope(|ctx| bipartite_coloring(ctx).is_some())
}
pub fn bipartite_coloring<G>(graph: &G) -> Option<HashMap<G::NodeIx, bool>>
where
G: Graph + Bigraph + StableNode + ?Sized,
{
let mut color: HashMap<G::NodeIx, bool> = HashMap::new();
for start in <_ as crate::graph::GraphOperation<'_>>::node_indices(graph) {
if color.contains_key(&start) {
continue;
}
color.insert(start, false);
let mut queue = VecDeque::new();
queue.push_back(start);
while let Some(node) = queue.pop_front() {
let node_color = color[&node];
for eix in unsafe { <G as crate::graph::GraphOperation<'_>>::edge_indices_of_unchecked(graph, node) } {
let mut found_other = false;
for endpoint in unsafe { <G as crate::graph::GraphOperation<'_>>::endpoints_unchecked(graph, eix) } {
if endpoint == node {
continue;
}
found_other = true;
let neighbor = endpoint;
if let Some(&existing_color) = color.get(&neighbor) {
if existing_color == node_color {
return None; }
} else {
color.insert(neighbor, !node_color);
queue.push_back(neighbor);
}
}
if !found_other {
return None;
}
}
}
}
Some(color)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BTreeGraph;
#[test]
fn bipartite_even_cycle() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("2->3", [2, 3]).unwrap();
g.insert_edge("3->0", [3, 0]).unwrap();
assert!(is_bipartite(&g));
let coloring = bipartite_coloring(&g).unwrap();
assert_eq!(coloring.len(), 4);
assert_ne!(coloring[&0], coloring[&1]);
assert_ne!(coloring[&1], coloring[&2]);
assert_ne!(coloring[&2], coloring[&3]);
}
#[test]
fn not_bipartite_odd_cycle() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("1->2", [1, 2]).unwrap();
g.insert_edge("2->0", [2, 0]).unwrap();
assert!(!is_bipartite(&g));
assert!(bipartite_coloring(&g).is_none());
}
#[test]
fn bipartite_tree() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("0->2", [0, 2]).unwrap();
g.insert_edge("1->3", [1, 3]).unwrap();
assert!(is_bipartite(&g));
}
#[test]
fn bipartite_disconnected() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_node(1).unwrap();
g.insert_node(2).unwrap();
g.insert_node(3).unwrap();
g.insert_edge("0->1", [0, 1]).unwrap();
g.insert_edge("2->3", [2, 3]).unwrap();
assert!(is_bipartite(&g));
}
#[test]
fn bipartite_empty() {
let g = BTreeGraph::<u32, &str>::default();
assert!(is_bipartite(&g));
}
#[test]
fn not_bipartite_self_loop() {
let mut g = BTreeGraph::<_, _>::default();
g.insert_node(0).unwrap();
g.insert_edge("0->0", [0, 0]).unwrap();
assert!(!is_bipartite(&g));
}
}