1use crate::graph::traits::{GraphBase, GraphQuery};
6use crate::graph::Graph;
7use crate::node::NodeIndex;
8use std::collections::{HashMap, VecDeque};
9
10pub fn is_connected<T>(graph: &Graph<T, impl Clone>) -> bool {
12 if graph.node_count() == 0 {
13 return true;
14 }
15
16 let first_node = graph.nodes().next().map(|n| n.index()).unwrap();
17 let mut visited = vec![false; graph.node_count()];
18 let mut count = 0;
19
20 let mut queue = VecDeque::new();
21 queue.push_back(first_node);
22 visited[first_node.index()] = true;
23
24 while let Some(node) = queue.pop_front() {
25 count += 1;
26
27 for neighbor in graph.neighbors(node) {
28 if !visited[neighbor.index()] {
29 visited[neighbor.index()] = true;
30 queue.push_back(neighbor);
31 }
32 }
33 }
34
35 count == graph.node_count()
36}
37
38pub fn is_bipartite<T>(graph: &Graph<T, impl Clone>) -> bool {
40 let mut color: HashMap<usize, bool> = HashMap::new();
41
42 for start in graph.nodes() {
43 let start_idx = start.index();
44 if color.contains_key(&start_idx.index()) {
45 continue;
46 }
47
48 let mut queue = VecDeque::new();
49 queue.push_back(start_idx.index());
50 color.insert(start_idx.index(), true);
51
52 while let Some(node_idx) = queue.pop_front() {
53 let current_color = color[&node_idx];
54 let node_ni = NodeIndex::new(node_idx, 0);
55
56 for neighbor in graph.neighbors(node_ni) {
57 let neighbor_idx = neighbor.index();
58 match color.get(&neighbor_idx) {
59 Some(&c) if c == current_color => return false,
60 None => {
61 color.insert(neighbor_idx, !current_color);
62 queue.push_back(neighbor_idx);
63 }
64 _ => {}
65 }
66 }
67 }
68 }
69
70 true
71}
72
73pub fn is_dag<T>(graph: &Graph<T, impl Clone>) -> bool {
75 crate::algorithms::traversal::topological_sort(graph).is_ok()
77}
78
79pub fn has_cycle<T>(graph: &Graph<T, impl Clone>) -> bool {
81 !is_dag(graph)
82}
83
84pub fn is_tree<T>(graph: &Graph<T, impl Clone>) -> bool {
86 let n = graph.node_count();
87 if n == 0 {
88 return true;
89 }
90
91 is_connected(graph) && graph.edge_count() == n - 1
93}
94
95pub fn diameter<T>(graph: &Graph<T, impl Clone>) -> usize {
97 if graph.node_count() == 0 {
98 return 0;
99 }
100
101 let mut max_distance = 0;
102
103 for start in graph.nodes() {
104 let mut distances: HashMap<usize, usize> = HashMap::new();
105 let mut queue = VecDeque::new();
106
107 let start_idx = start.index();
108 distances.insert(start_idx.index(), 0);
109 queue.push_back(start_idx);
110
111 while let Some(node) = queue.pop_front() {
112 let node_idx = node.index();
113 let dist = distances[&node_idx];
114 max_distance = max_distance.max(dist);
115
116 let node_ni = NodeIndex::new(node_idx, 0);
117 for neighbor in graph.neighbors(node_ni) {
118 let neighbor_idx = neighbor.index();
119 if let std::collections::hash_map::Entry::Vacant(e) = distances.entry(neighbor_idx)
120 {
121 e.insert(dist + 1);
122 queue.push_back(neighbor);
123 }
124 }
125 }
126 }
127
128 max_distance
129}
130
131pub fn density<T>(graph: &Graph<T, impl Clone>) -> f64 {
133 let n = graph.node_count();
134 if n <= 1 {
135 return 0.0;
136 }
137
138 let max_edges = n * (n - 1);
139 graph.edge_count() as f64 / max_edges as f64
140}
141
142pub fn girth<T>(graph: &Graph<T, impl Clone>) -> usize {
165 use std::collections::VecDeque;
166
167 let n = graph.node_count();
168 if n == 0 {
169 return 0;
170 }
171
172 let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
173 let mut min_cycle = usize::MAX;
174
175 for start in &node_indices {
177 let mut dist = vec![usize::MAX; n];
178 let mut queue = VecDeque::new();
179
180 dist[start.index()] = 0;
181 queue.push_back(*start);
182
183 while let Some(u) = queue.pop_front() {
184 if dist[u.index()] >= min_cycle {
185 continue; }
187
188 for v in graph.neighbors(u) {
189 let new_dist = dist[u.index()] + 1;
190
191 if dist[v.index()] != usize::MAX {
193 if dist[v.index()] != dist[u.index()] - 1 {
194 let cycle_len = new_dist + dist[v.index()];
195 min_cycle = min_cycle.min(cycle_len);
196 }
197 } else {
198 dist[v.index()] = new_dist;
199 queue.push_back(v);
200 }
201 }
202 }
203 }
204
205 if min_cycle == usize::MAX {
206 0
207 } else {
208 min_cycle
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate::graph::builders::GraphBuilder;
216
217 #[test]
218 fn test_is_connected() {
219 let graph = GraphBuilder::undirected()
220 .with_nodes(vec![1, 2, 3])
221 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0)])
222 .build()
223 .unwrap();
224 assert!(is_connected(&graph));
225 }
226
227 #[test]
228 fn test_is_bipartite() {
229 let graph = GraphBuilder::undirected()
230 .with_nodes(vec![1, 2, 3, 4])
231 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)])
232 .build()
233 .unwrap();
234 assert!(is_bipartite(&graph));
235 }
236
237 #[test]
238 fn test_is_dag() {
239 let graph = GraphBuilder::directed()
240 .with_nodes(vec![1, 2, 3])
241 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0)])
242 .build()
243 .unwrap();
244 assert!(is_dag(&graph));
245 }
246
247 #[test]
248 fn test_density() {
249 let graph = GraphBuilder::directed()
250 .with_nodes(vec![1, 2, 3, 4])
251 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0)])
252 .build()
253 .unwrap();
254 let d = density(&graph);
255 assert!(d > 0.0 && d < 1.0);
256 }
257
258 #[test]
259 fn test_diameter() {
260 let graph = GraphBuilder::directed()
261 .with_nodes(vec![1, 2, 3, 4, 5])
262 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0), (3, 4, 1.0)])
263 .build()
264 .unwrap();
265
266 let diam = diameter(&graph);
268 assert_eq!(diam, 4);
269 }
270
271 #[test]
272 fn test_girth_triangle() {
273 let graph = GraphBuilder::directed()
274 .with_nodes(vec!["A", "B", "C"])
275 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 0, 1.0)])
276 .build()
277 .unwrap();
278
279 let g = girth(&graph);
281 assert_eq!(g, 3);
282 }
283
284 #[test]
285 fn test_girth_no_cycle() {
286 let graph = GraphBuilder::directed()
287 .with_nodes(vec!["A", "B", "C"])
288 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0)])
289 .build()
290 .unwrap();
291
292 let g = girth(&graph);
294 assert_eq!(g, 0);
295 }
296
297 #[test]
298 fn test_has_cycle() {
299 let graph_with_cycle = GraphBuilder::directed()
300 .with_nodes(vec!["A", "B", "C"])
301 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 0, 1.0)])
302 .build()
303 .unwrap();
304 assert!(has_cycle(&graph_with_cycle));
305
306 let graph_without_cycle = GraphBuilder::directed()
307 .with_nodes(vec!["A", "B", "C"])
308 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0)])
309 .build()
310 .unwrap();
311 assert!(!has_cycle(&graph_without_cycle));
312 }
313
314 #[test]
315 fn test_is_tree() {
316 let tree = GraphBuilder::undirected()
317 .with_nodes(vec![1, 2, 3, 4])
318 .with_edges(vec![(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0)])
319 .build()
320 .unwrap();
321 assert!(is_tree(&tree));
322
323 let not_tree = GraphBuilder::undirected()
324 .with_nodes(vec![1, 2, 3])
325 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 0, 1.0)])
326 .build()
327 .unwrap();
328 assert!(!is_tree(¬_tree));
329 }
330}