rust_igraph/algorithms/properties/
is_apex_tree.rs1use crate::algorithms::paths::dijkstra::DijkstraMode;
14use crate::algorithms::properties::is_tree::is_tree;
15use crate::core::{Graph, IgraphResult};
16
17pub fn is_apex_tree(graph: &Graph) -> IgraphResult<bool> {
47 let n = graph.vcount();
48
49 if n <= 1 {
50 return Ok(true);
51 }
52
53 if is_tree(graph, DijkstraMode::All)?.is_some() {
54 return Ok(true);
55 }
56
57 let n_usize = n as usize;
58 let mut adj = vec![vec![false; n_usize]; n_usize];
59 for v in 0..n {
60 let nbrs = graph.neighbors(v)?;
61 for &w in &nbrs {
62 adj[v as usize][w as usize] = true;
63 adj[w as usize][v as usize] = true;
64 }
65 }
66
67 for apex in 0..n_usize {
68 if is_tree_without(&adj, apex, n_usize) {
69 return Ok(true);
70 }
71 }
72
73 Ok(false)
74}
75
76fn is_tree_without(adj: &[Vec<bool>], removed: usize, n: usize) -> bool {
77 let remaining = n - 1;
78 if remaining == 0 {
79 return true;
80 }
81
82 let mut visited = vec![false; n];
83 visited[removed] = true;
84
85 let Some(start) = (0..n).find(|&v| v != removed) else {
86 return false;
87 };
88 let mut stack = vec![start];
89 visited[start] = true;
90 let mut visited_count = 1usize;
91 let mut edge_count = 0usize;
92
93 while let Some(u) = stack.pop() {
94 for (v, &is_adj) in adj[u].iter().enumerate() {
95 if v == removed || !is_adj {
96 continue;
97 }
98 edge_count += 1;
99 if !visited[v] {
100 visited[v] = true;
101 visited_count += 1;
102 stack.push(v);
103 }
104 }
105 }
106
107 edge_count /= 2;
108
109 visited_count == remaining && edge_count == remaining - 1
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn empty_graph() {
119 let g = Graph::with_vertices(0);
120 assert!(is_apex_tree(&g).unwrap());
121 }
122
123 #[test]
124 fn single_vertex() {
125 let g = Graph::with_vertices(1);
126 assert!(is_apex_tree(&g).unwrap());
127 }
128
129 #[test]
130 fn single_edge() {
131 let mut g = Graph::with_vertices(2);
132 g.add_edge(0, 1).unwrap();
133 assert!(is_apex_tree(&g).unwrap());
134 }
135
136 #[test]
137 fn triangle() {
138 let mut g = Graph::with_vertices(3);
139 g.add_edge(0, 1).unwrap();
140 g.add_edge(1, 2).unwrap();
141 g.add_edge(2, 0).unwrap();
142 assert!(is_apex_tree(&g).unwrap());
143 }
144
145 #[test]
146 fn tree() {
147 let mut g = Graph::with_vertices(5);
148 g.add_edge(0, 1).unwrap();
149 g.add_edge(1, 2).unwrap();
150 g.add_edge(1, 3).unwrap();
151 g.add_edge(3, 4).unwrap();
152 assert!(is_apex_tree(&g).unwrap());
153 }
154
155 #[test]
156 fn c4() {
157 let mut g = Graph::with_vertices(4);
159 g.add_edge(0, 1).unwrap();
160 g.add_edge(1, 2).unwrap();
161 g.add_edge(2, 3).unwrap();
162 g.add_edge(3, 0).unwrap();
163 assert!(is_apex_tree(&g).unwrap());
164 }
165
166 #[test]
167 fn k4() {
168 let mut g = Graph::with_vertices(4);
170 for i in 0..4u32 {
171 for j in (i + 1)..4 {
172 g.add_edge(i, j).unwrap();
173 }
174 }
175 assert!(!is_apex_tree(&g).unwrap());
176 }
177
178 #[test]
179 fn two_triangles_not_apex_tree() {
180 let mut g = Graph::with_vertices(6);
181 g.add_edge(0, 1).unwrap();
182 g.add_edge(1, 2).unwrap();
183 g.add_edge(2, 0).unwrap();
184 g.add_edge(3, 4).unwrap();
185 g.add_edge(4, 5).unwrap();
186 g.add_edge(5, 3).unwrap();
187 assert!(!is_apex_tree(&g).unwrap());
188 }
189
190 #[test]
191 fn two_triangles_shared_vertex() {
192 let mut g = Graph::with_vertices(5);
196 g.add_edge(0, 1).unwrap();
197 g.add_edge(1, 2).unwrap();
198 g.add_edge(2, 0).unwrap();
199 g.add_edge(0, 3).unwrap();
200 g.add_edge(3, 4).unwrap();
201 g.add_edge(4, 0).unwrap();
202 assert!(!is_apex_tree(&g).unwrap());
203 }
204
205 #[test]
206 fn diamond() {
207 let mut g = Graph::with_vertices(4);
210 g.add_edge(0, 1).unwrap();
211 g.add_edge(0, 2).unwrap();
212 g.add_edge(0, 3).unwrap();
213 g.add_edge(1, 2).unwrap();
214 g.add_edge(1, 3).unwrap();
215 assert!(is_apex_tree(&g).unwrap());
216 }
217
218 #[test]
219 fn theta_graph() {
220 let mut g = Graph::with_vertices(4);
223 g.add_edge(0, 1).unwrap();
224 g.add_edge(1, 3).unwrap();
225 g.add_edge(0, 2).unwrap();
226 g.add_edge(2, 3).unwrap();
227 assert!(is_apex_tree(&g).unwrap());
228 }
229
230 #[test]
231 fn edgeless_not_apex_tree() {
232 let g = Graph::with_vertices(4);
234 assert!(!is_apex_tree(&g).unwrap());
235 }
236
237 #[test]
238 fn two_isolated() {
239 let g = Graph::with_vertices(2);
241 assert!(is_apex_tree(&g).unwrap());
242 }
243
244 #[test]
245 fn directed_treated_as_undirected() {
246 let mut g = Graph::new(3, true).unwrap();
247 g.add_edge(0, 1).unwrap();
248 g.add_edge(1, 2).unwrap();
249 g.add_edge(2, 0).unwrap();
250 assert!(is_apex_tree(&g).unwrap());
251 }
252
253 #[test]
254 fn wheel_w4() {
255 let mut g = Graph::with_vertices(5);
260 g.add_edge(0, 1).unwrap();
261 g.add_edge(0, 2).unwrap();
262 g.add_edge(0, 3).unwrap();
263 g.add_edge(0, 4).unwrap();
264 g.add_edge(1, 2).unwrap();
265 g.add_edge(2, 3).unwrap();
266 g.add_edge(3, 4).unwrap();
267 g.add_edge(4, 1).unwrap();
268 assert!(!is_apex_tree(&g).unwrap());
269 }
270
271 #[test]
272 fn tree_with_extra_edge() {
273 let mut g = Graph::with_vertices(4);
276 g.add_edge(0, 1).unwrap();
277 g.add_edge(1, 2).unwrap();
278 g.add_edge(2, 3).unwrap();
279 g.add_edge(0, 2).unwrap();
280 assert!(is_apex_tree(&g).unwrap());
281 }
282}