rust_igraph/algorithms/properties/
is_trivially_perfect.rs1use crate::core::{Graph, IgraphResult};
11
12pub fn is_trivially_perfect(graph: &Graph) -> IgraphResult<bool> {
40 if graph.is_directed() {
41 return Ok(false);
42 }
43
44 let n = graph.vcount();
45 if n <= 2 {
46 return Ok(true);
47 }
48
49 let n_usize = n as usize;
63 let mut adj = vec![vec![false; n_usize]; n_usize];
64 for v in 0..n {
65 let nbrs = graph.neighbors(v)?;
66 for &w in &nbrs {
67 adj[v as usize][w as usize] = true;
68 }
69 }
70
71 let mut visited = vec![false; n_usize];
73 let mut component = Vec::new();
74
75 for start in 0..n {
76 if visited[start as usize] {
77 continue;
78 }
79
80 component.clear();
82 let mut queue = std::collections::VecDeque::new();
83 queue.push_back(start);
84 visited[start as usize] = true;
85
86 while let Some(v) = queue.pop_front() {
87 component.push(v);
88 for w in 0..n {
89 if !visited[w as usize] && adj[v as usize][w as usize] {
90 visited[w as usize] = true;
91 queue.push_back(w);
92 }
93 }
94 }
95
96 if !check_component_trivially_perfect(&adj, &component) {
98 return Ok(false);
99 }
100 }
101
102 Ok(true)
103}
104
105fn check_component_trivially_perfect(adj: &[Vec<bool>], component: &[u32]) -> bool {
106 let size = component.len();
107 if size <= 2 {
108 return true;
109 }
110
111 let mut universal = None;
113 for &v in component {
114 let deg_in_comp = component
115 .iter()
116 .filter(|&&w| w != v && adj[v as usize][w as usize])
117 .count();
118 if deg_in_comp == size - 1 {
119 universal = Some(v);
120 break;
121 }
122 }
123
124 let Some(univ) = universal else {
125 return false;
126 };
127
128 let remaining: Vec<u32> = component.iter().copied().filter(|&v| v != univ).collect();
130
131 let mut rem_visited = vec![false; remaining.len()];
133 let mut sub_component = Vec::new();
134
135 for i in 0..remaining.len() {
136 if rem_visited[i] {
137 continue;
138 }
139
140 sub_component.clear();
141 let mut queue = std::collections::VecDeque::new();
142 queue.push_back(i);
143 rem_visited[i] = true;
144
145 while let Some(idx) = queue.pop_front() {
146 sub_component.push(remaining[idx]);
147 for j in 0..remaining.len() {
148 if !rem_visited[j] && adj[remaining[idx] as usize][remaining[j] as usize] {
149 rem_visited[j] = true;
150 queue.push_back(j);
151 }
152 }
153 }
154
155 if !check_component_trivially_perfect(adj, &sub_component) {
156 return false;
157 }
158 }
159
160 true
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn empty_graph() {
169 let g = Graph::with_vertices(0);
170 assert!(is_trivially_perfect(&g).unwrap());
171 }
172
173 #[test]
174 fn single_vertex() {
175 let g = Graph::with_vertices(1);
176 assert!(is_trivially_perfect(&g).unwrap());
177 }
178
179 #[test]
180 fn single_edge() {
181 let mut g = Graph::with_vertices(2);
182 g.add_edge(0, 1).unwrap();
183 assert!(is_trivially_perfect(&g).unwrap());
184 }
185
186 #[test]
187 fn triangle() {
188 let mut g = Graph::with_vertices(3);
189 g.add_edge(0, 1).unwrap();
190 g.add_edge(1, 2).unwrap();
191 g.add_edge(2, 0).unwrap();
192 assert!(is_trivially_perfect(&g).unwrap());
193 }
194
195 #[test]
196 fn star_k14() {
197 let mut g = Graph::with_vertices(5);
198 g.add_edge(0, 1).unwrap();
199 g.add_edge(0, 2).unwrap();
200 g.add_edge(0, 3).unwrap();
201 g.add_edge(0, 4).unwrap();
202 assert!(is_trivially_perfect(&g).unwrap());
203 }
204
205 #[test]
206 fn complete_k4() {
207 let mut g = Graph::with_vertices(4);
208 g.add_edge(0, 1).unwrap();
209 g.add_edge(0, 2).unwrap();
210 g.add_edge(0, 3).unwrap();
211 g.add_edge(1, 2).unwrap();
212 g.add_edge(1, 3).unwrap();
213 g.add_edge(2, 3).unwrap();
214 assert!(is_trivially_perfect(&g).unwrap());
215 }
216
217 #[test]
218 fn p4_not_trivially_perfect() {
219 let mut g = Graph::with_vertices(4);
221 g.add_edge(0, 1).unwrap();
222 g.add_edge(1, 2).unwrap();
223 g.add_edge(2, 3).unwrap();
224 assert!(!is_trivially_perfect(&g).unwrap());
225 }
226
227 #[test]
228 fn c4_not_trivially_perfect() {
229 let mut g = Graph::with_vertices(4);
231 g.add_edge(0, 1).unwrap();
232 g.add_edge(1, 2).unwrap();
233 g.add_edge(2, 3).unwrap();
234 g.add_edge(3, 0).unwrap();
235 assert!(!is_trivially_perfect(&g).unwrap());
236 }
237
238 #[test]
239 fn c5_not_trivially_perfect() {
240 let mut g = Graph::with_vertices(5);
241 g.add_edge(0, 1).unwrap();
242 g.add_edge(1, 2).unwrap();
243 g.add_edge(2, 3).unwrap();
244 g.add_edge(3, 4).unwrap();
245 g.add_edge(4, 0).unwrap();
246 assert!(!is_trivially_perfect(&g).unwrap());
247 }
248
249 #[test]
250 fn disconnected_trivially_perfect() {
251 let mut g = Graph::with_vertices(4);
253 g.add_edge(0, 1).unwrap();
254 g.add_edge(2, 3).unwrap();
255 assert!(is_trivially_perfect(&g).unwrap());
256 }
257
258 #[test]
259 fn disconnected_with_p4() {
260 let mut g = Graph::with_vertices(7);
262 g.add_edge(0, 1).unwrap();
263 g.add_edge(1, 2).unwrap();
264 g.add_edge(2, 0).unwrap();
265 g.add_edge(3, 4).unwrap();
266 g.add_edge(4, 5).unwrap();
267 g.add_edge(5, 6).unwrap();
268 assert!(!is_trivially_perfect(&g).unwrap());
269 }
270
271 #[test]
272 fn fan_with_p4_not_trivially_perfect() {
273 let mut g = Graph::with_vertices(5);
276 g.add_edge(0, 1).unwrap();
277 g.add_edge(0, 2).unwrap();
278 g.add_edge(0, 3).unwrap();
279 g.add_edge(0, 4).unwrap();
280 g.add_edge(1, 2).unwrap();
281 g.add_edge(2, 3).unwrap();
282 g.add_edge(3, 4).unwrap();
283 assert!(!is_trivially_perfect(&g).unwrap());
284 }
285
286 #[test]
287 fn fan_with_p3_trivially_perfect() {
288 let mut g = Graph::with_vertices(4);
292 g.add_edge(0, 1).unwrap();
293 g.add_edge(0, 2).unwrap();
294 g.add_edge(0, 3).unwrap();
295 g.add_edge(1, 2).unwrap();
296 g.add_edge(2, 3).unwrap();
297 assert!(is_trivially_perfect(&g).unwrap());
298 }
299
300 #[test]
301 fn nested_stars() {
302 let mut g = Graph::with_vertices(4);
305 g.add_edge(0, 1).unwrap();
306 g.add_edge(0, 2).unwrap();
307 g.add_edge(0, 3).unwrap();
308 g.add_edge(1, 2).unwrap();
309 g.add_edge(1, 3).unwrap();
310 assert!(is_trivially_perfect(&g).unwrap());
311 }
312
313 #[test]
314 fn directed_returns_false() {
315 let mut g = Graph::new(3, true).unwrap();
316 g.add_edge(0, 1).unwrap();
317 g.add_edge(1, 2).unwrap();
318 g.add_edge(2, 0).unwrap();
319 assert!(!is_trivially_perfect(&g).unwrap());
320 }
321
322 #[test]
323 fn bull_graph_not_trivially_perfect() {
324 let mut g = Graph::with_vertices(5);
327 g.add_edge(0, 1).unwrap();
328 g.add_edge(0, 2).unwrap();
329 g.add_edge(1, 2).unwrap();
330 g.add_edge(1, 3).unwrap();
331 g.add_edge(2, 4).unwrap();
332 assert!(!is_trivially_perfect(&g).unwrap());
333 }
334
335 #[test]
336 fn isolated_vertices() {
337 let g = Graph::with_vertices(5);
338 assert!(is_trivially_perfect(&g).unwrap());
339 }
340}