1use prelude::*;
8use props::Color;
9use traverse::*;
10
11pub trait Trees: Incidence {
12 fn is_tree(&self) -> bool
13 where
14 Self: VertexList + EdgeList + WithVertexProp<Color>,
15 {
16 let mut tree = true;
17 self.dfs(IsTree(&mut tree)).run();
18 tree
19 }
20
21 fn tree_diameter(&self) -> Result<usize, ()>
22 where
23 Self: VertexList + EdgeList + WithVertexProp<Color>,
24 {
25 let mut tree = false;
26 let mut dist = 0;
27 let mut v = Self::vertex_none();
28 self.dfs((IsTree(&mut tree), FarthestVertex(&mut v, &mut dist)))
29 .run();
30 if tree {
31 Ok(v.into_option()
32 .map(|r| {
33 self.dfs(FarthestVertex(&mut Self::vertex_none(), &mut dist))
34 .root(r)
35 .run();
36 dist
37 })
38 .unwrap_or(0))
39 } else {
40 Err(())
41 }
42 }
43}
44
45impl<G: Incidence> Trees for G {}
46
47pub struct IsTree<'a> {
48 tree: &'a mut bool,
49 saw_root: bool,
50}
51
52#[allow(non_snake_case)]
53pub fn IsTree(tree: &mut bool) -> IsTree {
54 IsTree {
55 tree: tree,
56 saw_root: false,
57 }
58}
59
60impl<'a, G: VertexList + EdgeList> Visitor<G> for IsTree<'a> {
62 fn start(&mut self, g: &G) -> Control {
63 self.saw_root = false;
64 *self.tree = g.num_vertices() == 0 || g.num_edges() == g.num_vertices() - 1;
65 continue_if(*self.tree)
66 }
67
68 fn discover_back_edge(&mut self, _g: &G, _e: Edge<G>) -> Control {
69 *self.tree = false;
70 Control::Break
71 }
72
73 fn discover_root_vertex(&mut self, _g: &G, _v: Vertex<G>) -> Control {
74 if self.saw_root {
75 *self.tree = false;
76 Control::Break
77 } else {
78 self.saw_root = true;
79 Control::Continue
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use algs::Distances;
88 use rand::prelude::*;
89
90 #[test]
91 fn tree_diameter() {
92 let mut rng = SmallRng::from_entropy();
93 for n in 1..10 {
94 for _ in 0..20 {
95 let g = StaticGraph::new_random_tree(n, &mut rng);
96 assert_eq!(g.tree_diameter(), Ok(g.diameter()));
97 }
98 }
99
100 for n in 3..30 {
101 for d in 2..n - 1 {
102 let g = StaticGraph::new_random_tree_with_diameter(n, d, &mut rng).unwrap();
103 assert_eq!(Ok(d as usize), g.tree_diameter());
104 }
105 }
106 }
107}