use crate::algorithms::connectivity::components::connected_components;
use crate::core::{Graph, IgraphResult};
pub fn is_pseudo_forest(graph: &Graph) -> IgraphResult<bool> {
if graph.is_directed() {
return Ok(false);
}
let n = graph.vcount();
if n == 0 {
return Ok(true);
}
let cc = connected_components(graph)?;
let comp_count = cc.count as usize;
let mut comp_verts = vec![0u64; comp_count];
let mut comp_edges = vec![0u64; comp_count];
for v in 0..n {
let c = cc.membership[v as usize] as usize;
comp_verts[c] = comp_verts[c].saturating_add(1);
}
let ecount = graph.ecount();
for eid in 0..ecount {
let eid_u32 = u32::try_from(eid).unwrap_or(u32::MAX);
let (u, _v) = graph.edge(eid_u32)?;
let c = cc.membership[u as usize] as usize;
comp_edges[c] = comp_edges[c].saturating_add(1);
}
for c in 0..comp_count {
if comp_edges[c] > comp_verts[c] {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn edgeless_graph() {
let g = Graph::with_vertices(5);
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn path_is_pseudo_forest() {
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_pseudo_forest(&g).unwrap());
}
#[test]
fn tree_is_pseudo_forest() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(1, 3).unwrap();
g.add_edge(1, 4).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn triangle_is_pseudo_forest() {
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!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn cycle_4_is_pseudo_forest() {
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!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn unicyclic_with_tail() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn two_disjoint_cycles() {
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();
g.add_edge(4, 5).unwrap();
g.add_edge(5, 3).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn k4_not_pseudo_forest() {
let mut g = Graph::with_vertices(4);
for u in 0..4u32 {
for v in (u + 1)..4 {
g.add_edge(u, v).unwrap();
}
}
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn diamond_not_pseudo_forest() {
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();
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn theta_graph_not_pseudo_forest() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 2).unwrap();
g.add_edge(2, 1).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(3, 1).unwrap();
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn directed_returns_false() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn self_loop_counts() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn two_self_loops_not_pseudo_forest() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 0).unwrap();
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn parallel_edges_not_pseudo_forest() {
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();
assert!(!is_pseudo_forest(&g).unwrap());
}
#[test]
fn parallel_edge_is_pseudo_forest() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
assert!(is_pseudo_forest(&g).unwrap());
}
#[test]
fn mixed_components() {
let mut g = Graph::with_vertices(7);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
for u in 3..7u32 {
for v in (u + 1)..7 {
g.add_edge(u, v).unwrap();
}
}
assert!(!is_pseudo_forest(&g).unwrap());
}
}