1use crate::base::{EdgeWeight, Graph, Node};
7use std::collections::{HashSet, VecDeque};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum EulerianType {
12 Circuit,
14 Path,
16 None,
18}
19
20#[allow(dead_code)]
31pub fn eulerian_type<N, E, Ix>(graph: &Graph<N, E, Ix>) -> EulerianType
32where
33 N: Node + std::fmt::Debug,
34 E: EdgeWeight,
35 Ix: petgraph::graph::IndexType,
36{
37 let non_isolated: Vec<_> = graph
39 .inner()
40 .node_indices()
41 .filter(|&idx| graph.inner().edges(idx).count() > 0)
42 .collect();
43
44 if non_isolated.is_empty() {
45 return EulerianType::Circuit; }
47
48 let mut visited = HashSet::new();
50 let mut queue = VecDeque::new();
51 queue.push_back(non_isolated[0]);
52 visited.insert(non_isolated[0]);
53
54 while let Some(node) = queue.pop_front() {
55 for neighbor in graph.inner().neighbors(node) {
56 if !visited.contains(&neighbor) && non_isolated.contains(&neighbor) {
57 visited.insert(neighbor);
58 queue.push_back(neighbor);
59 }
60 }
61 }
62
63 if visited.len() != non_isolated.len() {
64 return EulerianType::None; }
66
67 let mut odd_degree_count = 0;
69 for node in graph.inner().node_indices() {
70 let degree = graph.inner().edges(node).count();
71 if degree % 2 == 1 {
72 odd_degree_count += 1;
73 }
74 }
75
76 match odd_degree_count {
77 0 => EulerianType::Circuit,
78 2 => EulerianType::Path,
79 _ => EulerianType::None,
80 }
81}
82
83#[allow(dead_code)]
91pub fn has_hamiltonian_path<N, E, Ix>(graph: &Graph<N, E, Ix>) -> bool
92where
93 N: Node + std::fmt::Debug,
94 E: EdgeWeight,
95 Ix: petgraph::graph::IndexType,
96{
97 let n = graph.node_count();
98 if n == 0 {
99 return true;
100 }
101
102 let nodes: Vec<_> = graph.inner().node_indices().collect();
103
104 for &start in &nodes {
106 let mut visited = vec![false; n];
107 visited[start.index()] = true;
108
109 if hamiltonian_path_dfs(graph, start, &mut visited, 1, n) {
110 return true;
111 }
112 }
113
114 false
115}
116
117#[allow(dead_code)]
119fn hamiltonian_path_dfs<N, E, Ix>(
120 graph: &Graph<N, E, Ix>,
121 current: petgraph::graph::NodeIndex<Ix>,
122 visited: &mut Vec<bool>,
123 count: usize,
124 n: usize,
125) -> bool
126where
127 N: Node + std::fmt::Debug,
128 E: EdgeWeight,
129 Ix: petgraph::graph::IndexType,
130{
131 if count == n {
132 return true;
133 }
134
135 for neighbor in graph.inner().neighbors(current) {
136 if !visited[neighbor.index()] {
137 visited[neighbor.index()] = true;
138
139 if hamiltonian_path_dfs(graph, neighbor, visited, count + 1, n) {
140 return true;
141 }
142
143 visited[neighbor.index()] = false;
144 }
145 }
146
147 false
148}
149
150#[allow(dead_code)]
158pub fn has_hamiltonian_circuit<N, E, Ix>(graph: &Graph<N, E, Ix>) -> bool
159where
160 N: Node + std::fmt::Debug,
161 E: EdgeWeight,
162 Ix: petgraph::graph::IndexType,
163{
164 let n = graph.node_count();
165 if n == 0 {
166 return true;
167 }
168 if n < 3 {
169 return false;
170 }
171
172 let nodes: Vec<_> = graph.inner().node_indices().collect();
173 let start = nodes[0];
174
175 let mut visited = vec![false; n];
176 visited[start.index()] = true;
177
178 hamiltonian_circuit_dfs(graph, start, start, &mut visited, 1, n)
179}
180
181#[allow(dead_code)]
183fn hamiltonian_circuit_dfs<N, E, Ix>(
184 graph: &Graph<N, E, Ix>,
185 current: petgraph::graph::NodeIndex<Ix>,
186 start: petgraph::graph::NodeIndex<Ix>,
187 visited: &mut Vec<bool>,
188 count: usize,
189 n: usize,
190) -> bool
191where
192 N: Node + std::fmt::Debug,
193 E: EdgeWeight,
194 Ix: petgraph::graph::IndexType,
195{
196 if count == n {
197 return graph.inner().contains_edge(current, start);
199 }
200
201 for neighbor in graph.inner().neighbors(current) {
202 if !visited[neighbor.index()] {
203 visited[neighbor.index()] = true;
204
205 if hamiltonian_circuit_dfs(graph, neighbor, start, visited, count + 1, n) {
206 return true;
207 }
208
209 visited[neighbor.index()] = false;
210 }
211 }
212
213 false
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::error::Result as GraphResult;
220 use crate::generators::create_graph;
221
222 #[test]
223 fn test_eulerian_circuit() -> GraphResult<()> {
224 let mut graph = create_graph::<i32, ()>();
226
227 graph.add_edge(0, 1, ())?;
228 graph.add_edge(1, 2, ())?;
229 graph.add_edge(2, 3, ())?;
230 graph.add_edge(3, 0, ())?;
231
232 assert_eq!(eulerian_type(&graph), EulerianType::Circuit);
233
234 Ok(())
235 }
236
237 #[test]
238 fn test_eulerian_path() -> GraphResult<()> {
239 let mut graph = create_graph::<i32, ()>();
241
242 graph.add_edge(0, 1, ())?;
243 graph.add_edge(1, 2, ())?;
244
245 assert_eq!(eulerian_type(&graph), EulerianType::Path);
246
247 Ok(())
248 }
249
250 #[test]
251 fn test_no_eulerian() -> GraphResult<()> {
252 let mut graph = create_graph::<i32, ()>();
254
255 graph.add_edge(0, 1, ())?;
259 graph.add_edge(1, 2, ())?;
260 graph.add_edge(1, 4, ())?; graph.add_edge(3, 4, ())?;
262 graph.add_edge(4, 5, ())?;
263
264 assert_eq!(eulerian_type(&graph), EulerianType::None);
265
266 Ok(())
267 }
268
269 #[test]
270 fn test_hamiltonian_path() -> GraphResult<()> {
271 let mut graph = create_graph::<i32, ()>();
273
274 graph.add_edge(0, 1, ())?;
275 graph.add_edge(1, 2, ())?;
276 graph.add_edge(2, 3, ())?;
277
278 assert!(has_hamiltonian_path(&graph));
279
280 Ok(())
281 }
282
283 #[test]
284 fn test_hamiltonian_circuit() -> GraphResult<()> {
285 let mut graph = create_graph::<i32, ()>();
287
288 graph.add_edge(0, 1, ())?;
289 graph.add_edge(1, 2, ())?;
290 graph.add_edge(2, 3, ())?;
291 graph.add_edge(3, 0, ())?;
292
293 assert!(has_hamiltonian_circuit(&graph));
294
295 let mut star = create_graph::<i32, ()>();
297
298 star.add_edge(0, 1, ())?;
299 star.add_edge(0, 2, ())?;
300 star.add_edge(0, 3, ())?;
301
302 assert!(!has_hamiltonian_circuit(&star));
303
304 Ok(())
305 }
306}