1#![allow(
12 clippy::cast_possible_truncation,
13 clippy::cast_precision_loss,
14 clippy::many_single_char_names,
15 clippy::needless_range_loop,
16 clippy::too_many_lines
17)]
18
19use crate::core::{Graph, IgraphResult};
20
21pub fn is_clique_cover(graph: &Graph, parts: &[Vec<u32>]) -> IgraphResult<bool> {
38 let n = graph.vcount() as usize;
39 let mut seen = vec![false; n];
40
41 let adj = build_adj(graph, n);
42
43 for part in parts {
44 for &v in part {
45 let vi = v as usize;
46 if vi >= n || seen[vi] {
47 return Ok(false);
48 }
49 seen[vi] = true;
50 }
51
52 for i in 0..part.len() {
53 for j in (i + 1)..part.len() {
54 let ui = part[i] as usize;
55 let vi = part[j] as usize;
56 if !adj[ui * n + vi] {
57 return Ok(false);
58 }
59 }
60 }
61 }
62
63 if seen.iter().any(|&s| !s) {
64 return Ok(false);
65 }
66
67 Ok(true)
68}
69
70pub fn greedy_clique_cover(graph: &Graph) -> IgraphResult<Vec<Vec<u32>>> {
89 let n = graph.vcount() as usize;
90 if n == 0 {
91 return Ok(Vec::new());
92 }
93
94 let adj = build_adj(graph, n);
95
96 let mut degrees: Vec<(usize, u32)> = (0..n as u32)
97 .map(|v| {
98 let d = graph.degree(v).unwrap_or(0);
99 (d, v)
100 })
101 .collect();
102 degrees.sort_by_key(|b| std::cmp::Reverse(b.0));
103
104 let mut cliques: Vec<Vec<u32>> = Vec::new();
105
106 for &(_, v) in °rees {
107 let vi = v as usize;
108 let mut placed = false;
109
110 for clique in &mut cliques {
111 let fits = clique.iter().all(|&u| {
112 let ui = u as usize;
113 adj[vi * n + ui]
114 });
115 if fits {
116 clique.push(v);
117 placed = true;
118 break;
119 }
120 }
121
122 if !placed {
123 cliques.push(vec![v]);
124 }
125 }
126
127 for clique in &mut cliques {
128 clique.sort_unstable();
129 }
130
131 Ok(cliques)
132}
133
134pub fn clique_cover_number(graph: &Graph) -> IgraphResult<u32> {
151 let n = graph.vcount() as usize;
152 if n == 0 {
153 return Ok(0);
154 }
155
156 let adj = build_adj(graph, n);
157
158 let maximal_cliques = find_all_maximal_cliques(&adj, n);
159
160 let mut best = n as u32;
161 cover_search(&maximal_cliques, &mut vec![false; n], 0, 0, &mut best);
162
163 Ok(best)
164}
165
166fn build_adj(graph: &Graph, n: usize) -> Vec<bool> {
167 let mut adj = vec![false; n * n];
168 for (u, v) in graph.edges() {
169 let ui = u as usize;
170 let vi = v as usize;
171 adj[ui * n + vi] = true;
172 if !graph.is_directed() {
173 adj[vi * n + ui] = true;
174 }
175 }
176 adj
177}
178
179fn find_all_maximal_cliques(adj: &[bool], n: usize) -> Vec<Vec<usize>> {
180 let mut cliques = Vec::new();
181 let mut current = Vec::new();
182 let candidates: Vec<usize> = (0..n).collect();
183 let excluded: Vec<usize> = Vec::new();
184 bron_kerbosch(adj, n, &mut current, &candidates, &excluded, &mut cliques);
185 cliques.push(Vec::new());
186 cliques.retain(|c| !c.is_empty());
187
188 for v in 0..n {
189 let mut found = false;
190 for c in &cliques {
191 if c.contains(&v) {
192 found = true;
193 break;
194 }
195 }
196 if !found {
197 cliques.push(vec![v]);
198 }
199 }
200
201 cliques
202}
203
204fn bron_kerbosch(
205 adj: &[bool],
206 n: usize,
207 current: &mut Vec<usize>,
208 candidates: &[usize],
209 excluded: &[usize],
210 results: &mut Vec<Vec<usize>>,
211) {
212 if candidates.is_empty() && excluded.is_empty() {
213 if !current.is_empty() {
214 results.push(current.clone());
215 }
216 return;
217 }
218
219 let mut cand = candidates.to_vec();
220 let mut excl = excluded.to_vec();
221
222 let pivot = cand
223 .iter()
224 .chain(excl.iter())
225 .max_by_key(|&&v| cand.iter().filter(|&&u| adj[v * n + u]).count())
226 .copied();
227
228 let Some(pivot_v) = pivot else { return };
229
230 let to_try: Vec<usize> = cand
231 .iter()
232 .filter(|&&v| !adj[pivot_v * n + v])
233 .copied()
234 .collect();
235
236 for v in to_try {
237 let new_cand: Vec<usize> = cand.iter().filter(|&&u| adj[v * n + u]).copied().collect();
238 let new_excl: Vec<usize> = excl.iter().filter(|&&u| adj[v * n + u]).copied().collect();
239
240 current.push(v);
241 bron_kerbosch(adj, n, current, &new_cand, &new_excl, results);
242 current.pop();
243
244 cand.retain(|&u| u != v);
245 excl.push(v);
246 }
247}
248
249fn cover_search(
250 cliques: &[Vec<usize>],
251 covered: &mut Vec<bool>,
252 depth: u32,
253 start: usize,
254 best: &mut u32,
255) {
256 if covered.iter().all(|&c| c) {
257 if depth < *best {
258 *best = depth;
259 }
260 return;
261 }
262
263 if depth.saturating_add(1) >= *best {
264 return;
265 }
266
267 let uncovered = covered.iter().position(|&c| !c);
268 let Some(target) = uncovered else { return };
269
270 for i in start..cliques.len() {
271 if !cliques[i].contains(&target) {
272 continue;
273 }
274
275 let any_already_uncovered = cliques[i].iter().any(|&v| !covered[v]);
276 if !any_already_uncovered {
277 continue;
278 }
279
280 let mut restored = Vec::new();
281 for &v in &cliques[i] {
282 if !covered[v] {
283 covered[v] = true;
284 restored.push(v);
285 }
286 }
287
288 cover_search(cliques, covered, depth.saturating_add(1), i + 1, best);
289
290 for v in restored {
291 covered[v] = false;
292 }
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 fn path4() -> Graph {
301 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
302 }
303
304 fn k3() -> Graph {
305 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
306 }
307
308 fn k4() -> Graph {
309 Graph::from_edges(
310 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
311 false,
312 Some(4),
313 )
314 .unwrap()
315 }
316
317 fn cycle4() -> Graph {
318 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
319 }
320
321 fn cycle5() -> Graph {
322 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
323 }
324
325 fn k3_plus_k2() -> Graph {
326 Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (3, 4)], false, Some(5)).unwrap()
327 }
328
329 fn star5() -> Graph {
330 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
331 }
332
333 #[test]
336 fn icc_valid() {
337 let g = k3_plus_k2();
338 assert!(is_clique_cover(&g, &[vec![0, 1, 2], vec![3, 4]]).unwrap());
339 }
340
341 #[test]
342 fn icc_invalid_not_clique() {
343 let g = k3_plus_k2();
344 assert!(!is_clique_cover(&g, &[vec![0, 1, 3], vec![2, 4]]).unwrap());
345 }
346
347 #[test]
348 fn icc_missing_vertex() {
349 let g = k3_plus_k2();
350 assert!(!is_clique_cover(&g, &[vec![0, 1, 2]]).unwrap());
351 }
352
353 #[test]
354 fn icc_duplicate_vertex() {
355 let g = k3_plus_k2();
356 assert!(!is_clique_cover(&g, &[vec![0, 1, 2], vec![2, 3, 4]]).unwrap());
357 }
358
359 #[test]
360 fn icc_singletons() {
361 let g = path4();
362 assert!(is_clique_cover(&g, &[vec![0], vec![1], vec![2], vec![3]]).unwrap());
363 }
364
365 #[test]
366 fn icc_empty() {
367 let g = Graph::with_vertices(0);
368 assert!(is_clique_cover(&g, &[]).unwrap());
369 }
370
371 #[test]
372 fn icc_edges_as_cliques() {
373 let g = path4();
374 assert!(is_clique_cover(&g, &[vec![0, 1], vec![2, 3]]).unwrap());
375 }
376
377 #[test]
380 fn gcc_empty() {
381 let g = Graph::with_vertices(0);
382 let cover = greedy_clique_cover(&g).unwrap();
383 assert!(cover.is_empty());
384 }
385
386 #[test]
387 fn gcc_isolated() {
388 let g = Graph::with_vertices(3);
389 let cover = greedy_clique_cover(&g).unwrap();
390 assert!(is_clique_cover(&g, &cover).unwrap());
391 assert_eq!(cover.len(), 3);
392 }
393
394 #[test]
395 fn gcc_k3() {
396 let g = k3();
397 let cover = greedy_clique_cover(&g).unwrap();
398 assert!(is_clique_cover(&g, &cover).unwrap());
399 assert_eq!(cover.len(), 1);
400 }
401
402 #[test]
403 fn gcc_k4() {
404 let g = k4();
405 let cover = greedy_clique_cover(&g).unwrap();
406 assert!(is_clique_cover(&g, &cover).unwrap());
407 assert_eq!(cover.len(), 1);
408 }
409
410 #[test]
411 fn gcc_k3_plus_k2() {
412 let g = k3_plus_k2();
413 let cover = greedy_clique_cover(&g).unwrap();
414 assert!(is_clique_cover(&g, &cover).unwrap());
415 assert!(cover.len() <= 3);
416 }
417
418 #[test]
419 fn gcc_path4() {
420 let g = path4();
421 let cover = greedy_clique_cover(&g).unwrap();
422 assert!(is_clique_cover(&g, &cover).unwrap());
423 }
424
425 #[test]
426 fn gcc_cycle5() {
427 let g = cycle5();
428 let cover = greedy_clique_cover(&g).unwrap();
429 assert!(is_clique_cover(&g, &cover).unwrap());
430 }
431
432 #[test]
433 fn gcc_star5() {
434 let g = star5();
435 let cover = greedy_clique_cover(&g).unwrap();
436 assert!(is_clique_cover(&g, &cover).unwrap());
437 }
438
439 #[test]
442 fn ccn_empty() {
443 let g = Graph::with_vertices(0);
444 assert_eq!(clique_cover_number(&g).unwrap(), 0);
445 }
446
447 #[test]
448 fn ccn_isolated() {
449 let g = Graph::with_vertices(3);
450 assert_eq!(clique_cover_number(&g).unwrap(), 3);
451 }
452
453 #[test]
454 fn ccn_k3() {
455 assert_eq!(clique_cover_number(&k3()).unwrap(), 1);
456 }
457
458 #[test]
459 fn ccn_k4() {
460 assert_eq!(clique_cover_number(&k4()).unwrap(), 1);
461 }
462
463 #[test]
464 fn ccn_k3_plus_k2() {
465 assert_eq!(clique_cover_number(&k3_plus_k2()).unwrap(), 2);
466 }
467
468 #[test]
469 fn ccn_path4() {
470 assert_eq!(clique_cover_number(&path4()).unwrap(), 2);
471 }
472
473 #[test]
474 fn ccn_cycle4() {
475 assert_eq!(clique_cover_number(&cycle4()).unwrap(), 2);
476 }
477
478 #[test]
479 fn ccn_cycle5() {
480 assert_eq!(clique_cover_number(&cycle5()).unwrap(), 3);
481 }
482
483 #[test]
484 fn ccn_single_vertex() {
485 let g = Graph::with_vertices(1);
486 assert_eq!(clique_cover_number(&g).unwrap(), 1);
487 }
488
489 #[test]
490 fn ccn_single_edge() {
491 let g = Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap();
492 assert_eq!(clique_cover_number(&g).unwrap(), 1);
493 }
494
495 #[test]
498 fn greedy_at_least_optimal() {
499 for g in &[k3(), k4(), k3_plus_k2(), path4(), cycle4()] {
500 let greedy = greedy_clique_cover(g).unwrap();
501 let opt = clique_cover_number(g).unwrap();
502 assert!(greedy.len() as u32 >= opt);
503 }
504 }
505
506 #[test]
507 fn cover_number_at_most_n() {
508 for g in &[k3(), k4(), path4(), cycle5(), star5()] {
509 let theta = clique_cover_number(g).unwrap();
510 assert!(theta <= g.vcount());
511 }
512 }
513
514 #[test]
515 fn complete_graph_cover_is_one() {
516 for n in 1_u32..=5 {
517 let edges: Vec<(u32, u32)> = (0..n)
518 .flat_map(|i| ((i + 1)..n).map(move |j| (i, j)))
519 .collect();
520 let g = if edges.is_empty() {
521 Graph::with_vertices(n)
522 } else {
523 Graph::from_edges(&edges, false, Some(n)).unwrap()
524 };
525 assert_eq!(clique_cover_number(&g).unwrap(), 1);
526 }
527 }
528}