use crate::core::graph::EdgeId;
use crate::core::{Graph, IgraphError, IgraphResult};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SimpleMode {
DirectedAsDirected,
DirectedAsUndirected,
}
pub fn is_simple(graph: &Graph) -> IgraphResult<bool> {
is_simple_with_mode(graph, SimpleMode::DirectedAsDirected)
}
pub fn is_simple_with_mode(graph: &Graph, mode: SimpleMode) -> IgraphResult<bool> {
let n = graph.vcount();
let m = graph.ecount();
if n == 0 || m == 0 {
return Ok(true);
}
if !graph.is_directed() || mode == SimpleMode::DirectedAsDirected {
for v in 0..n {
let neis = graph.neighbors(v)?;
let mut prev: Option<u32> = None;
for &u in &neis {
if u == v {
return Ok(false);
}
if Some(u) == prev {
return Ok(false);
}
prev = Some(u);
}
}
return Ok(true);
}
let m_u32 = u32::try_from(m).map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(m);
for eid in 0..m_u32 {
let (src, tgt) = graph.edge(eid as EdgeId)?;
if src == tgt {
return Ok(false);
}
pairs.push(if src < tgt { (src, tgt) } else { (tgt, src) });
}
pairs.sort_unstable();
for w in pairs.windows(2) {
if w[0] == w[1] {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_is_simple() {
let g = Graph::with_vertices(0);
assert!(is_simple(&g).unwrap());
}
#[test]
fn no_edge_graph_is_simple() {
let g = Graph::with_vertices(5);
assert!(is_simple(&g).unwrap());
}
#[test]
fn path_is_simple() {
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_simple(&g).unwrap());
}
#[test]
fn self_loop_breaks_simplicity() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
assert!(!is_simple(&g).unwrap());
}
#[test]
fn parallel_edge_breaks_simplicity_undirected() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
assert!(!is_simple(&g).unwrap());
}
#[test]
fn reversed_parallel_undirected_breaks_simplicity() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 0).unwrap();
assert!(!is_simple(&g).unwrap());
}
#[test]
fn directed_mutual_pair_is_simple() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 0).unwrap();
assert!(is_simple(&g).unwrap());
}
#[test]
fn directed_parallel_breaks_simplicity() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
assert!(!is_simple(&g).unwrap());
}
#[test]
fn directed_self_loop_breaks_simplicity() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 0).unwrap();
assert!(!is_simple(&g).unwrap());
}
#[test]
fn directed_mutual_pair_not_simple_in_undirected_mode() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 0).unwrap();
assert!(is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap());
assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
}
#[test]
fn directed_mode_default_matches_is_simple() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
assert_eq!(
is_simple(&g).unwrap(),
is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap()
);
}
#[test]
fn directed_3_cycle_simple_in_undirected_mode() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
assert!(is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
}
#[test]
fn directed_self_loop_not_simple_in_either_mode() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 0).unwrap();
assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap());
assert!(!is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap());
}
#[test]
fn undirected_graph_modes_are_equivalent() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let a = is_simple_with_mode(&g, SimpleMode::DirectedAsDirected).unwrap();
let b = is_simple_with_mode(&g, SimpleMode::DirectedAsUndirected).unwrap();
assert_eq!(a, b);
assert!(a);
}
#[test]
fn simplify_makes_graph_simple() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 0).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert!(!is_simple(&g).unwrap());
let s = crate::simplify(&g, true, true).unwrap();
assert!(is_simple(&s).unwrap());
}
}