use crate::base::{EdgeWeight, Graph, Node};
use std::collections::{HashSet, VecDeque};
#[derive(Debug, Clone, PartialEq)]
pub enum EulerianType {
Circuit,
Path,
None,
}
#[allow(dead_code)]
pub fn eulerian_type<N, E, Ix>(graph: &Graph<N, E, Ix>) -> EulerianType
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let non_isolated: Vec<_> = graph
.inner()
.node_indices()
.filter(|&idx| graph.inner().edges(idx).count() > 0)
.collect();
if non_isolated.is_empty() {
return EulerianType::Circuit; }
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(non_isolated[0]);
visited.insert(non_isolated[0]);
while let Some(node) = queue.pop_front() {
for neighbor in graph.inner().neighbors(node) {
if !visited.contains(&neighbor) && non_isolated.contains(&neighbor) {
visited.insert(neighbor);
queue.push_back(neighbor);
}
}
}
if visited.len() != non_isolated.len() {
return EulerianType::None; }
let mut odd_degree_count = 0;
for node in graph.inner().node_indices() {
let degree = graph.inner().edges(node).count();
if degree % 2 == 1 {
odd_degree_count += 1;
}
}
match odd_degree_count {
0 => EulerianType::Circuit,
2 => EulerianType::Path,
_ => EulerianType::None,
}
}
#[allow(dead_code)]
pub fn has_hamiltonian_path<N, E, Ix>(graph: &Graph<N, E, Ix>) -> bool
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let n = graph.node_count();
if n == 0 {
return true;
}
let nodes: Vec<_> = graph.inner().node_indices().collect();
for &start in &nodes {
let mut visited = vec![false; n];
visited[start.index()] = true;
if hamiltonian_path_dfs(graph, start, &mut visited, 1, n) {
return true;
}
}
false
}
#[allow(dead_code)]
fn hamiltonian_path_dfs<N, E, Ix>(
graph: &Graph<N, E, Ix>,
current: petgraph::graph::NodeIndex<Ix>,
visited: &mut Vec<bool>,
count: usize,
n: usize,
) -> bool
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
if count == n {
return true;
}
for neighbor in graph.inner().neighbors(current) {
if !visited[neighbor.index()] {
visited[neighbor.index()] = true;
if hamiltonian_path_dfs(graph, neighbor, visited, count + 1, n) {
return true;
}
visited[neighbor.index()] = false;
}
}
false
}
#[allow(dead_code)]
pub fn has_hamiltonian_circuit<N, E, Ix>(graph: &Graph<N, E, Ix>) -> bool
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
let n = graph.node_count();
if n == 0 {
return true;
}
if n < 3 {
return false;
}
let nodes: Vec<_> = graph.inner().node_indices().collect();
let start = nodes[0];
let mut visited = vec![false; n];
visited[start.index()] = true;
hamiltonian_circuit_dfs(graph, start, start, &mut visited, 1, n)
}
#[allow(dead_code)]
fn hamiltonian_circuit_dfs<N, E, Ix>(
graph: &Graph<N, E, Ix>,
current: petgraph::graph::NodeIndex<Ix>,
start: petgraph::graph::NodeIndex<Ix>,
visited: &mut Vec<bool>,
count: usize,
n: usize,
) -> bool
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: petgraph::graph::IndexType,
{
if count == n {
return graph.inner().contains_edge(current, start);
}
for neighbor in graph.inner().neighbors(current) {
if !visited[neighbor.index()] {
visited[neighbor.index()] = true;
if hamiltonian_circuit_dfs(graph, neighbor, start, visited, count + 1, n) {
return true;
}
visited[neighbor.index()] = false;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result as GraphResult;
use crate::generators::create_graph;
#[test]
fn test_eulerian_circuit() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(2, 3, ())?;
graph.add_edge(3, 0, ())?;
assert_eq!(eulerian_type(&graph), EulerianType::Circuit);
Ok(())
}
#[test]
fn test_eulerian_path() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
assert_eq!(eulerian_type(&graph), EulerianType::Path);
Ok(())
}
#[test]
fn test_no_eulerian() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(1, 4, ())?; graph.add_edge(3, 4, ())?;
graph.add_edge(4, 5, ())?;
assert_eq!(eulerian_type(&graph), EulerianType::None);
Ok(())
}
#[test]
fn test_hamiltonian_path() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(2, 3, ())?;
assert!(has_hamiltonian_path(&graph));
Ok(())
}
#[test]
fn test_hamiltonian_circuit() -> GraphResult<()> {
let mut graph = create_graph::<i32, ()>();
graph.add_edge(0, 1, ())?;
graph.add_edge(1, 2, ())?;
graph.add_edge(2, 3, ())?;
graph.add_edge(3, 0, ())?;
assert!(has_hamiltonian_circuit(&graph));
let mut star = create_graph::<i32, ()>();
star.add_edge(0, 1, ())?;
star.add_edge(0, 2, ())?;
star.add_edge(0, 3, ())?;
assert!(!has_hamiltonian_circuit(&star));
Ok(())
}
}