rust_igraph/algorithms/properties/
is_apex_forest.rs1use crate::algorithms::paths::dijkstra::DijkstraMode;
14use crate::algorithms::properties::is_forest::is_forest;
15use crate::core::{Graph, IgraphResult};
16
17pub fn is_apex_forest(graph: &Graph) -> IgraphResult<bool> {
47 let n = graph.vcount();
48
49 if n == 0 {
50 return Ok(true);
51 }
52
53 if is_forest(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_forest_without(&adj, apex, n_usize) {
69 return Ok(true);
70 }
71 }
72
73 Ok(false)
74}
75
76fn is_forest_without(adj: &[Vec<bool>], removed: usize, n: usize) -> bool {
78 let mut visited = vec![false; n];
79 visited[removed] = true;
80
81 let remaining = n - 1;
82 if remaining == 0 {
83 return true;
84 }
85
86 let mut edge_count = 0usize;
87 let mut component_count = 0usize;
88 let mut visited_count = 0usize;
89
90 for start in 0..n {
91 if visited[start] {
92 continue;
93 }
94 component_count += 1;
95 let mut stack = vec![start];
96 visited[start] = true;
97
98 while let Some(u) = stack.pop() {
99 visited_count += 1;
100 for (v, &is_adj) in adj[u].iter().enumerate() {
101 if v == removed || !is_adj {
102 continue;
103 }
104 edge_count += 1;
105 if !visited[v] {
106 visited[v] = true;
107 stack.push(v);
108 }
109 }
110 }
111 }
112
113 edge_count /= 2;
115
116 edge_count == visited_count - component_count
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn empty_graph() {
126 let g = Graph::with_vertices(0);
127 assert!(is_apex_forest(&g).unwrap());
128 }
129
130 #[test]
131 fn single_vertex() {
132 let g = Graph::with_vertices(1);
133 assert!(is_apex_forest(&g).unwrap());
134 }
135
136 #[test]
137 fn single_edge() {
138 let mut g = Graph::with_vertices(2);
139 g.add_edge(0, 1).unwrap();
140 assert!(is_apex_forest(&g).unwrap());
141 }
142
143 #[test]
144 fn triangle() {
145 let mut g = Graph::with_vertices(3);
146 g.add_edge(0, 1).unwrap();
147 g.add_edge(1, 2).unwrap();
148 g.add_edge(2, 0).unwrap();
149 assert!(is_apex_forest(&g).unwrap());
150 }
151
152 #[test]
153 fn tree() {
154 let mut g = Graph::with_vertices(5);
155 g.add_edge(0, 1).unwrap();
156 g.add_edge(1, 2).unwrap();
157 g.add_edge(1, 3).unwrap();
158 g.add_edge(3, 4).unwrap();
159 assert!(is_apex_forest(&g).unwrap());
160 }
161
162 #[test]
163 fn c4() {
164 let mut g = Graph::with_vertices(4);
166 g.add_edge(0, 1).unwrap();
167 g.add_edge(1, 2).unwrap();
168 g.add_edge(2, 3).unwrap();
169 g.add_edge(3, 0).unwrap();
170 assert!(is_apex_forest(&g).unwrap());
171 }
172
173 #[test]
174 fn k4() {
175 let mut g = Graph::with_vertices(4);
178 for i in 0..4u32 {
179 for j in (i + 1)..4 {
180 g.add_edge(i, j).unwrap();
181 }
182 }
183 assert!(!is_apex_forest(&g).unwrap());
184 }
185
186 #[test]
187 fn two_triangles_not_apex() {
188 let mut g = Graph::with_vertices(6);
189 g.add_edge(0, 1).unwrap();
190 g.add_edge(1, 2).unwrap();
191 g.add_edge(2, 0).unwrap();
192 g.add_edge(3, 4).unwrap();
193 g.add_edge(4, 5).unwrap();
194 g.add_edge(5, 3).unwrap();
195 assert!(!is_apex_forest(&g).unwrap());
196 }
197
198 #[test]
199 fn wheel_w4() {
200 let mut g = Graph::with_vertices(5);
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(0, 4).unwrap();
214 g.add_edge(1, 2).unwrap();
215 g.add_edge(2, 3).unwrap();
216 g.add_edge(3, 4).unwrap();
217 g.add_edge(4, 1).unwrap();
218 assert!(!is_apex_forest(&g).unwrap());
219 }
220
221 #[test]
222 fn diamond_apex() {
223 let mut g = Graph::with_vertices(4);
226 g.add_edge(0, 1).unwrap();
227 g.add_edge(0, 2).unwrap();
228 g.add_edge(0, 3).unwrap();
229 g.add_edge(1, 2).unwrap();
230 g.add_edge(1, 3).unwrap();
231 assert!(is_apex_forest(&g).unwrap());
232 }
233
234 #[test]
235 fn theta_graph_apex() {
236 let mut g = Graph::with_vertices(4);
239 g.add_edge(0, 1).unwrap();
240 g.add_edge(1, 3).unwrap();
241 g.add_edge(0, 2).unwrap();
242 g.add_edge(2, 3).unwrap();
243 assert!(is_apex_forest(&g).unwrap());
244 }
245
246 #[test]
247 fn edgeless() {
248 let g = Graph::with_vertices(4);
249 assert!(is_apex_forest(&g).unwrap());
250 }
251
252 #[test]
253 fn directed_treated_as_undirected() {
254 let mut g = Graph::new(3, true).unwrap();
255 g.add_edge(0, 1).unwrap();
256 g.add_edge(1, 2).unwrap();
257 g.add_edge(2, 0).unwrap();
258 assert!(is_apex_forest(&g).unwrap());
259 }
260
261 #[test]
262 fn two_triangles_shared_vertex() {
263 let mut g = Graph::with_vertices(5);
266 g.add_edge(0, 1).unwrap();
267 g.add_edge(1, 2).unwrap();
268 g.add_edge(2, 0).unwrap();
269 g.add_edge(0, 3).unwrap();
270 g.add_edge(3, 4).unwrap();
271 g.add_edge(4, 0).unwrap();
272 assert!(is_apex_forest(&g).unwrap());
273 }
274}