rust_igraph/algorithms/properties/
is_outerplanar.rs1use crate::algorithms::properties::is_series_parallel::is_series_parallel;
20use crate::core::{Graph, IgraphResult};
21
22pub fn is_outerplanar(graph: &Graph) -> IgraphResult<bool> {
53 let n = graph.vcount();
54 if n <= 3 {
55 return is_series_parallel(graph);
56 }
57
58 if !is_series_parallel(graph)? {
59 return Ok(false);
60 }
61
62 let e = graph.ecount();
64 let n_usize = n as usize;
65 if e > 2 * n_usize - 3 {
66 return Ok(false);
67 }
68
69 has_no_k23_minor(graph)
70}
71
72fn has_no_k23_minor(graph: &Graph) -> IgraphResult<bool> {
82 let n = graph.vcount() as usize;
83
84 if n < 5 {
86 return Ok(true);
87 }
88
89 let mut adj: Vec<Vec<(usize, bool)>> = vec![vec![]; n];
90 for v in 0..graph.vcount() {
91 let nbrs = graph.neighbors(v)?;
92 for &w in &nbrs {
93 let vv = v as usize;
94 let ww = w as usize;
95 if vv < ww {
96 adj[vv].push((ww, false));
97 adj[ww].push((vv, false));
98 }
99 }
100 }
101
102 Ok(!k23_reduce_check(&mut adj, n))
103}
104
105fn k23_reduce_check(adj: &mut [Vec<(usize, bool)>], n: usize) -> bool {
107 loop {
108 for edges in adj.iter() {
110 let mut syn_count = std::collections::HashMap::new();
111 for &(w, syn) in edges {
112 if syn {
113 *syn_count.entry(w).or_insert(0usize) += 1;
114 }
115 }
116 for &count in syn_count.values() {
117 if count >= 3 {
118 return true;
119 }
120 }
121 }
122
123 let mut changed = false;
124
125 for v in 0..n {
127 if adj[v].is_empty() {
128 continue;
129 }
130 let distinct = distinct_neighbors(&adj[v]);
131 if distinct.len() <= 1 {
132 changed = true;
133 for &w in &distinct {
134 adj[w].retain(|&(x, _)| x != v);
135 }
136 adj[v].clear();
137 }
138 }
139
140 for v in 0..n {
142 if adj[v].is_empty() {
143 continue;
144 }
145 let distinct = distinct_neighbors(&adj[v]);
146 if distinct.len() == 2 {
147 let a = distinct[0];
148 let b = distinct[1];
149 changed = true;
150 adj[v].clear();
151 adj[a].retain(|&(x, _)| x != v);
152 adj[b].retain(|&(x, _)| x != v);
153 adj[a].push((b, true));
154 adj[b].push((a, true));
155 }
156 }
157
158 if !changed {
159 break;
160 }
161 }
162
163 false
164}
165
166fn distinct_neighbors(edges: &[(usize, bool)]) -> Vec<usize> {
167 let mut d: Vec<usize> = edges.iter().map(|&(w, _)| w).collect();
168 d.sort_unstable();
169 d.dedup();
170 d
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn empty_graph() {
179 let g = Graph::with_vertices(0);
180 assert!(is_outerplanar(&g).unwrap());
181 }
182
183 #[test]
184 fn single_vertex() {
185 let g = Graph::with_vertices(1);
186 assert!(is_outerplanar(&g).unwrap());
187 }
188
189 #[test]
190 fn single_edge() {
191 let mut g = Graph::with_vertices(2);
192 g.add_edge(0, 1).unwrap();
193 assert!(is_outerplanar(&g).unwrap());
194 }
195
196 #[test]
197 fn triangle() {
198 let mut g = Graph::with_vertices(3);
199 g.add_edge(0, 1).unwrap();
200 g.add_edge(1, 2).unwrap();
201 g.add_edge(2, 0).unwrap();
202 assert!(is_outerplanar(&g).unwrap());
203 }
204
205 #[test]
206 fn path() {
207 let mut g = Graph::with_vertices(5);
208 g.add_edge(0, 1).unwrap();
209 g.add_edge(1, 2).unwrap();
210 g.add_edge(2, 3).unwrap();
211 g.add_edge(3, 4).unwrap();
212 assert!(is_outerplanar(&g).unwrap());
213 }
214
215 #[test]
216 fn cycle_c5() {
217 let mut g = Graph::with_vertices(5);
218 g.add_edge(0, 1).unwrap();
219 g.add_edge(1, 2).unwrap();
220 g.add_edge(2, 3).unwrap();
221 g.add_edge(3, 4).unwrap();
222 g.add_edge(4, 0).unwrap();
223 assert!(is_outerplanar(&g).unwrap());
224 }
225
226 #[test]
227 fn k4_not_outerplanar() {
228 let mut g = Graph::with_vertices(4);
229 for i in 0..4u32 {
230 for j in (i + 1)..4 {
231 g.add_edge(i, j).unwrap();
232 }
233 }
234 assert!(!is_outerplanar(&g).unwrap());
235 }
236
237 #[test]
238 fn k23_not_outerplanar() {
239 let mut g = Graph::with_vertices(5);
241 g.add_edge(0, 2).unwrap();
242 g.add_edge(0, 3).unwrap();
243 g.add_edge(0, 4).unwrap();
244 g.add_edge(1, 2).unwrap();
245 g.add_edge(1, 3).unwrap();
246 g.add_edge(1, 4).unwrap();
247 assert!(!is_outerplanar(&g).unwrap());
248 }
249
250 #[test]
251 fn diamond_outerplanar() {
252 let mut g = Graph::with_vertices(4);
254 g.add_edge(0, 1).unwrap();
255 g.add_edge(0, 2).unwrap();
256 g.add_edge(0, 3).unwrap();
257 g.add_edge(1, 2).unwrap();
258 g.add_edge(1, 3).unwrap();
259 assert!(is_outerplanar(&g).unwrap());
260 }
261
262 #[test]
263 fn star() {
264 let mut g = Graph::with_vertices(5);
265 g.add_edge(0, 1).unwrap();
266 g.add_edge(0, 2).unwrap();
267 g.add_edge(0, 3).unwrap();
268 g.add_edge(0, 4).unwrap();
269 assert!(is_outerplanar(&g).unwrap());
270 }
271
272 #[test]
273 fn tree() {
274 let mut g = Graph::with_vertices(6);
275 g.add_edge(0, 1).unwrap();
276 g.add_edge(1, 2).unwrap();
277 g.add_edge(1, 3).unwrap();
278 g.add_edge(3, 4).unwrap();
279 g.add_edge(3, 5).unwrap();
280 assert!(is_outerplanar(&g).unwrap());
281 }
282
283 #[test]
284 fn edgeless() {
285 let g = Graph::with_vertices(5);
286 assert!(is_outerplanar(&g).unwrap());
287 }
288
289 #[test]
290 fn maximal_outerplanar_5() {
291 let mut g = Graph::with_vertices(5);
293 g.add_edge(0, 1).unwrap();
294 g.add_edge(1, 2).unwrap();
295 g.add_edge(2, 3).unwrap();
296 g.add_edge(3, 4).unwrap();
297 g.add_edge(4, 0).unwrap();
298 g.add_edge(0, 2).unwrap();
299 g.add_edge(0, 3).unwrap();
300 assert!(is_outerplanar(&g).unwrap());
301 }
302
303 #[test]
304 fn petersen_not_outerplanar() {
305 let mut g = Graph::with_vertices(10);
306 let edges = [
307 (0, 1),
308 (1, 2),
309 (2, 3),
310 (3, 4),
311 (4, 0),
312 (5, 7),
313 (7, 9),
314 (9, 6),
315 (6, 8),
316 (8, 5),
317 (0, 5),
318 (1, 6),
319 (2, 7),
320 (3, 8),
321 (4, 9),
322 ];
323 for (u, v) in edges {
324 g.add_edge(u, v).unwrap();
325 }
326 assert!(!is_outerplanar(&g).unwrap());
327 }
328
329 #[test]
330 fn directed_treated_as_undirected() {
331 let mut g = Graph::new(5, true).unwrap();
332 g.add_edge(0, 1).unwrap();
333 g.add_edge(1, 2).unwrap();
334 g.add_edge(2, 3).unwrap();
335 g.add_edge(3, 4).unwrap();
336 g.add_edge(4, 0).unwrap();
337 assert!(is_outerplanar(&g).unwrap());
338 }
339
340 #[test]
341 fn disconnected_outerplanar() {
342 let mut g = Graph::with_vertices(6);
343 g.add_edge(0, 1).unwrap();
344 g.add_edge(1, 2).unwrap();
345 g.add_edge(2, 0).unwrap();
346 g.add_edge(3, 4).unwrap();
347 g.add_edge(4, 5).unwrap();
348 g.add_edge(5, 3).unwrap();
349 assert!(is_outerplanar(&g).unwrap());
350 }
351
352 #[test]
353 fn subdivided_k23_not_outerplanar() {
354 let mut g = Graph::with_vertices(6);
357 g.add_edge(0, 5).unwrap();
358 g.add_edge(5, 2).unwrap();
359 g.add_edge(0, 3).unwrap();
360 g.add_edge(0, 4).unwrap();
361 g.add_edge(1, 2).unwrap();
362 g.add_edge(1, 3).unwrap();
363 g.add_edge(1, 4).unwrap();
364 assert!(!is_outerplanar(&g).unwrap());
365 }
366
367 #[test]
368 fn three_parallel_paths_with_direct_edge() {
369 let mut g = Graph::with_vertices(5);
371 g.add_edge(0, 1).unwrap();
372 g.add_edge(0, 2).unwrap();
373 g.add_edge(2, 1).unwrap();
374 g.add_edge(0, 3).unwrap();
375 g.add_edge(3, 4).unwrap();
376 g.add_edge(4, 1).unwrap();
377 assert!(is_outerplanar(&g).unwrap());
378 }
379}