rust_igraph/algorithms/properties/
is_bowtie_free.rs1use crate::core::{Graph, IgraphResult};
10
11pub fn is_bowtie_free(graph: &Graph) -> IgraphResult<bool> {
42 if graph.is_directed() {
43 return Ok(false);
44 }
45
46 let n = graph.vcount();
47 if n < 5 {
48 return Ok(true);
49 }
50
51 let n_usize = n as usize;
52 let mut adj = vec![vec![false; n_usize]; n_usize];
53 let mut nbrs_list: Vec<Vec<u32>> = Vec::with_capacity(n_usize);
54
55 for v in 0..n {
56 let nbrs = graph.neighbors(v)?;
57 for &w in &nbrs {
58 adj[v as usize][w as usize] = true;
59 }
60 nbrs_list.push(nbrs);
61 }
62
63 for c in 0..n {
72 let ci = c as usize;
73 let cn = &nbrs_list[ci];
74
75 let mut triangle_edges: Vec<(usize, usize)> = Vec::new();
77 for (idx, &u) in cn.iter().enumerate() {
78 let ui = u as usize;
79 for &v in &cn[(idx + 1)..] {
80 let vi = v as usize;
81 if adj[ui][vi] {
82 triangle_edges.push((ui, vi));
83 }
84 }
85 }
86
87 if triangle_edges.len() < 2 {
88 continue;
89 }
90
91 for (i, &(a, b)) in triangle_edges.iter().enumerate() {
93 for &(d, e) in &triangle_edges[(i + 1)..] {
94 if a == d || a == e || b == d || b == e {
95 continue;
96 }
97 if !adj[a][d] && !adj[a][e] && !adj[b][d] && !adj[b][e] {
98 return Ok(false);
99 }
100 }
101 }
102 }
103
104 Ok(true)
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn empty_graph() {
113 let g = Graph::with_vertices(0);
114 assert!(is_bowtie_free(&g).unwrap());
115 }
116
117 #[test]
118 fn small_graphs() {
119 let g = Graph::with_vertices(4);
120 assert!(is_bowtie_free(&g).unwrap());
121 }
122
123 #[test]
124 fn triangle() {
125 let mut g = Graph::with_vertices(3);
126 g.add_edge(0, 1).unwrap();
127 g.add_edge(1, 2).unwrap();
128 g.add_edge(2, 0).unwrap();
129 assert!(is_bowtie_free(&g).unwrap());
130 }
131
132 #[test]
133 fn bowtie() {
134 let mut g = Graph::with_vertices(5);
136 g.add_edge(0, 1).unwrap();
137 g.add_edge(0, 2).unwrap();
138 g.add_edge(1, 2).unwrap();
139 g.add_edge(2, 3).unwrap();
140 g.add_edge(2, 4).unwrap();
141 g.add_edge(3, 4).unwrap();
142 assert!(!is_bowtie_free(&g).unwrap());
143 }
144
145 #[test]
146 fn k4_bowtie_free() {
147 let mut g = Graph::with_vertices(4);
150 for i in 0..4u32 {
151 for j in (i + 1)..4 {
152 g.add_edge(i, j).unwrap();
153 }
154 }
155 assert!(is_bowtie_free(&g).unwrap());
156 }
157
158 #[test]
159 fn k5_bowtie_free() {
160 let mut g = Graph::with_vertices(5);
163 for i in 0..5u32 {
164 for j in (i + 1)..5 {
165 g.add_edge(i, j).unwrap();
166 }
167 }
168 assert!(is_bowtie_free(&g).unwrap());
169 }
170
171 #[test]
172 fn diamond_bowtie_free() {
173 let mut g = Graph::with_vertices(4);
175 g.add_edge(0, 1).unwrap();
176 g.add_edge(0, 2).unwrap();
177 g.add_edge(0, 3).unwrap();
178 g.add_edge(1, 2).unwrap();
179 g.add_edge(1, 3).unwrap();
180 assert!(is_bowtie_free(&g).unwrap());
181 }
182
183 #[test]
184 fn two_disjoint_triangles_bowtie_free() {
185 let mut g = Graph::with_vertices(6);
187 g.add_edge(0, 1).unwrap();
188 g.add_edge(1, 2).unwrap();
189 g.add_edge(2, 0).unwrap();
190 g.add_edge(3, 4).unwrap();
191 g.add_edge(4, 5).unwrap();
192 g.add_edge(5, 3).unwrap();
193 assert!(is_bowtie_free(&g).unwrap());
194 }
195
196 #[test]
197 fn two_triangles_sharing_edge_bowtie_free() {
198 let mut g = Graph::with_vertices(4);
201 g.add_edge(0, 1).unwrap();
202 g.add_edge(0, 2).unwrap();
203 g.add_edge(1, 2).unwrap();
204 g.add_edge(0, 3).unwrap();
205 g.add_edge(1, 3).unwrap();
206 assert!(is_bowtie_free(&g).unwrap());
207 }
208
209 #[test]
210 fn bowtie_with_extra_cross_edge() {
211 let mut g = Graph::with_vertices(5);
214 g.add_edge(0, 1).unwrap();
215 g.add_edge(0, 2).unwrap();
216 g.add_edge(1, 2).unwrap();
217 g.add_edge(2, 3).unwrap();
218 g.add_edge(2, 4).unwrap();
219 g.add_edge(3, 4).unwrap();
220 g.add_edge(0, 3).unwrap();
221 assert!(is_bowtie_free(&g).unwrap());
222 }
223
224 #[test]
225 fn directed_returns_false() {
226 let mut g = Graph::new(3, true).unwrap();
227 g.add_edge(0, 1).unwrap();
228 g.add_edge(1, 2).unwrap();
229 g.add_edge(2, 0).unwrap();
230 assert!(!is_bowtie_free(&g).unwrap());
231 }
232
233 #[test]
234 fn path_bowtie_free() {
235 let mut g = Graph::with_vertices(5);
236 g.add_edge(0, 1).unwrap();
237 g.add_edge(1, 2).unwrap();
238 g.add_edge(2, 3).unwrap();
239 g.add_edge(3, 4).unwrap();
240 assert!(is_bowtie_free(&g).unwrap());
241 }
242
243 #[test]
244 fn star_bowtie_free() {
245 let mut g = Graph::with_vertices(6);
246 g.add_edge(0, 1).unwrap();
247 g.add_edge(0, 2).unwrap();
248 g.add_edge(0, 3).unwrap();
249 g.add_edge(0, 4).unwrap();
250 g.add_edge(0, 5).unwrap();
251 assert!(is_bowtie_free(&g).unwrap());
252 }
253
254 #[test]
255 fn triple_bowtie() {
256 let mut g = Graph::with_vertices(7);
259 g.add_edge(0, 1).unwrap();
260 g.add_edge(0, 2).unwrap();
261 g.add_edge(1, 2).unwrap();
262 g.add_edge(0, 3).unwrap();
263 g.add_edge(0, 4).unwrap();
264 g.add_edge(3, 4).unwrap();
265 g.add_edge(0, 5).unwrap();
266 g.add_edge(0, 6).unwrap();
267 g.add_edge(5, 6).unwrap();
268 assert!(!is_bowtie_free(&g).unwrap());
269 }
270}