rust_igraph/algorithms/properties/
is_chain_graph.rs1use crate::algorithms::properties::is_bipartite::is_bipartite;
11use crate::core::{Graph, IgraphResult};
12
13pub fn is_chain_graph(graph: &Graph) -> IgraphResult<bool> {
45 if graph.is_directed() {
46 return Ok(false);
47 }
48
49 let n = graph.vcount();
50 if n <= 2 {
51 return is_bipartite(graph).map(|r| r.is_bipartite);
52 }
53
54 let bip = is_bipartite(graph)?;
55 if !bip.is_bipartite {
56 return Ok(false);
57 }
58
59 let n_usize = n as usize;
60 let types = &bip.types;
61
62 let mut adj = vec![vec![false; n_usize]; n_usize];
63 for v in 0..n {
64 let nbrs = graph.neighbors(v)?;
65 for &w in &nbrs {
66 adj[v as usize][w as usize] = true;
67 }
68 }
69
70 let part_a: Vec<usize> = (0..n_usize).filter(|&v| !types[v]).collect();
72 let part_b: Vec<usize> = (0..n_usize).filter(|&v| types[v]).collect();
73
74 if !neighborhoods_nested(&adj, &part_a, &part_b) {
77 return Ok(false);
78 }
79
80 if !neighborhoods_nested(&adj, &part_b, &part_a) {
83 return Ok(false);
84 }
85
86 Ok(true)
87}
88
89fn neighborhoods_nested(adj: &[Vec<bool>], part: &[usize], other_part: &[usize]) -> bool {
92 if part.len() <= 1 {
93 return true;
94 }
95
96 let mut nbr_sets: Vec<Vec<usize>> = part
98 .iter()
99 .map(|&v| other_part.iter().copied().filter(|&u| adj[v][u]).collect())
100 .collect();
101
102 nbr_sets.sort_by_key(Vec::len);
104
105 for window in nbr_sets.windows(2) {
107 let smaller = &window[0];
108 let larger = &window[1];
109 if !is_subset(smaller, larger) {
110 return false;
111 }
112 }
113
114 true
115}
116
117fn is_subset(a: &[usize], b: &[usize]) -> bool {
119 if a.len() > b.len() {
120 return false;
121 }
122 let mut bi = 0;
123 for &x in a {
124 while bi < b.len() && b[bi] < x {
125 bi += 1;
126 }
127 if bi >= b.len() || b[bi] != x {
128 return false;
129 }
130 bi += 1;
131 }
132 true
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn empty_graph() {
141 let g = Graph::with_vertices(0);
142 assert!(is_chain_graph(&g).unwrap());
143 }
144
145 #[test]
146 fn single_vertex() {
147 let g = Graph::with_vertices(1);
148 assert!(is_chain_graph(&g).unwrap());
149 }
150
151 #[test]
152 fn single_edge() {
153 let mut g = Graph::with_vertices(2);
154 g.add_edge(0, 1).unwrap();
155 assert!(is_chain_graph(&g).unwrap());
156 }
157
158 #[test]
159 fn path_p3() {
160 let mut g = Graph::with_vertices(3);
162 g.add_edge(0, 1).unwrap();
163 g.add_edge(1, 2).unwrap();
164 assert!(is_chain_graph(&g).unwrap());
165 }
166
167 #[test]
168 fn path_p4() {
169 let mut g = Graph::with_vertices(4);
172 g.add_edge(0, 1).unwrap();
173 g.add_edge(1, 2).unwrap();
174 g.add_edge(2, 3).unwrap();
175 assert!(is_chain_graph(&g).unwrap());
176 }
177
178 #[test]
179 fn complete_bipartite_k23() {
180 let mut g = Graph::with_vertices(5);
181 g.add_edge(0, 2).unwrap();
182 g.add_edge(0, 3).unwrap();
183 g.add_edge(0, 4).unwrap();
184 g.add_edge(1, 2).unwrap();
185 g.add_edge(1, 3).unwrap();
186 g.add_edge(1, 4).unwrap();
187 assert!(is_chain_graph(&g).unwrap());
188 }
189
190 #[test]
191 fn c4_not_chain() {
192 let mut g = Graph::with_vertices(4);
196 g.add_edge(0, 1).unwrap();
197 g.add_edge(1, 2).unwrap();
198 g.add_edge(2, 3).unwrap();
199 g.add_edge(3, 0).unwrap();
200 assert!(is_chain_graph(&g).unwrap());
201 }
202
203 #[test]
204 fn c6_not_chain() {
205 let mut g = Graph::with_vertices(6);
207 g.add_edge(0, 1).unwrap();
208 g.add_edge(1, 2).unwrap();
209 g.add_edge(2, 3).unwrap();
210 g.add_edge(3, 4).unwrap();
211 g.add_edge(4, 5).unwrap();
212 g.add_edge(5, 0).unwrap();
213 assert!(!is_chain_graph(&g).unwrap());
214 }
215
216 #[test]
217 fn star() {
218 let mut g = Graph::with_vertices(4);
221 g.add_edge(0, 1).unwrap();
222 g.add_edge(0, 2).unwrap();
223 g.add_edge(0, 3).unwrap();
224 assert!(is_chain_graph(&g).unwrap());
225 }
226
227 #[test]
228 fn two_disjoint_edges() {
229 let mut g = Graph::with_vertices(4);
232 g.add_edge(0, 1).unwrap();
233 g.add_edge(2, 3).unwrap();
234 assert!(!is_chain_graph(&g).unwrap());
235 }
236
237 #[test]
238 fn triangle_not_bipartite() {
239 let mut g = Graph::with_vertices(3);
240 g.add_edge(0, 1).unwrap();
241 g.add_edge(1, 2).unwrap();
242 g.add_edge(2, 0).unwrap();
243 assert!(!is_chain_graph(&g).unwrap());
244 }
245
246 #[test]
247 fn directed_returns_false() {
248 let mut g = Graph::new(2, true).unwrap();
249 g.add_edge(0, 1).unwrap();
250 assert!(!is_chain_graph(&g).unwrap());
251 }
252
253 #[test]
254 fn isolated_vertices() {
255 let g = Graph::with_vertices(3);
257 assert!(is_chain_graph(&g).unwrap());
258 }
259
260 #[test]
261 fn k33_not_chain() {
262 let mut g = Graph::with_vertices(6);
264 for i in 0..3u32 {
265 for j in 3..6u32 {
266 g.add_edge(i, j).unwrap();
267 }
268 }
269 assert!(is_chain_graph(&g).unwrap());
270 }
271
272 #[test]
273 fn bipartite_with_induced_2k2() {
274 let mut g = Graph::with_vertices(4);
281 g.add_edge(0, 2).unwrap();
282 g.add_edge(1, 3).unwrap();
283 assert!(!is_chain_graph(&g).unwrap());
284 }
285
286 #[test]
287 fn nested_neighborhoods() {
288 let mut g = Graph::with_vertices(6);
295 g.add_edge(0, 3).unwrap();
296 g.add_edge(0, 4).unwrap();
297 g.add_edge(1, 3).unwrap();
298 g.add_edge(1, 4).unwrap();
299 g.add_edge(1, 5).unwrap();
300 g.add_edge(2, 3).unwrap();
301 g.add_edge(2, 4).unwrap();
302 g.add_edge(2, 5).unwrap();
303 assert!(is_chain_graph(&g).unwrap());
304 }
305}