1use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
7
8pub fn clique_number(graph: &Graph) -> IgraphResult<u32> {
32 let n = graph.vcount();
33 if n == 0 {
34 return Ok(0);
35 }
36
37 let adj = build_neighbor_set(graph)?;
38 let mut max_size: u32 = 0;
39
40 let all_vertices: Vec<VertexId> = (0..n).collect();
41 bron_kerbosch_max(
42 &adj,
43 &mut Vec::new(),
44 &mut all_vertices.clone(),
45 &mut Vec::new(),
46 &mut max_size,
47 );
48
49 Ok(max_size)
50}
51
52pub fn maximal_cliques(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
74 let n = graph.vcount();
75 if n == 0 {
76 return Ok(Vec::new());
77 }
78
79 let adj = build_neighbor_set(graph)?;
80 let mut result: Vec<Vec<VertexId>> = Vec::new();
81
82 let all_vertices: Vec<VertexId> = (0..n).collect();
83 bron_kerbosch_all(
84 &adj,
85 &mut Vec::new(),
86 &mut all_vertices.clone(),
87 &mut Vec::new(),
88 &mut result,
89 );
90
91 for v in 0..n {
93 if adj[v as usize].is_empty() {
94 result.push(vec![v]);
95 }
96 }
97
98 Ok(result)
99}
100
101pub fn largest_cliques(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
125 let all = maximal_cliques(graph)?;
126 let max_size = all.iter().map(Vec::len).max().unwrap_or(0);
127 Ok(all.into_iter().filter(|c| c.len() == max_size).collect())
128}
129
130pub fn independence_number(graph: &Graph) -> IgraphResult<u32> {
151 let n = graph.vcount();
152 if n == 0 {
153 return Ok(0);
154 }
155
156 let adj = build_complement_neighbor_set(graph)?;
157 let mut max_size: u32 = 0;
158
159 let all_vertices: Vec<VertexId> = (0..n).collect();
160 bron_kerbosch_max(
161 &adj,
162 &mut Vec::new(),
163 &mut all_vertices.clone(),
164 &mut Vec::new(),
165 &mut max_size,
166 );
167
168 Ok(max_size)
169}
170
171pub fn maximal_independent_vertex_sets(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
197 let n = graph.vcount();
198 if n == 0 {
199 return Ok(Vec::new());
200 }
201
202 let adj = build_complement_neighbor_set(graph)?;
203 let mut result: Vec<Vec<VertexId>> = Vec::new();
204
205 let all_vertices: Vec<VertexId> = (0..n).collect();
206 bron_kerbosch_all_independent(
207 &adj,
208 &mut Vec::new(),
209 &mut all_vertices.clone(),
210 &mut Vec::new(),
211 &mut result,
212 );
213
214 Ok(result)
215}
216
217pub fn largest_independent_vertex_sets(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
242 let all = maximal_independent_vertex_sets(graph)?;
243 let max_size = all.iter().map(Vec::len).max().unwrap_or(0);
244 Ok(all.into_iter().filter(|s| s.len() == max_size).collect())
245}
246
247fn bron_kerbosch_all_independent(
251 adj: &[Vec<VertexId>],
252 r_clique: &mut Vec<VertexId>,
253 p_candidates: &mut Vec<VertexId>,
254 x_excluded: &mut Vec<VertexId>,
255 result: &mut Vec<Vec<VertexId>>,
256) {
257 if p_candidates.is_empty() && x_excluded.is_empty() {
258 let mut clique = r_clique.clone();
259 clique.sort_unstable();
260 result.push(clique);
261 return;
262 }
263
264 if p_candidates.is_empty() {
265 return;
266 }
267
268 let pivot = choose_pivot(adj, p_candidates, x_excluded);
269 let pivot_neighbors = &adj[pivot as usize];
270
271 let candidates: Vec<VertexId> = p_candidates
272 .iter()
273 .filter(|&&v| !pivot_neighbors.contains(&v))
274 .copied()
275 .collect();
276
277 for v in candidates {
278 let v_neighbors = &adj[v as usize];
279
280 r_clique.push(v);
281
282 let mut new_p: Vec<VertexId> = p_candidates
283 .iter()
284 .filter(|&&u| v_neighbors.contains(&u))
285 .copied()
286 .collect();
287 let mut new_x: Vec<VertexId> = x_excluded
288 .iter()
289 .filter(|&&u| v_neighbors.contains(&u))
290 .copied()
291 .collect();
292
293 bron_kerbosch_all_independent(adj, r_clique, &mut new_p, &mut new_x, result);
294
295 r_clique.pop();
296
297 p_candidates.retain(|&u| u != v);
298 x_excluded.push(v);
299 }
300}
301
302fn build_complement_neighbor_set(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
304 let n = graph.vcount();
305 let adj = build_neighbor_set(graph)?;
306
307 let mut complement: Vec<Vec<VertexId>> = Vec::with_capacity(n as usize);
308 for v in 0..n {
309 let neighbors = &adj[v as usize];
310 let non_neighbors: Vec<VertexId> = (0..n)
311 .filter(|&u| u != v && !neighbors.contains(&u))
312 .collect();
313 complement.push(non_neighbors);
314 }
315
316 Ok(complement)
317}
318
319fn bron_kerbosch_max(
321 adj: &[Vec<VertexId>],
322 r_clique: &mut Vec<VertexId>,
323 p_candidates: &mut Vec<VertexId>,
324 x_excluded: &mut Vec<VertexId>,
325 max_size: &mut u32,
326) {
327 if p_candidates.is_empty() && x_excluded.is_empty() {
328 #[allow(clippy::cast_possible_truncation)]
329 let size = r_clique.len() as u32;
330 if size > *max_size {
331 *max_size = size;
332 }
333 return;
334 }
335
336 if p_candidates.is_empty() {
337 return;
338 }
339
340 let pivot = choose_pivot(adj, p_candidates, x_excluded);
342 let pivot_neighbors = &adj[pivot as usize];
343
344 let candidates: Vec<VertexId> = p_candidates
346 .iter()
347 .filter(|&&v| !pivot_neighbors.contains(&v))
348 .copied()
349 .collect();
350
351 for v in candidates {
352 let v_neighbors = &adj[v as usize];
353
354 r_clique.push(v);
355
356 let mut new_p: Vec<VertexId> = p_candidates
357 .iter()
358 .filter(|&&u| v_neighbors.contains(&u))
359 .copied()
360 .collect();
361 let mut new_x: Vec<VertexId> = x_excluded
362 .iter()
363 .filter(|&&u| v_neighbors.contains(&u))
364 .copied()
365 .collect();
366
367 bron_kerbosch_max(adj, r_clique, &mut new_p, &mut new_x, max_size);
368
369 r_clique.pop();
370
371 p_candidates.retain(|&u| u != v);
372 x_excluded.push(v);
373 }
374}
375
376fn bron_kerbosch_all(
378 adj: &[Vec<VertexId>],
379 r_clique: &mut Vec<VertexId>,
380 p_candidates: &mut Vec<VertexId>,
381 x_excluded: &mut Vec<VertexId>,
382 result: &mut Vec<Vec<VertexId>>,
383) {
384 if p_candidates.is_empty() && x_excluded.is_empty() {
385 if r_clique.len() >= 2 {
386 let mut clique = r_clique.clone();
387 clique.sort_unstable();
388 result.push(clique);
389 }
390 return;
391 }
392
393 if p_candidates.is_empty() {
394 return;
395 }
396
397 let pivot = choose_pivot(adj, p_candidates, x_excluded);
398 let pivot_neighbors = &adj[pivot as usize];
399
400 let candidates: Vec<VertexId> = p_candidates
401 .iter()
402 .filter(|&&v| !pivot_neighbors.contains(&v))
403 .copied()
404 .collect();
405
406 for v in candidates {
407 let v_neighbors = &adj[v as usize];
408
409 r_clique.push(v);
410
411 let mut new_p: Vec<VertexId> = p_candidates
412 .iter()
413 .filter(|&&u| v_neighbors.contains(&u))
414 .copied()
415 .collect();
416 let mut new_x: Vec<VertexId> = x_excluded
417 .iter()
418 .filter(|&&u| v_neighbors.contains(&u))
419 .copied()
420 .collect();
421
422 bron_kerbosch_all(adj, r_clique, &mut new_p, &mut new_x, result);
423
424 r_clique.pop();
425
426 p_candidates.retain(|&u| u != v);
427 x_excluded.push(v);
428 }
429}
430
431pub fn maximal_cliques_count(graph: &Graph) -> IgraphResult<u64> {
451 let n = graph.vcount();
452 if n == 0 {
453 return Ok(0);
454 }
455
456 let adj = build_neighbor_set(graph)?;
457 let mut count: u64 = 0;
458
459 let all_vertices: Vec<VertexId> = (0..n).collect();
460 bron_kerbosch_count(
461 &adj,
462 &mut Vec::new(),
463 &mut all_vertices.clone(),
464 &mut Vec::new(),
465 &mut count,
466 );
467
468 for v in 0..n {
470 if adj[v as usize].is_empty() {
471 count = count.saturating_add(1);
472 }
473 }
474
475 Ok(count)
476}
477
478pub fn clique_size_hist(graph: &Graph) -> IgraphResult<Vec<u64>> {
509 let n = graph.vcount();
510 if n == 0 {
511 return Ok(Vec::new());
512 }
513
514 let adj = build_neighbor_set(graph)?;
515 let mut hist: Vec<u64> = Vec::new();
516
517 let mut current: Vec<VertexId> = Vec::new();
518 count_cliques_by_size(&adj, &mut current, 0, n, &mut hist);
519
520 Ok(hist)
521}
522
523pub fn maximal_cliques_hist(graph: &Graph) -> IgraphResult<Vec<u64>> {
552 let n = graph.vcount();
553 if n == 0 {
554 return Ok(Vec::new());
555 }
556
557 let adj = build_neighbor_set(graph)?;
558 let mut hist: Vec<u64> = Vec::new();
559
560 let all_vertices: Vec<VertexId> = (0..n).collect();
561 bron_kerbosch_hist(
562 &adj,
563 &mut Vec::new(),
564 &mut all_vertices.clone(),
565 &mut Vec::new(),
566 &mut hist,
567 );
568
569 for v in 0..n {
571 if adj[v as usize].is_empty() {
572 if hist.len() < 2 {
573 hist.resize(2, 0);
574 }
575 hist[1] = hist[1].saturating_add(1);
576 }
577 }
578
579 Ok(hist)
580}
581
582pub fn maximal_cliques_subset(
627 graph: &Graph,
628 subset: &[VertexId],
629 min_size: u32,
630 max_size: u32,
631 max_results: Option<usize>,
632) -> IgraphResult<Vec<Vec<VertexId>>> {
633 let n = graph.vcount();
634
635 for &v in subset {
636 if v >= n {
637 return Err(IgraphError::InvalidArgument(format!(
638 "vertex id {v} is out of range [0, {n})"
639 )));
640 }
641 }
642
643 if n == 0 || subset.is_empty() {
644 return Ok(Vec::new());
645 }
646
647 let adj = build_neighbor_set(graph)?;
648 let mut all_cliques: Vec<Vec<VertexId>> = Vec::new();
649
650 let all_vertices: Vec<VertexId> = (0..n).collect();
651 bron_kerbosch_all(
652 &adj,
653 &mut Vec::new(),
654 &mut all_vertices.clone(),
655 &mut Vec::new(),
656 &mut all_cliques,
657 );
658
659 for v in 0..n {
661 if adj[v as usize].is_empty() {
662 all_cliques.push(vec![v]);
663 }
664 }
665
666 let mut in_subset = vec![false; n as usize];
668 for &v in subset {
669 in_subset[v as usize] = true;
670 }
671
672 let effective_min = if min_size == 0 { 1 } else { min_size };
673 let effective_max = if max_size == 0 { u32::MAX } else { max_size };
674 let limit = max_results.unwrap_or(usize::MAX);
675
676 let mut result: Vec<Vec<VertexId>> = Vec::new();
677 for clique in all_cliques {
678 #[allow(clippy::cast_possible_truncation)]
679 let csize = clique.len() as u32;
680 if csize < effective_min || csize > effective_max {
681 continue;
682 }
683 if !clique.iter().any(|&v| in_subset[v as usize]) {
684 continue;
685 }
686 result.push(clique);
687 if result.len() >= limit {
688 break;
689 }
690 }
691
692 Ok(result)
693}
694
695fn count_cliques_by_size(
697 adj: &[Vec<VertexId>],
698 current: &mut Vec<VertexId>,
699 start: u32,
700 n: u32,
701 hist: &mut Vec<u64>,
702) {
703 let len = current.len();
704 if len >= 1 {
705 if hist.len() <= len {
706 hist.resize(len + 1, 0);
707 }
708 hist[len] = hist[len].saturating_add(1);
709 }
710
711 for v in start..n {
712 let v_adj = &adj[v as usize];
713 if current.iter().all(|&u| v_adj.contains(&u)) {
714 current.push(v);
715 count_cliques_by_size(adj, current, v + 1, n, hist);
716 current.pop();
717 }
718 }
719}
720
721fn bron_kerbosch_count(
723 adj: &[Vec<VertexId>],
724 r_clique: &mut Vec<VertexId>,
725 p_candidates: &mut Vec<VertexId>,
726 x_excluded: &mut Vec<VertexId>,
727 count: &mut u64,
728) {
729 if p_candidates.is_empty() && x_excluded.is_empty() {
730 if r_clique.len() >= 2 {
731 *count = count.saturating_add(1);
732 }
733 return;
734 }
735
736 if p_candidates.is_empty() {
737 return;
738 }
739
740 let pivot = choose_pivot(adj, p_candidates, x_excluded);
741 let pivot_neighbors = &adj[pivot as usize];
742
743 let candidates: Vec<VertexId> = p_candidates
744 .iter()
745 .filter(|&&v| !pivot_neighbors.contains(&v))
746 .copied()
747 .collect();
748
749 for v in candidates {
750 let v_neighbors = &adj[v as usize];
751
752 r_clique.push(v);
753
754 let mut new_p: Vec<VertexId> = p_candidates
755 .iter()
756 .filter(|&&u| v_neighbors.contains(&u))
757 .copied()
758 .collect();
759 let mut new_x: Vec<VertexId> = x_excluded
760 .iter()
761 .filter(|&&u| v_neighbors.contains(&u))
762 .copied()
763 .collect();
764
765 bron_kerbosch_count(adj, r_clique, &mut new_p, &mut new_x, count);
766
767 r_clique.pop();
768
769 p_candidates.retain(|&u| u != v);
770 x_excluded.push(v);
771 }
772}
773
774fn bron_kerbosch_hist(
776 adj: &[Vec<VertexId>],
777 r_clique: &mut Vec<VertexId>,
778 p_candidates: &mut Vec<VertexId>,
779 x_excluded: &mut Vec<VertexId>,
780 hist: &mut Vec<u64>,
781) {
782 if p_candidates.is_empty() && x_excluded.is_empty() {
783 let size = r_clique.len();
784 if size >= 2 {
785 if hist.len() <= size {
786 hist.resize(size + 1, 0);
787 }
788 hist[size] = hist[size].saturating_add(1);
789 }
790 return;
791 }
792
793 if p_candidates.is_empty() {
794 return;
795 }
796
797 let pivot = choose_pivot(adj, p_candidates, x_excluded);
798 let pivot_neighbors = &adj[pivot as usize];
799
800 let candidates: Vec<VertexId> = p_candidates
801 .iter()
802 .filter(|&&v| !pivot_neighbors.contains(&v))
803 .copied()
804 .collect();
805
806 for v in candidates {
807 let v_neighbors = &adj[v as usize];
808
809 r_clique.push(v);
810
811 let mut new_p: Vec<VertexId> = p_candidates
812 .iter()
813 .filter(|&&u| v_neighbors.contains(&u))
814 .copied()
815 .collect();
816 let mut new_x: Vec<VertexId> = x_excluded
817 .iter()
818 .filter(|&&u| v_neighbors.contains(&u))
819 .copied()
820 .collect();
821
822 bron_kerbosch_hist(adj, r_clique, &mut new_p, &mut new_x, hist);
823
824 r_clique.pop();
825
826 p_candidates.retain(|&u| u != v);
827 x_excluded.push(v);
828 }
829}
830
831fn choose_pivot(
833 adj: &[Vec<VertexId>],
834 p_candidates: &[VertexId],
835 x_excluded: &[VertexId],
836) -> VertexId {
837 let mut best = p_candidates[0];
838 let mut best_count = 0usize;
839
840 for &v in p_candidates.iter().chain(x_excluded.iter()) {
841 let count = p_candidates
842 .iter()
843 .filter(|&&u| adj[v as usize].contains(&u))
844 .count();
845 if count > best_count {
846 best_count = count;
847 best = v;
848 }
849 }
850
851 best
852}
853
854pub fn cliques(
877 graph: &Graph,
878 min_size: u32,
879 max_size: u32,
880 max_results: Option<usize>,
881) -> IgraphResult<Vec<Vec<VertexId>>> {
882 let n = graph.vcount();
883 if n == 0 || min_size > n || max_size == 0 {
884 return Ok(Vec::new());
885 }
886
887 let min_sz = if min_size == 0 { 1 } else { min_size } as usize;
888 let max_sz = max_size as usize;
889 let limit = max_results.unwrap_or(usize::MAX);
890
891 let adj = build_neighbor_set(graph)?;
892 let mut result: Vec<Vec<VertexId>> = Vec::new();
893
894 let mut current: Vec<VertexId> = Vec::new();
898 enumerate_cliques(&adj, &mut current, 0, n, min_sz, max_sz, limit, &mut result);
899
900 Ok(result)
901}
902
903#[allow(clippy::cast_precision_loss)]
939pub fn weighted_clique_number(graph: &Graph, vertex_weights: &[f64]) -> IgraphResult<f64> {
940 let n = graph.vcount();
941 let weights = validate_clique_weights(vertex_weights, n)?;
942 if n == 0 {
943 return Ok(0.0);
944 }
945
946 let mut best: i64 = 0;
947 for clique in maximal_cliques(graph)? {
948 let w = clique_weight(&weights, &clique)?;
949 if w > best {
950 best = w;
951 }
952 }
953 Ok(best as f64)
954}
955
956pub fn largest_weighted_cliques(
987 graph: &Graph,
988 vertex_weights: &[f64],
989) -> IgraphResult<Vec<Vec<VertexId>>> {
990 let n = graph.vcount();
991 let weights = validate_clique_weights(vertex_weights, n)?;
992 if n == 0 {
993 return Ok(Vec::new());
994 }
995
996 let mut best: i64 = 0;
997 let mut weighted: Vec<(i64, Vec<VertexId>)> = Vec::new();
998 for mut clique in maximal_cliques(graph)? {
999 clique.sort_unstable();
1000 let w = clique_weight(&weights, &clique)?;
1001 if w > best {
1002 best = w;
1003 }
1004 weighted.push((w, clique));
1005 }
1006
1007 let mut result: Vec<Vec<VertexId>> = weighted
1008 .into_iter()
1009 .filter(|(w, _)| *w == best)
1010 .map(|(_, clique)| clique)
1011 .collect();
1012 result.sort_unstable();
1013 Ok(result)
1014}
1015
1016#[allow(clippy::cast_precision_loss)]
1052pub fn weighted_cliques(
1053 graph: &Graph,
1054 vertex_weights: &[f64],
1055 maximal: bool,
1056 min_weight: f64,
1057 max_weight: f64,
1058 max_results: Option<usize>,
1059) -> IgraphResult<Vec<Vec<VertexId>>> {
1060 let n = graph.vcount();
1061 let weights = validate_clique_weights(vertex_weights, n)?;
1062 if n == 0 {
1063 return Ok(Vec::new());
1064 }
1065
1066 let mut candidates = if maximal {
1067 let mut cs = maximal_cliques(graph)?;
1068 for clique in &mut cs {
1069 clique.sort_unstable();
1070 }
1071 cs
1072 } else {
1073 cliques(graph, 1, n, None)?
1074 };
1075 candidates.sort_unstable();
1076
1077 let has_min = min_weight > 0.0;
1078 let has_max = max_weight > 0.0;
1079 let limit = max_results.unwrap_or(usize::MAX);
1080
1081 let mut result: Vec<Vec<VertexId>> = Vec::new();
1082 for clique in candidates {
1083 let w = clique_weight(&weights, &clique)? as f64;
1084 if (has_min && w < min_weight) || (has_max && w > max_weight) {
1085 continue;
1086 }
1087 result.push(clique);
1088 if result.len() >= limit {
1089 break;
1090 }
1091 }
1092 Ok(result)
1093}
1094
1095#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
1099fn validate_clique_weights(vertex_weights: &[f64], n: u32) -> IgraphResult<Vec<i64>> {
1100 if vertex_weights.len() != n as usize {
1101 return Err(IgraphError::InvalidArgument(format!(
1102 "vertex_weights length {} does not match vertex count {n}",
1103 vertex_weights.len()
1104 )));
1105 }
1106 let mut weights = Vec::with_capacity(vertex_weights.len());
1107 for (v, &w) in vertex_weights.iter().enumerate() {
1108 let truncated = w.trunc();
1109 if !truncated.is_finite() || truncated < 1.0 {
1110 return Err(IgraphError::InvalidArgument(format!(
1111 "weighted cliques require positive integer vertex weights; vertex {v} has weight {w}"
1112 )));
1113 }
1114 if truncated > i64::MAX as f64 {
1115 return Err(IgraphError::InvalidArgument(format!(
1116 "vertex weight for vertex {v} is too large"
1117 )));
1118 }
1119 weights.push(truncated as i64);
1120 }
1121 Ok(weights)
1122}
1123
1124fn clique_weight(weights: &[i64], clique: &[VertexId]) -> IgraphResult<i64> {
1126 let mut total: i64 = 0;
1127 for &v in clique {
1128 total = total
1129 .checked_add(weights[v as usize])
1130 .ok_or(IgraphError::Internal("clique weight sum overflows i64"))?;
1131 }
1132 Ok(total)
1133}
1134
1135pub fn independent_vertex_sets(
1157 graph: &Graph,
1158 min_size: u32,
1159 max_size: u32,
1160 max_results: Option<usize>,
1161) -> IgraphResult<Vec<Vec<VertexId>>> {
1162 let n = graph.vcount();
1163 if n == 0 || min_size > n || max_size == 0 {
1164 return Ok(Vec::new());
1165 }
1166
1167 let min_sz = if min_size == 0 { 1 } else { min_size } as usize;
1168 let max_sz = max_size as usize;
1169 let limit = max_results.unwrap_or(usize::MAX);
1170
1171 let adj = build_complement_neighbor_set(graph)?;
1172 let mut result: Vec<Vec<VertexId>> = Vec::new();
1173
1174 let mut current: Vec<VertexId> = Vec::new();
1175 enumerate_cliques(&adj, &mut current, 0, n, min_sz, max_sz, limit, &mut result);
1176
1177 Ok(result)
1178}
1179
1180#[allow(clippy::too_many_arguments)]
1181fn enumerate_cliques(
1182 adj: &[Vec<VertexId>],
1183 current: &mut Vec<VertexId>,
1184 start: u32,
1185 n: u32,
1186 min_sz: usize,
1187 max_sz: usize,
1188 limit: usize,
1189 result: &mut Vec<Vec<VertexId>>,
1190) {
1191 let cur_len = current.len();
1192
1193 if cur_len >= min_sz {
1194 let mut clique = current.clone();
1195 clique.sort_unstable();
1196 result.push(clique);
1197 if result.len() >= limit {
1198 return;
1199 }
1200 }
1201
1202 if cur_len >= max_sz {
1203 return;
1204 }
1205
1206 for v in start..n {
1207 let v_adj = &adj[v as usize];
1209 let all_connected = current.iter().all(|&u| v_adj.contains(&u));
1210 if !all_connected {
1211 continue;
1212 }
1213
1214 current.push(v);
1215 enumerate_cliques(adj, current, v + 1, n, min_sz, max_sz, limit, result);
1216 current.pop();
1217
1218 if result.len() >= limit {
1219 return;
1220 }
1221 }
1222}
1223
1224fn build_neighbor_set(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
1226 let n = graph.vcount() as usize;
1227 let ecount = graph.ecount();
1228 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
1229
1230 for eid in 0..ecount {
1231 #[allow(clippy::cast_possible_truncation)]
1232 let (src, tgt) = graph.edge(eid as u32)?;
1233 if src != tgt {
1234 adj[src as usize].push(tgt);
1235 adj[tgt as usize].push(src);
1236 }
1237 }
1238
1239 for neighbors in &mut adj {
1241 neighbors.sort_unstable();
1242 neighbors.dedup();
1243 }
1244
1245 Ok(adj)
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250 use super::*;
1251
1252 #[test]
1253 fn test_clique_number_empty() {
1254 let g = Graph::with_vertices(0);
1255 assert_eq!(clique_number(&g).unwrap(), 0);
1256 }
1257
1258 #[test]
1259 fn test_clique_number_isolated() {
1260 let g = Graph::with_vertices(5);
1261 assert_eq!(clique_number(&g).unwrap(), 1);
1262 }
1263
1264 #[test]
1265 fn test_clique_number_single_edge() {
1266 let mut g = Graph::with_vertices(3);
1267 g.add_edge(0, 1).unwrap();
1268 assert_eq!(clique_number(&g).unwrap(), 2);
1269 }
1270
1271 #[test]
1272 fn test_clique_number_triangle() {
1273 let mut g = Graph::with_vertices(4);
1274 g.add_edge(0, 1).unwrap();
1275 g.add_edge(1, 2).unwrap();
1276 g.add_edge(0, 2).unwrap();
1277 g.add_edge(2, 3).unwrap();
1278 assert_eq!(clique_number(&g).unwrap(), 3);
1279 }
1280
1281 #[test]
1282 fn test_clique_number_k5() {
1283 let mut g = Graph::with_vertices(5);
1284 for i in 0..5u32 {
1285 for j in (i + 1)..5 {
1286 g.add_edge(i, j).unwrap();
1287 }
1288 }
1289 assert_eq!(clique_number(&g).unwrap(), 5);
1290 }
1291
1292 #[test]
1293 fn test_clique_number_directed_ignores_direction() {
1294 let mut g = Graph::new(3, true).unwrap();
1295 g.add_edge(0, 1).unwrap();
1296 g.add_edge(1, 2).unwrap();
1297 g.add_edge(2, 0).unwrap();
1298 assert_eq!(clique_number(&g).unwrap(), 3);
1300 }
1301
1302 #[test]
1303 fn test_maximal_cliques_path() {
1304 let mut g = Graph::with_vertices(4);
1305 g.add_edge(0, 1).unwrap();
1306 g.add_edge(1, 2).unwrap();
1307 g.add_edge(2, 3).unwrap();
1308
1309 let cliques = maximal_cliques(&g).unwrap();
1310 assert_eq!(cliques.len(), 3);
1311 for c in &cliques {
1312 assert_eq!(c.len(), 2);
1313 }
1314 }
1315
1316 #[test]
1317 fn test_maximal_cliques_triangle_plus_edge() {
1318 let mut g = Graph::with_vertices(4);
1319 g.add_edge(0, 1).unwrap();
1320 g.add_edge(1, 2).unwrap();
1321 g.add_edge(0, 2).unwrap();
1322 g.add_edge(2, 3).unwrap();
1323
1324 let cliques = maximal_cliques(&g).unwrap();
1325 assert_eq!(cliques.len(), 2);
1327 let sizes: Vec<usize> = cliques.iter().map(Vec::len).collect();
1328 assert!(sizes.contains(&3));
1329 assert!(sizes.contains(&2));
1330 }
1331
1332 #[test]
1333 fn test_maximal_cliques_isolated_vertices() {
1334 let mut g = Graph::with_vertices(5);
1335 g.add_edge(0, 1).unwrap();
1336 let cliques = maximal_cliques(&g).unwrap();
1339 assert_eq!(cliques.len(), 4);
1341 }
1342
1343 #[test]
1344 fn test_largest_cliques() {
1345 let mut g = Graph::with_vertices(6);
1346 g.add_edge(0, 1).unwrap();
1348 g.add_edge(1, 2).unwrap();
1349 g.add_edge(0, 2).unwrap();
1350 g.add_edge(2, 3).unwrap();
1351 g.add_edge(3, 4).unwrap();
1352 g.add_edge(2, 4).unwrap();
1353
1354 let cliques = largest_cliques(&g).unwrap();
1355 assert_eq!(cliques.len(), 2);
1357 for c in &cliques {
1358 assert_eq!(c.len(), 3);
1359 }
1360 }
1361
1362 #[test]
1363 fn test_largest_cliques_k4() {
1364 let mut g = Graph::with_vertices(4);
1365 for i in 0..4u32 {
1366 for j in (i + 1)..4 {
1367 g.add_edge(i, j).unwrap();
1368 }
1369 }
1370
1371 let cliques = largest_cliques(&g).unwrap();
1372 assert_eq!(cliques.len(), 1);
1373 assert_eq!(cliques[0].len(), 4);
1374 }
1375
1376 #[test]
1377 fn test_clique_number_petersen() {
1378 let mut g = Graph::with_vertices(10);
1380 g.add_edge(0, 1).unwrap();
1382 g.add_edge(1, 2).unwrap();
1383 g.add_edge(2, 3).unwrap();
1384 g.add_edge(3, 4).unwrap();
1385 g.add_edge(4, 0).unwrap();
1386 g.add_edge(5, 7).unwrap();
1388 g.add_edge(7, 9).unwrap();
1389 g.add_edge(9, 6).unwrap();
1390 g.add_edge(6, 8).unwrap();
1391 g.add_edge(8, 5).unwrap();
1392 g.add_edge(0, 5).unwrap();
1394 g.add_edge(1, 6).unwrap();
1395 g.add_edge(2, 7).unwrap();
1396 g.add_edge(3, 8).unwrap();
1397 g.add_edge(4, 9).unwrap();
1398
1399 assert_eq!(clique_number(&g).unwrap(), 2);
1400 }
1401
1402 #[test]
1405 fn test_independence_number_empty() {
1406 let g = Graph::with_vertices(0);
1407 assert_eq!(independence_number(&g).unwrap(), 0);
1408 }
1409
1410 #[test]
1411 fn test_independence_number_isolated() {
1412 let g = Graph::with_vertices(5);
1413 assert_eq!(independence_number(&g).unwrap(), 5);
1414 }
1415
1416 #[test]
1417 fn test_independence_number_complete() {
1418 let mut g = Graph::with_vertices(5);
1419 for i in 0..5u32 {
1420 for j in (i + 1)..5 {
1421 g.add_edge(i, j).unwrap();
1422 }
1423 }
1424 assert_eq!(independence_number(&g).unwrap(), 1);
1425 }
1426
1427 #[test]
1428 fn test_independence_number_path() {
1429 let mut g = Graph::with_vertices(4);
1430 g.add_edge(0, 1).unwrap();
1431 g.add_edge(1, 2).unwrap();
1432 g.add_edge(2, 3).unwrap();
1433 assert_eq!(independence_number(&g).unwrap(), 2);
1435 }
1436
1437 #[test]
1438 fn test_independence_number_cycle_5() {
1439 let mut g = Graph::with_vertices(5);
1440 g.add_edge(0, 1).unwrap();
1441 g.add_edge(1, 2).unwrap();
1442 g.add_edge(2, 3).unwrap();
1443 g.add_edge(3, 4).unwrap();
1444 g.add_edge(4, 0).unwrap();
1445 assert_eq!(independence_number(&g).unwrap(), 2);
1447 }
1448
1449 #[test]
1450 fn test_independence_number_bipartite() {
1451 let mut g = Graph::with_vertices(5);
1453 for &a in &[0u32, 1] {
1454 for &b in &[2u32, 3, 4] {
1455 g.add_edge(a, b).unwrap();
1456 }
1457 }
1458 assert_eq!(independence_number(&g).unwrap(), 3);
1459 }
1460
1461 #[test]
1462 fn test_maximal_independent_sets_triangle() {
1463 let mut g = Graph::with_vertices(3);
1464 g.add_edge(0, 1).unwrap();
1465 g.add_edge(1, 2).unwrap();
1466 g.add_edge(0, 2).unwrap();
1467
1468 let sets = maximal_independent_vertex_sets(&g).unwrap();
1469 assert_eq!(sets.len(), 3);
1471 for s in &sets {
1472 assert_eq!(s.len(), 1);
1473 }
1474 }
1475
1476 #[test]
1477 fn test_maximal_independent_sets_path_4() {
1478 let mut g = Graph::with_vertices(4);
1479 g.add_edge(0, 1).unwrap();
1480 g.add_edge(1, 2).unwrap();
1481 g.add_edge(2, 3).unwrap();
1482
1483 let sets = maximal_independent_vertex_sets(&g).unwrap();
1484 assert_eq!(sets.len(), 3);
1486 for s in &sets {
1487 assert_eq!(s.len(), 2);
1488 }
1489 }
1490
1491 #[test]
1492 fn test_largest_independent_vertex_sets_star() {
1493 let mut g = Graph::with_vertices(5);
1495 g.add_edge(0, 1).unwrap();
1496 g.add_edge(0, 2).unwrap();
1497 g.add_edge(0, 3).unwrap();
1498 g.add_edge(0, 4).unwrap();
1499
1500 let sets = largest_independent_vertex_sets(&g).unwrap();
1501 assert_eq!(sets.len(), 1);
1503 assert_eq!(sets[0].len(), 4);
1504 }
1505
1506 #[test]
1507 fn test_independence_number_petersen() {
1508 let mut g = Graph::with_vertices(10);
1510 g.add_edge(0, 1).unwrap();
1511 g.add_edge(1, 2).unwrap();
1512 g.add_edge(2, 3).unwrap();
1513 g.add_edge(3, 4).unwrap();
1514 g.add_edge(4, 0).unwrap();
1515 g.add_edge(5, 7).unwrap();
1516 g.add_edge(7, 9).unwrap();
1517 g.add_edge(9, 6).unwrap();
1518 g.add_edge(6, 8).unwrap();
1519 g.add_edge(8, 5).unwrap();
1520 g.add_edge(0, 5).unwrap();
1521 g.add_edge(1, 6).unwrap();
1522 g.add_edge(2, 7).unwrap();
1523 g.add_edge(3, 8).unwrap();
1524 g.add_edge(4, 9).unwrap();
1525
1526 assert_eq!(independence_number(&g).unwrap(), 4);
1527 }
1528
1529 #[test]
1530 fn test_independent_sets_directed_ignores_direction() {
1531 let mut g = Graph::new(3, true).unwrap();
1533 g.add_edge(0, 1).unwrap();
1534 g.add_edge(1, 2).unwrap();
1535 g.add_edge(2, 0).unwrap();
1536 assert_eq!(independence_number(&g).unwrap(), 1);
1537 }
1538
1539 #[test]
1542 fn test_count_empty() {
1543 let g = Graph::with_vertices(0);
1544 assert_eq!(maximal_cliques_count(&g).unwrap(), 0);
1545 }
1546
1547 #[test]
1548 fn test_count_isolated() {
1549 let g = Graph::with_vertices(3);
1550 assert_eq!(maximal_cliques_count(&g).unwrap(), 3);
1552 }
1553
1554 #[test]
1555 fn test_count_path() {
1556 let mut g = Graph::with_vertices(4);
1557 g.add_edge(0, 1).unwrap();
1558 g.add_edge(1, 2).unwrap();
1559 g.add_edge(2, 3).unwrap();
1560 assert_eq!(maximal_cliques_count(&g).unwrap(), 3);
1562 }
1563
1564 #[test]
1565 fn test_count_triangle_plus_edge() {
1566 let mut g = Graph::with_vertices(4);
1567 g.add_edge(0, 1).unwrap();
1568 g.add_edge(0, 2).unwrap();
1569 g.add_edge(1, 2).unwrap();
1570 g.add_edge(2, 3).unwrap();
1571 assert_eq!(maximal_cliques_count(&g).unwrap(), 2);
1573 }
1574
1575 #[test]
1576 fn test_count_matches_enum() {
1577 let mut g = Graph::with_vertices(5);
1578 g.add_edge(0, 1).unwrap();
1579 g.add_edge(0, 2).unwrap();
1580 g.add_edge(1, 2).unwrap();
1581 g.add_edge(2, 3).unwrap();
1582 g.add_edge(3, 4).unwrap();
1583 let enumerated = maximal_cliques(&g).unwrap();
1584 assert_eq!(maximal_cliques_count(&g).unwrap(), enumerated.len() as u64);
1585 }
1586
1587 #[test]
1590 fn test_hist_empty() {
1591 let g = Graph::with_vertices(0);
1592 assert!(clique_size_hist(&g).unwrap().is_empty());
1593 }
1594
1595 #[test]
1596 fn test_hist_isolated() {
1597 let g = Graph::with_vertices(3);
1598 let hist = clique_size_hist(&g).unwrap();
1599 assert_eq!(hist, vec![0, 3]);
1601 }
1602
1603 #[test]
1604 fn test_hist_triangle_plus_edge() {
1605 let mut g = Graph::with_vertices(4);
1606 g.add_edge(0, 1).unwrap();
1607 g.add_edge(0, 2).unwrap();
1608 g.add_edge(1, 2).unwrap();
1609 g.add_edge(2, 3).unwrap();
1610 let hist = clique_size_hist(&g).unwrap();
1611 assert_eq!(hist, vec![0, 4, 4, 1]);
1613 }
1614
1615 #[test]
1616 fn test_hist_complete_4() {
1617 let mut g = Graph::with_vertices(4);
1618 for i in 0..4u32 {
1619 for j in (i + 1)..4 {
1620 g.add_edge(i, j).unwrap();
1621 }
1622 }
1623 let hist = clique_size_hist(&g).unwrap();
1624 assert_eq!(hist, vec![0, 4, 6, 4, 1]);
1626 }
1627
1628 #[test]
1629 fn test_hist_path() {
1630 let mut g = Graph::with_vertices(4);
1631 g.add_edge(0, 1).unwrap();
1632 g.add_edge(1, 2).unwrap();
1633 g.add_edge(2, 3).unwrap();
1634 let hist = clique_size_hist(&g).unwrap();
1635 assert_eq!(hist, vec![0, 4, 3]);
1637 }
1638
1639 #[test]
1640 fn test_hist_matches_cliques_enumeration() {
1641 let mut g = Graph::with_vertices(6);
1642 for &(a, b) in &[(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5)] {
1643 g.add_edge(a, b).unwrap();
1644 }
1645 let hist = clique_size_hist(&g).unwrap();
1646 let all = cliques(&g, 1, g.vcount(), None).unwrap();
1648 let mut expected = vec![0u64; hist.len()];
1649 for c in &all {
1650 expected[c.len()] += 1;
1651 }
1652 assert_eq!(hist, expected);
1653 }
1654
1655 #[test]
1658 fn test_max_hist_empty() {
1659 let g = Graph::with_vertices(0);
1660 assert!(maximal_cliques_hist(&g).unwrap().is_empty());
1661 }
1662
1663 #[test]
1664 fn test_max_hist_isolated() {
1665 let g = Graph::with_vertices(3);
1666 let hist = maximal_cliques_hist(&g).unwrap();
1667 assert_eq!(hist, vec![0, 3]);
1669 }
1670
1671 #[test]
1672 fn test_max_hist_triangle_plus_edge() {
1673 let mut g = Graph::with_vertices(4);
1674 g.add_edge(0, 1).unwrap();
1675 g.add_edge(0, 2).unwrap();
1676 g.add_edge(1, 2).unwrap();
1677 g.add_edge(2, 3).unwrap();
1678 let hist = maximal_cliques_hist(&g).unwrap();
1679 assert_eq!(hist, vec![0, 0, 1, 1]);
1681 }
1682
1683 #[test]
1684 fn test_max_hist_complete_4() {
1685 let mut g = Graph::with_vertices(4);
1686 for i in 0..4u32 {
1687 for j in (i + 1)..4 {
1688 g.add_edge(i, j).unwrap();
1689 }
1690 }
1691 let hist = maximal_cliques_hist(&g).unwrap();
1692 assert_eq!(hist, vec![0, 0, 0, 0, 1]);
1694 }
1695
1696 #[test]
1697 fn test_max_hist_path() {
1698 let mut g = Graph::with_vertices(4);
1699 g.add_edge(0, 1).unwrap();
1700 g.add_edge(1, 2).unwrap();
1701 g.add_edge(2, 3).unwrap();
1702 let hist = maximal_cliques_hist(&g).unwrap();
1703 assert_eq!(hist, vec![0, 0, 3]);
1705 }
1706
1707 #[test]
1708 fn test_max_hist_total_matches_count() {
1709 let mut g = Graph::with_vertices(6);
1710 for &(a, b) in &[(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5)] {
1711 g.add_edge(a, b).unwrap();
1712 }
1713 let hist = maximal_cliques_hist(&g).unwrap();
1714 let total: u64 = hist.iter().sum();
1715 assert_eq!(total, maximal_cliques_count(&g).unwrap());
1716 }
1717
1718 fn triangle_plus_pendant() -> Graph {
1721 let mut g = Graph::with_vertices(4);
1722 g.add_edge(0, 1).unwrap();
1723 g.add_edge(0, 2).unwrap();
1724 g.add_edge(1, 2).unwrap();
1725 g.add_edge(2, 3).unwrap();
1726 g
1727 }
1728
1729 #[test]
1730 fn test_weighted_clique_number_pendant_wins() {
1731 let g = triangle_plus_pendant();
1732 let w = weighted_clique_number(&g, &[1.0, 1.0, 1.0, 5.0]).unwrap();
1733 assert!((w - 6.0).abs() < 1e-9);
1734 }
1735
1736 #[test]
1737 fn test_weighted_clique_number_triangle_wins() {
1738 let g = triangle_plus_pendant();
1739 let w = weighted_clique_number(&g, &[3.0, 3.0, 3.0, 1.0]).unwrap();
1741 assert!((w - 9.0).abs() < 1e-9);
1742 }
1743
1744 #[test]
1745 fn test_weighted_clique_number_unit_weights_equals_clique_number() {
1746 let g = triangle_plus_pendant();
1747 let w = weighted_clique_number(&g, &[1.0, 1.0, 1.0, 1.0]).unwrap();
1748 assert!((w - f64::from(clique_number(&g).unwrap())).abs() < 1e-9);
1749 }
1750
1751 #[test]
1752 fn test_weighted_clique_number_truncates() {
1753 let g = triangle_plus_pendant();
1754 let w = weighted_clique_number(&g, &[2.9, 2.9, 2.9, 2.9]).unwrap();
1756 assert!((w - 6.0).abs() < 1e-9);
1757 }
1758
1759 #[test]
1760 fn test_weighted_clique_number_empty_graph() {
1761 let g = Graph::with_vertices(0);
1762 assert!(weighted_clique_number(&g, &[]).unwrap().abs() < 1e-9);
1763 }
1764
1765 #[test]
1766 fn test_weighted_clique_number_isolated_vertices() {
1767 let g = Graph::with_vertices(3);
1768 let w = weighted_clique_number(&g, &[2.0, 7.0, 3.0]).unwrap();
1769 assert!((w - 7.0).abs() < 1e-9);
1770 }
1771
1772 #[test]
1773 fn test_largest_weighted_cliques_single_winner() {
1774 let g = triangle_plus_pendant();
1775 let cs = largest_weighted_cliques(&g, &[1.0, 1.0, 1.0, 5.0]).unwrap();
1776 assert_eq!(cs, vec![vec![2, 3]]);
1777 }
1778
1779 #[test]
1780 fn test_largest_weighted_cliques_tie() {
1781 let mut g = Graph::with_vertices(4);
1783 g.add_edge(0, 1).unwrap();
1784 g.add_edge(2, 3).unwrap();
1785 let cs = largest_weighted_cliques(&g, &[1.0, 1.0, 1.0, 1.0]).unwrap();
1786 assert_eq!(cs, vec![vec![0, 1], vec![2, 3]]);
1787 }
1788
1789 #[test]
1790 fn test_weighted_cliques_all_maximal() {
1791 let g = triangle_plus_pendant();
1792 let cs = weighted_cliques(&g, &[1.0, 1.0, 1.0, 1.0], true, 0.0, 0.0, None).unwrap();
1793 assert_eq!(cs, vec![vec![0, 1, 2], vec![2, 3]]);
1794 }
1795
1796 #[test]
1797 fn test_weighted_cliques_min_weight_filter() {
1798 let g = triangle_plus_pendant();
1799 let cs = weighted_cliques(&g, &[1.0, 1.0, 1.0, 5.0], false, 3.0, 0.0, None).unwrap();
1801 assert!(cs.contains(&vec![2, 3]));
1803 assert!(cs.contains(&vec![0, 1, 2]));
1804 for c in &cs {
1805 let total: f64 = c.iter().map(|&v| [1.0, 1.0, 1.0, 5.0][v as usize]).sum();
1806 assert!(total >= 3.0);
1807 }
1808 }
1809
1810 #[test]
1811 fn test_weighted_cliques_max_weight_filter() {
1812 let g = triangle_plus_pendant();
1813 let cs = weighted_cliques(&g, &[1.0, 1.0, 1.0, 1.0], false, 0.0, 2.0, None).unwrap();
1815 for c in &cs {
1816 assert!(c.len() <= 2);
1817 }
1818 assert!(cs.contains(&vec![0]));
1819 assert!(cs.contains(&vec![0, 1]));
1820 assert!(!cs.contains(&vec![0, 1, 2]));
1821 }
1822
1823 #[test]
1824 fn test_weighted_cliques_max_results() {
1825 let g = triangle_plus_pendant();
1826 let cs = weighted_cliques(&g, &[1.0, 1.0, 1.0, 1.0], false, 0.0, 0.0, Some(2)).unwrap();
1827 assert_eq!(cs.len(), 2);
1828 }
1829
1830 #[test]
1831 fn test_weighted_cliques_wrong_weight_length_errors() {
1832 let g = triangle_plus_pendant();
1833 assert!(weighted_clique_number(&g, &[1.0, 1.0]).is_err());
1834 assert!(largest_weighted_cliques(&g, &[1.0, 1.0]).is_err());
1835 assert!(weighted_cliques(&g, &[1.0, 1.0], true, 0.0, 0.0, None).is_err());
1836 }
1837
1838 #[test]
1839 fn test_weighted_cliques_non_positive_weight_errors() {
1840 let g = triangle_plus_pendant();
1841 assert!(weighted_clique_number(&g, &[1.0, 0.0, 1.0, 1.0]).is_err());
1842 assert!(weighted_clique_number(&g, &[1.0, -2.0, 1.0, 1.0]).is_err());
1843 assert!(weighted_clique_number(&g, &[0.5, 1.0, 1.0, 1.0]).is_err());
1845 }
1846
1847 #[test]
1850 fn test_cliques_triangle() {
1851 let mut g = Graph::with_vertices(3);
1852 g.add_edge(0, 1).unwrap();
1853 g.add_edge(1, 2).unwrap();
1854 g.add_edge(0, 2).unwrap();
1855 let all = cliques(&g, 1, 3, None).unwrap();
1856 assert_eq!(all.len(), 7);
1858 }
1859
1860 #[test]
1861 fn test_cliques_size_filter() {
1862 let mut g = Graph::with_vertices(3);
1863 g.add_edge(0, 1).unwrap();
1864 g.add_edge(1, 2).unwrap();
1865 g.add_edge(0, 2).unwrap();
1866 let edges = cliques(&g, 2, 2, None).unwrap();
1868 assert_eq!(edges.len(), 3);
1869 for c in &edges {
1870 assert_eq!(c.len(), 2);
1871 }
1872 }
1873
1874 #[test]
1875 fn test_cliques_max_results() {
1876 let mut g = Graph::with_vertices(3);
1877 g.add_edge(0, 1).unwrap();
1878 g.add_edge(1, 2).unwrap();
1879 g.add_edge(0, 2).unwrap();
1880 let limited = cliques(&g, 1, 3, Some(4)).unwrap();
1881 assert_eq!(limited.len(), 4);
1882 }
1883
1884 #[test]
1885 fn test_cliques_k4_size_3() {
1886 let mut g = Graph::with_vertices(4);
1887 for i in 0..4u32 {
1888 for j in (i + 1)..4 {
1889 g.add_edge(i, j).unwrap();
1890 }
1891 }
1892 let tri = cliques(&g, 3, 3, None).unwrap();
1894 assert_eq!(tri.len(), 4);
1895 }
1896
1897 #[test]
1898 fn test_cliques_empty_graph() {
1899 let g = Graph::with_vertices(0);
1900 assert!(cliques(&g, 1, 5, None).unwrap().is_empty());
1901 }
1902
1903 #[test]
1904 fn test_cliques_no_edges_min_2() {
1905 let g = Graph::with_vertices(5);
1906 let result = cliques(&g, 2, 5, None).unwrap();
1907 assert!(result.is_empty());
1908 }
1909
1910 #[test]
1913 fn test_ivs_path_size_2() {
1914 let mut g = Graph::with_vertices(3);
1915 g.add_edge(0, 1).unwrap();
1916 g.add_edge(1, 2).unwrap();
1917 let sets = independent_vertex_sets(&g, 2, 3, None).unwrap();
1919 assert_eq!(sets.len(), 1);
1920 assert_eq!(sets[0], vec![0, 2]);
1921 }
1922
1923 #[test]
1924 fn test_ivs_complete_graph() {
1925 let mut g = Graph::with_vertices(4);
1926 for i in 0..4u32 {
1927 for j in (i + 1)..4 {
1928 g.add_edge(i, j).unwrap();
1929 }
1930 }
1931 let sets = independent_vertex_sets(&g, 2, 4, None).unwrap();
1933 assert!(sets.is_empty());
1934 }
1935
1936 #[test]
1937 fn test_ivs_no_edges() {
1938 let g = Graph::with_vertices(4);
1939 let sets = independent_vertex_sets(&g, 2, 2, None).unwrap();
1941 assert_eq!(sets.len(), 6);
1942 }
1943
1944 #[test]
1945 fn test_ivs_max_results() {
1946 let g = Graph::with_vertices(5);
1947 let sets = independent_vertex_sets(&g, 1, 5, Some(10)).unwrap();
1948 assert_eq!(sets.len(), 10);
1949 }
1950
1951 #[test]
1954 fn test_mcs_empty_graph() {
1955 let g = Graph::with_vertices(0);
1956 let r = maximal_cliques_subset(&g, &[], 0, 0, None).unwrap();
1957 assert!(r.is_empty());
1958 }
1959
1960 #[test]
1961 fn test_mcs_empty_subset() {
1962 let mut g = Graph::with_vertices(3);
1963 g.add_edge(0, 1).unwrap();
1964 let r = maximal_cliques_subset(&g, &[], 0, 0, None).unwrap();
1965 assert!(r.is_empty());
1966 }
1967
1968 #[test]
1969 fn test_mcs_invalid_vertex() {
1970 let g = Graph::with_vertices(3);
1971 assert!(maximal_cliques_subset(&g, &[5], 0, 0, None).is_err());
1972 }
1973
1974 #[test]
1975 fn test_mcs_triangle_plus_edge() {
1976 let mut g = Graph::with_vertices(4);
1978 g.add_edge(0, 1).unwrap();
1979 g.add_edge(0, 2).unwrap();
1980 g.add_edge(1, 2).unwrap();
1981 g.add_edge(2, 3).unwrap();
1982
1983 let r = maximal_cliques_subset(&g, &[3], 0, 0, None).unwrap();
1985 assert_eq!(r.len(), 1);
1986 assert!(r[0].contains(&2));
1987 assert!(r[0].contains(&3));
1988
1989 let r = maximal_cliques_subset(&g, &[0], 0, 0, None).unwrap();
1991 assert_eq!(r.len(), 1);
1992 assert_eq!(r[0].len(), 3);
1993
1994 let r = maximal_cliques_subset(&g, &[2], 0, 0, None).unwrap();
1996 assert_eq!(r.len(), 2);
1997 }
1998
1999 #[test]
2000 fn test_mcs_size_filter() {
2001 let mut g = Graph::with_vertices(4);
2003 g.add_edge(0, 1).unwrap();
2004 g.add_edge(0, 2).unwrap();
2005 g.add_edge(1, 2).unwrap();
2006 g.add_edge(2, 3).unwrap();
2007
2008 let r = maximal_cliques_subset(&g, &[2], 3, 0, None).unwrap();
2010 assert_eq!(r.len(), 1);
2011 assert_eq!(r[0].len(), 3);
2012
2013 let r = maximal_cliques_subset(&g, &[2], 0, 2, None).unwrap();
2015 assert_eq!(r.len(), 1);
2016 assert_eq!(r[0].len(), 2);
2017 }
2018
2019 #[test]
2020 fn test_mcs_max_results() {
2021 let mut g = Graph::with_vertices(4);
2023 for i in 0..4u32 {
2024 for j in (i + 1)..4 {
2025 g.add_edge(i, j).unwrap();
2026 }
2027 }
2028 let all = maximal_cliques_subset(&g, &[0, 1, 2, 3], 0, 0, None).unwrap();
2030 let limited = maximal_cliques_subset(&g, &[0, 1, 2, 3], 0, 0, Some(1)).unwrap();
2031 assert!(!all.is_empty());
2032 assert_eq!(limited.len(), 1);
2033 }
2034
2035 #[test]
2036 fn test_mcs_isolated_vertex_in_subset() {
2037 let mut g = Graph::with_vertices(3);
2039 g.add_edge(0, 1).unwrap();
2040
2041 let r = maximal_cliques_subset(&g, &[2], 0, 0, None).unwrap();
2043 assert_eq!(r.len(), 1);
2044 assert_eq!(r[0], vec![2]);
2045 }
2046
2047 #[test]
2048 fn test_mcs_directed_ignores_direction() {
2049 let mut g = Graph::new(3, true).unwrap();
2051 g.add_edge(0, 1).unwrap();
2052 g.add_edge(1, 2).unwrap();
2053 g.add_edge(2, 0).unwrap();
2054
2055 let r = maximal_cliques_subset(&g, &[0], 0, 0, None).unwrap();
2056 assert_eq!(r.len(), 1);
2057 assert_eq!(r[0].len(), 3);
2058 }
2059
2060 #[test]
2061 fn test_mcs_all_vertices_subset() {
2062 let mut g = Graph::with_vertices(5);
2064 g.add_edge(0, 1).unwrap();
2065 g.add_edge(1, 2).unwrap();
2066 g.add_edge(2, 3).unwrap();
2067 g.add_edge(3, 4).unwrap();
2068
2069 let subset: Vec<u32> = (0..5).collect();
2070 let mcs = maximal_cliques_subset(&g, &subset, 0, 0, None).unwrap();
2071 let mc = maximal_cliques(&g).unwrap();
2072 assert_eq!(mcs.len(), mc.len());
2073 }
2074}