use crate::algorithms::connectivity::{ConnectednessMode, is_connected};
use crate::core::{Graph, IgraphResult};
pub fn is_cycle(graph: &Graph) -> IgraphResult<bool> {
if graph.is_directed() {
return Ok(false);
}
let n = graph.vcount();
if n < 3 {
return Ok(false);
}
if graph.ecount() != n as usize {
return Ok(false);
}
for v in 0..n {
if graph.degree(v)? != 2 {
return Ok(false);
}
}
is_connected(graph, ConnectednessMode::Weak)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn two_vertices_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
assert!(!is_cycle(&g).unwrap());
}
#[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();
assert!(is_cycle(&g).unwrap());
}
#[test]
fn c4() {
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_cycle(&g).unwrap());
}
#[test]
fn c5() {
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();
assert!(is_cycle(&g).unwrap());
}
#[test]
fn path_not_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();
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn star_not_cycle() {
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();
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn complete_k4_not_cycle() {
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();
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn two_disjoint_triangles_not_cycle() {
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_cycle(&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();
g.add_edge(2, 0).unwrap();
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn wheel_not_cycle() {
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();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
g.add_edge(4, 1).unwrap();
assert!(!is_cycle(&g).unwrap());
}
#[test]
fn c6_non_sequential() {
let mut g = Graph::with_vertices(6);
g.add_edge(0, 3).unwrap();
g.add_edge(3, 1).unwrap();
g.add_edge(1, 4).unwrap();
g.add_edge(4, 2).unwrap();
g.add_edge(2, 5).unwrap();
g.add_edge(5, 0).unwrap();
assert!(is_cycle(&g).unwrap());
}
}