rust_igraph/algorithms/properties/
is_spider.rs1use crate::algorithms::paths::dijkstra::DijkstraMode;
15use crate::algorithms::properties::is_tree::is_tree;
16use crate::core::{Graph, IgraphResult};
17
18pub fn is_spider(graph: &Graph) -> IgraphResult<bool> {
48 if is_tree(graph, DijkstraMode::All)?.is_none() {
49 return Ok(false);
50 }
51
52 let n = graph.vcount();
53 if n <= 3 {
54 return Ok(true);
55 }
56
57 let mut high_degree_count = 0u32;
58 for v in 0..n {
59 let deg = graph.degree(v)?;
60 if deg >= 3 {
61 high_degree_count = high_degree_count.saturating_add(1);
62 if high_degree_count > 1 {
63 return Ok(false);
64 }
65 }
66 }
67
68 Ok(true)
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn empty_graph() {
77 let g = Graph::with_vertices(0);
79 assert!(!is_spider(&g).unwrap());
80 }
81
82 #[test]
83 fn single_vertex() {
84 let g = Graph::with_vertices(1);
85 assert!(is_spider(&g).unwrap());
86 }
87
88 #[test]
89 fn single_edge() {
90 let mut g = Graph::with_vertices(2);
91 g.add_edge(0, 1).unwrap();
92 assert!(is_spider(&g).unwrap());
93 }
94
95 #[test]
96 fn path_p3() {
97 let mut g = Graph::with_vertices(3);
98 g.add_edge(0, 1).unwrap();
99 g.add_edge(1, 2).unwrap();
100 assert!(is_spider(&g).unwrap());
101 }
102
103 #[test]
104 fn path_p5() {
105 let mut g = Graph::with_vertices(5);
106 g.add_edge(0, 1).unwrap();
107 g.add_edge(1, 2).unwrap();
108 g.add_edge(2, 3).unwrap();
109 g.add_edge(3, 4).unwrap();
110 assert!(is_spider(&g).unwrap());
111 }
112
113 #[test]
114 fn star_s3() {
115 let mut g = Graph::with_vertices(4);
116 g.add_edge(0, 1).unwrap();
117 g.add_edge(0, 2).unwrap();
118 g.add_edge(0, 3).unwrap();
119 assert!(is_spider(&g).unwrap());
120 }
121
122 #[test]
123 fn star_s5() {
124 let mut g = Graph::with_vertices(6);
125 g.add_edge(0, 1).unwrap();
126 g.add_edge(0, 2).unwrap();
127 g.add_edge(0, 3).unwrap();
128 g.add_edge(0, 4).unwrap();
129 g.add_edge(0, 5).unwrap();
130 assert!(is_spider(&g).unwrap());
131 }
132
133 #[test]
134 fn spider_with_legs() {
135 let mut g = Graph::with_vertices(7);
138 g.add_edge(0, 1).unwrap();
139 g.add_edge(0, 2).unwrap();
140 g.add_edge(2, 3).unwrap();
141 g.add_edge(0, 4).unwrap();
142 g.add_edge(4, 5).unwrap();
143 g.add_edge(5, 6).unwrap();
144 assert!(is_spider(&g).unwrap());
145 }
146
147 #[test]
148 fn two_branching_vertices_not_spider() {
149 let mut g = Graph::with_vertices(6);
151 g.add_edge(0, 1).unwrap();
152 g.add_edge(1, 2).unwrap();
153 g.add_edge(1, 3).unwrap();
154 g.add_edge(2, 4).unwrap();
155 g.add_edge(2, 5).unwrap();
156 assert!(!is_spider(&g).unwrap());
157 }
158
159 #[test]
160 fn triangle_not_tree() {
161 let mut g = Graph::with_vertices(3);
162 g.add_edge(0, 1).unwrap();
163 g.add_edge(1, 2).unwrap();
164 g.add_edge(2, 0).unwrap();
165 assert!(!is_spider(&g).unwrap());
166 }
167
168 #[test]
169 fn disconnected_not_spider() {
170 let g = Graph::with_vertices(3);
171 assert!(!is_spider(&g).unwrap());
173 }
174
175 #[test]
176 fn caterpillar_not_spider() {
177 let mut g = Graph::with_vertices(6);
179 g.add_edge(0, 1).unwrap();
180 g.add_edge(1, 2).unwrap();
181 g.add_edge(2, 3).unwrap();
182 g.add_edge(1, 4).unwrap();
183 g.add_edge(2, 5).unwrap();
184 assert!(!is_spider(&g).unwrap());
185 }
186
187 #[test]
188 fn directed_treated_as_undirected() {
189 let mut g = Graph::new(4, true).unwrap();
191 g.add_edge(0, 1).unwrap();
192 g.add_edge(0, 2).unwrap();
193 g.add_edge(0, 3).unwrap();
194 assert!(is_spider(&g).unwrap());
195 }
196
197 #[test]
198 fn two_vertices_no_edge() {
199 let g = Graph::with_vertices(2);
201 assert!(!is_spider(&g).unwrap());
202 }
203}