use crate::core::{Graph, IgraphResult, VertexId};
pub fn is_vertex_cover(graph: &Graph, cover: &[VertexId]) -> bool {
let n = graph.vcount() as usize;
let mut in_cover = vec![false; n];
for &v in cover {
if (v as usize) < n {
in_cover[v as usize] = true;
}
}
let ecount = graph.ecount();
#[allow(clippy::cast_possible_truncation)] for eid in 0..ecount as u32 {
if let Ok((u, v)) = graph.edge(eid) {
if !in_cover[u as usize] && !in_cover[v as usize] {
return false;
}
}
}
true
}
#[allow(clippy::cast_possible_truncation)] pub fn minimum_vertex_cover(graph: &Graph) -> IgraphResult<Vec<VertexId>> {
let n = graph.vcount();
let ecount = graph.ecount();
if ecount == 0 {
return Ok(Vec::new());
}
let mut in_cover = vec![false; n as usize];
let mut covered = vec![false; ecount];
for eid in 0..ecount {
if covered[eid] {
continue;
}
let (u, v) = graph.edge(eid as u32)?;
if in_cover[u as usize] || in_cover[v as usize] {
covered[eid] = true;
continue;
}
in_cover[u as usize] = true;
in_cover[v as usize] = true;
mark_incident_covered(graph, u, &mut covered);
mark_incident_covered(graph, v, &mut covered);
}
let mut result: Vec<VertexId> = (0..n).filter(|&v| in_cover[v as usize]).collect();
result.sort_unstable();
Ok(result)
}
fn mark_incident_covered(graph: &Graph, v: VertexId, covered: &mut [bool]) {
if let Ok(edges) = graph.incident(v) {
for &eid in &edges {
covered[eid as usize] = true;
}
}
if graph.is_directed() {
if let Ok(edges) = graph.incident_in(v) {
for &eid in &edges {
covered[eid as usize] = true;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
let cover = minimum_vertex_cover(&g).unwrap();
assert!(cover.is_empty());
}
#[test]
fn no_edges() {
let g = Graph::with_vertices(5);
let cover = minimum_vertex_cover(&g).unwrap();
assert!(cover.is_empty());
}
#[test]
fn single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert_eq!(cover.len(), 2);
assert!(is_vertex_cover(&g, &cover));
}
#[test]
fn path_3() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() <= 4); }
#[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();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() >= 2); assert!(cover.len() <= 4); }
#[test]
fn star_graph() {
let mut g = Graph::with_vertices(5);
for i in 1..5u32 {
g.add_edge(0, i).unwrap();
}
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() <= 2);
}
#[test]
fn complete_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();
}
}
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() >= 3); }
#[test]
fn bipartite_matching_example() {
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();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() >= 2); assert!(cover.len() <= 4); }
#[test]
fn directed_graph() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
}
#[test]
fn self_loop() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 0).unwrap();
g.add_edge(1, 2).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.contains(&0)); }
#[test]
fn isolated_with_edges() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(!cover.contains(&4));
}
#[test]
fn two_approx_guarantee() {
let mut g = Graph::with_vertices(6);
for i in 0..5u32 {
g.add_edge(i, i + 1).unwrap();
}
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert!(cover.len() <= 6); }
#[test]
fn is_cover_validator_positive() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert!(is_vertex_cover(&g, &[1]));
assert!(is_vertex_cover(&g, &[0, 2]));
assert!(is_vertex_cover(&g, &[0, 1, 2]));
}
#[test]
fn is_cover_validator_negative() {
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_vertex_cover(&g, &[]));
assert!(!is_vertex_cover(&g, &[0]));
assert!(!is_vertex_cover(&g, &[0, 3]));
}
#[test]
fn sorted_output() {
let mut g = Graph::with_vertices(6);
g.add_edge(4, 5).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
for i in 1..cover.len() {
assert!(cover[i] > cover[i - 1]);
}
}
#[test]
fn parallel_edges() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
let cover = minimum_vertex_cover(&g).unwrap();
assert!(is_vertex_cover(&g, &cover));
assert_eq!(cover.len(), 2);
}
}