1use crate::base::{EdgeWeight, Graph, Node};
14use crate::error::{GraphError, Result};
15use petgraph::visit::EdgeRef;
16use std::collections::{HashMap, HashSet};
17
18#[derive(Debug, Clone)]
20pub struct GraphColoring<N: Node> {
21 pub coloring: HashMap<N, usize>,
23 pub num_colors: usize,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ColoringOrder {
30 Natural,
32 LargestFirst,
34 SmallestLast,
36 DSatur,
38 Random,
40}
41
42#[derive(Debug, Clone)]
44pub struct EdgeColoring<N: Node> {
45 pub coloring: HashMap<(N, N), usize>,
47 pub num_colors: usize,
49 pub max_degree: usize,
51 pub is_class_one: bool,
53}
54
55#[derive(Debug, Clone)]
57pub struct ChromaticBounds {
58 pub lower: usize,
60 pub upper: usize,
62 pub best_num_colors: usize,
64}
65
66#[derive(Debug, Clone)]
68pub struct ListColoring<N: Node> {
69 pub feasible: bool,
71 pub coloring: HashMap<N, usize>,
73 pub num_colors: usize,
75}
76
77pub fn greedy_coloring<N, E, Ix>(graph: &Graph<N, E, Ix>) -> GraphColoring<N>
92where
93 N: Node + std::fmt::Debug,
94 E: EdgeWeight,
95 Ix: petgraph::graph::IndexType,
96{
97 greedy_coloring_with_order(graph, ColoringOrder::Natural)
98}
99
100pub fn greedy_coloring_with_order<N, E, Ix>(
102 graph: &Graph<N, E, Ix>,
103 order: ColoringOrder,
104) -> GraphColoring<N>
105where
106 N: Node + std::fmt::Debug,
107 E: EdgeWeight,
108 Ix: petgraph::graph::IndexType,
109{
110 let node_count = graph.inner().node_count();
111 if node_count == 0 {
112 return GraphColoring {
113 coloring: HashMap::new(),
114 num_colors: 0,
115 };
116 }
117
118 let ordered_nodes = compute_ordering(graph, order);
119 color_in_order(graph, &ordered_nodes)
120}
121
122fn compute_ordering<N, E, Ix>(graph: &Graph<N, E, Ix>, order: ColoringOrder) -> Vec<N>
124where
125 N: Node + std::fmt::Debug,
126 E: EdgeWeight,
127 Ix: petgraph::graph::IndexType,
128{
129 let nodes: Vec<N> = graph
130 .inner()
131 .node_indices()
132 .map(|idx| graph.inner()[idx].clone())
133 .collect();
134
135 match order {
136 ColoringOrder::Natural => nodes,
137 ColoringOrder::LargestFirst => largest_first_order(graph, &nodes),
138 ColoringOrder::SmallestLast => smallest_last_order(graph, &nodes),
139 ColoringOrder::DSatur => {
140 nodes
143 }
144 ColoringOrder::Random => {
145 let mut shuffled = nodes;
146 let n = shuffled.len();
148 for i in 0..n {
149 let j = (i * 7 + 3) % n;
150 shuffled.swap(i, j);
151 }
152 shuffled
153 }
154 }
155}
156
157fn largest_first_order<N, E, Ix>(graph: &Graph<N, E, Ix>, nodes: &[N]) -> Vec<N>
159where
160 N: Node + std::fmt::Debug,
161 E: EdgeWeight,
162 Ix: petgraph::graph::IndexType,
163{
164 let mut node_degrees: Vec<(N, usize)> =
165 nodes.iter().map(|n| (n.clone(), graph.degree(n))).collect();
166 node_degrees.sort_by(|a, b| b.1.cmp(&a.1));
167 node_degrees.into_iter().map(|(n, _)| n).collect()
168}
169
170fn smallest_last_order<N, E, Ix>(graph: &Graph<N, E, Ix>, nodes: &[N]) -> Vec<N>
175where
176 N: Node + std::fmt::Debug,
177 E: EdgeWeight,
178 Ix: petgraph::graph::IndexType,
179{
180 let n = nodes.len();
181 let mut remaining: HashSet<N> = nodes.iter().cloned().collect();
182 let mut order = Vec::with_capacity(n);
183
184 let mut adj: HashMap<N, HashSet<N>> = HashMap::new();
186 for node in nodes {
187 if let Ok(neighbors) = graph.neighbors(node) {
188 let neighbor_set: HashSet<N> = neighbors.into_iter().collect();
189 adj.insert(node.clone(), neighbor_set);
190 } else {
191 adj.insert(node.clone(), HashSet::new());
192 }
193 }
194
195 for _ in 0..n {
196 let mut min_deg = usize::MAX;
198 let mut min_node = None;
199
200 for node in &remaining {
201 let deg = adj
202 .get(node)
203 .map(|neighbors| neighbors.iter().filter(|n| remaining.contains(n)).count())
204 .unwrap_or(0);
205 if deg < min_deg {
206 min_deg = deg;
207 min_node = Some(node.clone());
208 }
209 }
210
211 if let Some(node) = min_node {
212 order.push(node.clone());
213 remaining.remove(&node);
214 }
215 }
216
217 order.reverse();
219 order
220}
221
222fn color_in_order<N, E, Ix>(graph: &Graph<N, E, Ix>, ordered_nodes: &[N]) -> GraphColoring<N>
224where
225 N: Node + std::fmt::Debug,
226 E: EdgeWeight,
227 Ix: petgraph::graph::IndexType,
228{
229 let mut coloring: HashMap<N, usize> = HashMap::new();
230 let mut max_color = 0;
231
232 for node in ordered_nodes {
233 let mut used_colors = HashSet::new();
235 if let Ok(neighbors) = graph.neighbors(node) {
236 for neighbor in &neighbors {
237 if let Some(&color) = coloring.get(neighbor) {
238 used_colors.insert(color);
239 }
240 }
241 }
242
243 let mut color = 0;
245 while used_colors.contains(&color) {
246 color += 1;
247 }
248
249 coloring.insert(node.clone(), color);
250 if color >= max_color {
251 max_color = color + 1;
252 }
253 }
254
255 GraphColoring {
256 coloring,
257 num_colors: max_color,
258 }
259}
260
261pub fn dsatur_coloring<N, E, Ix>(graph: &Graph<N, E, Ix>) -> GraphColoring<N>
273where
274 N: Node + std::fmt::Debug,
275 E: EdgeWeight,
276 Ix: petgraph::graph::IndexType,
277{
278 let nodes: Vec<N> = graph
279 .inner()
280 .node_indices()
281 .map(|idx| graph.inner()[idx].clone())
282 .collect();
283
284 let n = nodes.len();
285 if n == 0 {
286 return GraphColoring {
287 coloring: HashMap::new(),
288 num_colors: 0,
289 };
290 }
291
292 let mut coloring: HashMap<N, usize> = HashMap::new();
293 let mut saturation: HashMap<N, HashSet<usize>> = HashMap::new();
294 let mut colored = HashSet::new();
295 let mut max_color = 0;
296
297 for node in &nodes {
298 saturation.insert(node.clone(), HashSet::new());
299 }
300
301 for _ in 0..n {
302 let mut best_node = None;
304 let mut best_sat = 0;
305 let mut best_deg = 0;
306
307 for node in &nodes {
308 if colored.contains(node) {
309 continue;
310 }
311
312 let sat = saturation.get(node).map(|s| s.len()).unwrap_or(0);
313 let deg = graph.degree(node);
314
315 if best_node.is_none() || sat > best_sat || (sat == best_sat && deg > best_deg) {
316 best_node = Some(node.clone());
317 best_sat = sat;
318 best_deg = deg;
319 }
320 }
321
322 if let Some(node) = best_node {
323 let mut used_colors = HashSet::new();
325 if let Ok(neighbors) = graph.neighbors(&node) {
326 for neighbor in &neighbors {
327 if let Some(&color) = coloring.get(neighbor) {
328 used_colors.insert(color);
329 }
330 }
331 }
332
333 let mut color = 0;
335 while used_colors.contains(&color) {
336 color += 1;
337 }
338
339 coloring.insert(node.clone(), color);
340 colored.insert(node.clone());
341 if color + 1 > max_color {
342 max_color = color + 1;
343 }
344
345 if let Ok(neighbors) = graph.neighbors(&node) {
347 for neighbor in &neighbors {
348 if !colored.contains(neighbor) {
349 if let Some(sat) = saturation.get_mut(neighbor) {
350 sat.insert(color);
351 }
352 }
353 }
354 }
355 }
356 }
357
358 GraphColoring {
359 coloring,
360 num_colors: max_color,
361 }
362}
363
364pub fn welsh_powell<N, E, Ix>(graph: &Graph<N, E, Ix>) -> GraphColoring<N>
373where
374 N: Node + std::fmt::Debug,
375 E: EdgeWeight,
376 Ix: petgraph::graph::IndexType,
377{
378 greedy_coloring_with_order(graph, ColoringOrder::LargestFirst)
379}
380
381pub fn chromatic_number<N, E, Ix>(graph: &Graph<N, E, Ix>, max_colors: usize) -> Option<usize>
390where
391 N: Node + std::fmt::Debug,
392 E: EdgeWeight,
393 Ix: petgraph::graph::IndexType,
394{
395 if graph.inner().node_count() == 0 {
396 return Some(0);
397 }
398
399 (1..=max_colors).find(|&num_colors| can_color_with_k_colors(graph, num_colors))
400}
401
402fn can_color_with_k_colors<N, E, Ix>(graph: &Graph<N, E, Ix>, k: usize) -> bool
404where
405 N: Node + std::fmt::Debug,
406 E: EdgeWeight,
407 Ix: petgraph::graph::IndexType,
408{
409 let nodes: Vec<_> = graph.inner().node_indices().collect();
410 let mut coloring = vec![0usize; nodes.len()];
411
412 fn backtrack<N, E, Ix>(
413 graph: &Graph<N, E, Ix>,
414 nodes: &[petgraph::graph::NodeIndex<Ix>],
415 coloring: &mut [usize],
416 node_idx: usize,
417 k: usize,
418 ) -> bool
419 where
420 N: Node + std::fmt::Debug,
421 E: EdgeWeight,
422 Ix: petgraph::graph::IndexType,
423 {
424 if node_idx == nodes.len() {
425 return true;
426 }
427
428 let node = nodes[node_idx];
429
430 for color in 0..k {
431 let mut valid = true;
432 for (i, &other_node) in nodes.iter().enumerate().take(node_idx) {
433 if (graph.inner().contains_edge(node, other_node)
434 || graph.inner().contains_edge(other_node, node))
435 && coloring[i] == color
436 {
437 valid = false;
438 break;
439 }
440 }
441
442 if valid {
443 coloring[node_idx] = color;
444 if backtrack(graph, nodes, coloring, node_idx + 1, k) {
445 return true;
446 }
447 }
448 }
449
450 false
451 }
452
453 backtrack(graph, &nodes, &mut coloring, 0, k)
454}
455
456pub fn chromatic_bounds<N, E, Ix>(graph: &Graph<N, E, Ix>) -> ChromaticBounds
461where
462 N: Node + std::fmt::Debug,
463 E: EdgeWeight,
464 Ix: petgraph::graph::IndexType,
465{
466 let n = graph.inner().node_count();
467 if n == 0 {
468 return ChromaticBounds {
469 lower: 0,
470 upper: 0,
471 best_num_colors: 0,
472 };
473 }
474
475 let lower = greedy_clique_number(graph);
477
478 let dsatur_result = dsatur_coloring(graph);
480 let welsh_result = welsh_powell(graph);
481 let sl_result = greedy_coloring_with_order(graph, ColoringOrder::SmallestLast);
482
483 let upper = dsatur_result
484 .num_colors
485 .min(welsh_result.num_colors)
486 .min(sl_result.num_colors);
487
488 let max_degree = graph
490 .inner()
491 .node_indices()
492 .map(|idx| graph.inner().neighbors(idx).count())
493 .max()
494 .unwrap_or(0);
495 let brooks_upper = max_degree + 1;
496
497 let final_upper = upper.min(brooks_upper);
498
499 ChromaticBounds {
500 lower,
501 upper: final_upper,
502 best_num_colors: final_upper,
503 }
504}
505
506fn greedy_clique_number<N, E, Ix>(graph: &Graph<N, E, Ix>) -> usize
508where
509 N: Node + std::fmt::Debug,
510 E: EdgeWeight,
511 Ix: petgraph::graph::IndexType,
512{
513 let nodes: Vec<N> = graph
514 .inner()
515 .node_indices()
516 .map(|idx| graph.inner()[idx].clone())
517 .collect();
518
519 if nodes.is_empty() {
520 return 0;
521 }
522
523 let mut max_clique_size = 1;
524
525 for start_node in &nodes {
527 let mut clique = vec![start_node.clone()];
528
529 let mut candidates: Vec<N> = Vec::new();
532 if let Ok(neighbors) = graph.neighbors(start_node) {
533 candidates = neighbors;
534 }
535 candidates.sort_by_key(|b| std::cmp::Reverse(graph.degree(b)));
536
537 for candidate in &candidates {
538 let all_adjacent = clique.iter().all(|c| graph.has_edge(candidate, c));
540 if all_adjacent {
541 clique.push(candidate.clone());
542 }
543 }
544
545 max_clique_size = max_clique_size.max(clique.len());
546 }
547
548 max_clique_size
549}
550
551pub fn edge_coloring<N, E, Ix>(graph: &Graph<N, E, Ix>) -> Result<EdgeColoring<N>>
561where
562 N: Node + Clone + std::fmt::Debug,
563 E: EdgeWeight + Clone,
564 Ix: petgraph::graph::IndexType,
565{
566 let edges = graph.edges();
567 let n = graph.inner().node_count();
568
569 if n == 0 || edges.is_empty() {
570 return Ok(EdgeColoring {
571 coloring: HashMap::new(),
572 num_colors: 0,
573 max_degree: 0,
574 is_class_one: true,
575 });
576 }
577
578 let max_degree = graph
579 .inner()
580 .node_indices()
581 .map(|idx| graph.inner().neighbors(idx).count())
582 .max()
583 .unwrap_or(0);
584
585 let mut edge_colors: HashMap<(N, N), usize> = HashMap::new();
588 let mut node_used_colors: HashMap<N, HashSet<usize>> = HashMap::new();
589 let mut num_colors = 0;
590
591 for edge in &edges {
592 let src = edge.source.clone();
593 let tgt = edge.target.clone();
594
595 let src_colors = node_used_colors.get(&src).cloned().unwrap_or_default();
597 let tgt_colors = node_used_colors.get(&tgt).cloned().unwrap_or_default();
599
600 let mut color = 0;
602 while src_colors.contains(&color) || tgt_colors.contains(&color) {
603 color += 1;
604 }
605
606 edge_colors.insert((src.clone(), tgt.clone()), color);
607
608 node_used_colors.entry(src).or_default().insert(color);
609 node_used_colors.entry(tgt).or_default().insert(color);
610
611 if color + 1 > num_colors {
612 num_colors = color + 1;
613 }
614 }
615
616 let is_class_one = num_colors <= max_degree;
617
618 Ok(EdgeColoring {
619 coloring: edge_colors,
620 num_colors,
621 max_degree,
622 is_class_one,
623 })
624}
625
626pub fn list_coloring<N, E, Ix>(
639 graph: &Graph<N, E, Ix>,
640 color_lists: &HashMap<N, Vec<usize>>,
641) -> Result<ListColoring<N>>
642where
643 N: Node + Clone + std::fmt::Debug,
644 E: EdgeWeight,
645 Ix: petgraph::graph::IndexType,
646{
647 let nodes: Vec<N> = graph
648 .inner()
649 .node_indices()
650 .map(|idx| graph.inner()[idx].clone())
651 .collect();
652
653 if nodes.is_empty() {
654 return Ok(ListColoring {
655 feasible: true,
656 coloring: HashMap::new(),
657 num_colors: 0,
658 });
659 }
660
661 for node in &nodes {
663 if !color_lists.contains_key(node) {
664 return Err(GraphError::InvalidGraph(format!(
665 "Node {node:?} has no color list"
666 )));
667 }
668 }
669
670 let mut coloring: HashMap<N, usize> = HashMap::new();
671 let feasible = list_coloring_backtrack(graph, &nodes, color_lists, &mut coloring, 0);
672
673 if feasible {
674 let num_colors = coloring.values().copied().collect::<HashSet<_>>().len();
675 Ok(ListColoring {
676 feasible: true,
677 coloring,
678 num_colors,
679 })
680 } else {
681 Ok(ListColoring {
682 feasible: false,
683 coloring: HashMap::new(),
684 num_colors: 0,
685 })
686 }
687}
688
689fn list_coloring_backtrack<N, E, Ix>(
691 graph: &Graph<N, E, Ix>,
692 nodes: &[N],
693 color_lists: &HashMap<N, Vec<usize>>,
694 coloring: &mut HashMap<N, usize>,
695 idx: usize,
696) -> bool
697where
698 N: Node + Clone + std::fmt::Debug,
699 E: EdgeWeight,
700 Ix: petgraph::graph::IndexType,
701{
702 if idx == nodes.len() {
703 return true;
704 }
705
706 let node = &nodes[idx];
707 let allowed = color_lists.get(node).cloned().unwrap_or_default();
708
709 let mut forbidden = HashSet::new();
711 if let Ok(neighbors) = graph.neighbors(node) {
712 for neighbor in &neighbors {
713 if let Some(&c) = coloring.get(neighbor) {
714 forbidden.insert(c);
715 }
716 }
717 }
718
719 for &color in &allowed {
720 if !forbidden.contains(&color) {
721 coloring.insert(node.clone(), color);
722 if list_coloring_backtrack(graph, nodes, color_lists, coloring, idx + 1) {
723 return true;
724 }
725 coloring.remove(node);
726 }
727 }
728
729 false
730}
731
732pub fn verify_coloring<N, E, Ix>(graph: &Graph<N, E, Ix>, coloring: &HashMap<N, usize>) -> bool
738where
739 N: Node + std::fmt::Debug,
740 E: EdgeWeight,
741 Ix: petgraph::graph::IndexType,
742{
743 for edge in graph.inner().edge_references() {
744 let src = &graph.inner()[edge.source()];
745 let tgt = &graph.inner()[edge.target()];
746
747 if let (Some(&c1), Some(&c2)) = (coloring.get(src), coloring.get(tgt)) {
748 if c1 == c2 {
749 return false;
750 }
751 }
752 }
753 true
754}
755
756#[cfg(test)]
761mod tests {
762 use super::*;
763 use crate::generators::create_graph;
764 use petgraph::visit::EdgeRef;
765
766 #[test]
767 fn test_greedy_coloring() {
768 let mut graph = create_graph::<char, ()>();
769 graph.add_edge('A', 'B', ()).expect("add edge");
770 graph.add_edge('B', 'C', ()).expect("add edge");
771 graph.add_edge('C', 'A', ()).expect("add edge");
772
773 let coloring = greedy_coloring(&graph);
774 assert_eq!(coloring.num_colors, 3);
775 assert_ne!(coloring.coloring[&'A'], coloring.coloring[&'B']);
776 assert_ne!(coloring.coloring[&'B'], coloring.coloring[&'C']);
777 assert_ne!(coloring.coloring[&'C'], coloring.coloring[&'A']);
778 }
779
780 #[test]
781 fn test_bipartite_graph_coloring() {
782 let mut graph = create_graph::<i32, ()>();
783 graph.add_edge(0, 1, ()).expect("add edge");
784 graph.add_edge(0, 3, ()).expect("add edge");
785 graph.add_edge(2, 1, ()).expect("add edge");
786 graph.add_edge(2, 3, ()).expect("add edge");
787
788 let coloring = greedy_coloring(&graph);
789 assert!(coloring.num_colors <= 2);
790 }
791
792 #[test]
793 fn test_chromatic_number() {
794 let mut triangle = create_graph::<i32, ()>();
795 triangle.add_edge(0, 1, ()).expect("add edge");
796 triangle.add_edge(1, 2, ()).expect("add edge");
797 triangle.add_edge(2, 0, ()).expect("add edge");
798
799 assert_eq!(chromatic_number(&triangle, 5), Some(3));
800
801 let mut bipartite = create_graph::<i32, ()>();
802 bipartite.add_edge(0, 1, ()).expect("add edge");
803 bipartite.add_edge(1, 2, ()).expect("add edge");
804 bipartite.add_edge(2, 3, ()).expect("add edge");
805 bipartite.add_edge(3, 0, ()).expect("add edge");
806
807 assert_eq!(chromatic_number(&bipartite, 5), Some(2));
808
809 let empty = create_graph::<i32, ()>();
810 assert_eq!(chromatic_number(&empty, 5), Some(0));
811 }
812
813 #[test]
814 fn test_largest_first_coloring() {
815 let mut graph = create_graph::<i32, ()>();
816 graph.add_edge(0, 1, ()).expect("add edge");
817 graph.add_edge(0, 2, ()).expect("add edge");
818 graph.add_edge(0, 3, ()).expect("add edge");
819 graph.add_edge(1, 2, ()).expect("add edge");
820
821 let coloring = greedy_coloring_with_order(&graph, ColoringOrder::LargestFirst);
822 assert!(verify_coloring(&graph, &coloring.coloring));
823 }
824
825 #[test]
826 fn test_smallest_last_coloring() {
827 let mut graph = create_graph::<i32, ()>();
828 for i in 0..5 {
829 for j in (i + 1)..5 {
830 graph.add_edge(i, j, ()).expect("add edge");
831 }
832 }
833
834 let coloring = greedy_coloring_with_order(&graph, ColoringOrder::SmallestLast);
835 assert!(verify_coloring(&graph, &coloring.coloring));
836 assert_eq!(coloring.num_colors, 5); }
838
839 #[test]
840 fn test_dsatur_coloring() {
841 let mut graph = create_graph::<i32, ()>();
842 graph.add_edge(0, 1, ()).expect("add edge");
843 graph.add_edge(1, 2, ()).expect("add edge");
844 graph.add_edge(2, 0, ()).expect("add edge");
845 graph.add_edge(2, 3, ()).expect("add edge");
846 graph.add_edge(3, 4, ()).expect("add edge");
847
848 let coloring = dsatur_coloring(&graph);
849 assert!(verify_coloring(&graph, &coloring.coloring));
850 assert!(coloring.num_colors >= 3);
852 }
853
854 #[test]
855 fn test_welsh_powell() {
856 let mut graph = create_graph::<i32, ()>();
857 graph.add_edge(0, 3, ()).expect("add edge");
859 graph.add_edge(0, 4, ()).expect("add edge");
860 graph.add_edge(1, 3, ()).expect("add edge");
861 graph.add_edge(1, 5, ()).expect("add edge");
862 graph.add_edge(2, 4, ()).expect("add edge");
863 graph.add_edge(2, 5, ()).expect("add edge");
864
865 let coloring = welsh_powell(&graph);
866 assert!(verify_coloring(&graph, &coloring.coloring));
867 assert!(coloring.num_colors <= 2);
868 }
869
870 #[test]
871 fn test_chromatic_bounds() {
872 let mut graph = create_graph::<i32, ()>();
873 graph.add_edge(0, 1, ()).expect("add edge");
876 graph.add_edge(1, 2, ()).expect("add edge");
877 graph.add_edge(2, 0, ()).expect("add edge");
878
879 let bounds = chromatic_bounds(&graph);
880 assert!(bounds.lower >= 3); assert!(bounds.upper >= 3);
882 assert!(bounds.lower <= bounds.upper);
883 }
884
885 #[test]
886 fn test_edge_coloring() -> Result<()> {
887 let mut graph = create_graph::<i32, ()>();
888 graph.add_edge(0, 1, ())?;
889 graph.add_edge(1, 2, ())?;
890 graph.add_edge(2, 0, ())?;
891
892 let result = edge_coloring(&graph)?;
893 assert_eq!(result.max_degree, 2);
894 assert!(result.num_colors <= result.max_degree + 1);
896
897 for edge1 in graph.inner().edge_references() {
899 for edge2 in graph.inner().edge_references() {
900 if edge1.id() == edge2.id() {
901 continue;
902 }
903 if edge1.source() == edge2.source()
904 || edge1.source() == edge2.target()
905 || edge1.target() == edge2.source()
906 || edge1.target() == edge2.target()
907 {
908 let s1 = graph.inner()[edge1.source()].clone();
909 let t1 = graph.inner()[edge1.target()].clone();
910 let s2 = graph.inner()[edge2.source()].clone();
911 let t2 = graph.inner()[edge2.target()].clone();
912
913 let c1 = result
914 .coloring
915 .get(&(s1.clone(), t1.clone()))
916 .or_else(|| result.coloring.get(&(t1, s1)));
917 let c2 = result
918 .coloring
919 .get(&(s2.clone(), t2.clone()))
920 .or_else(|| result.coloring.get(&(t2, s2)));
921
922 if let (Some(c1), Some(c2)) = (c1, c2) {
923 assert_ne!(c1, c2, "Adjacent edges should have different colors");
924 }
925 }
926 }
927 }
928 Ok(())
929 }
930
931 #[test]
932 fn test_edge_coloring_bipartite() -> Result<()> {
933 let mut graph = create_graph::<i32, ()>();
935 graph.add_edge(0, 2, ())?;
936 graph.add_edge(0, 3, ())?;
937 graph.add_edge(1, 2, ())?;
938 graph.add_edge(1, 3, ())?;
939
940 let result = edge_coloring(&graph)?;
941 assert_eq!(result.max_degree, 2);
942 assert!(result.num_colors <= 3); Ok(())
944 }
945
946 #[test]
947 fn test_list_coloring_feasible() -> Result<()> {
948 let mut graph = create_graph::<i32, ()>();
949 graph.add_edge(0, 1, ())?;
950 graph.add_edge(1, 2, ())?;
951
952 let mut lists = HashMap::new();
953 lists.insert(0, vec![0, 1]);
954 lists.insert(1, vec![0, 1]);
955 lists.insert(2, vec![0, 1]);
956
957 let result = list_coloring(&graph, &lists)?;
958 assert!(result.feasible);
959 assert!(result.num_colors <= 2);
960 Ok(())
961 }
962
963 #[test]
964 fn test_list_coloring_infeasible() -> Result<()> {
965 let mut graph = create_graph::<i32, ()>();
967 graph.add_edge(0, 1, ())?;
968 graph.add_edge(1, 2, ())?;
969 graph.add_edge(2, 0, ())?;
970
971 let mut lists = HashMap::new();
972 lists.insert(0, vec![0, 1]);
973 lists.insert(1, vec![0, 1]);
974 lists.insert(2, vec![0, 1]); let result = list_coloring(&graph, &lists)?;
977 assert!(!result.feasible);
978 Ok(())
979 }
980
981 #[test]
982 fn test_list_coloring_with_enough_colors() -> Result<()> {
983 let mut graph = create_graph::<i32, ()>();
984 graph.add_edge(0, 1, ())?;
985 graph.add_edge(1, 2, ())?;
986 graph.add_edge(2, 0, ())?;
987
988 let mut lists = HashMap::new();
989 lists.insert(0, vec![0, 1, 2]);
990 lists.insert(1, vec![0, 1, 2]);
991 lists.insert(2, vec![0, 1, 2]);
992
993 let result = list_coloring(&graph, &lists)?;
994 assert!(result.feasible);
995 assert_eq!(result.num_colors, 3);
996 Ok(())
997 }
998
999 #[test]
1000 fn test_verify_coloring_valid() {
1001 let mut graph = create_graph::<i32, ()>();
1002 graph.add_edge(0, 1, ()).expect("add edge");
1003 graph.add_edge(1, 2, ()).expect("add edge");
1004
1005 let mut coloring = HashMap::new();
1006 coloring.insert(0, 0);
1007 coloring.insert(1, 1);
1008 coloring.insert(2, 0);
1009
1010 assert!(verify_coloring(&graph, &coloring));
1011 }
1012
1013 #[test]
1014 fn test_verify_coloring_invalid() {
1015 let mut graph = create_graph::<i32, ()>();
1016 graph.add_edge(0, 1, ()).expect("add edge");
1017
1018 let mut coloring = HashMap::new();
1019 coloring.insert(0, 0);
1020 coloring.insert(1, 0); assert!(!verify_coloring(&graph, &coloring));
1023 }
1024
1025 #[test]
1026 fn test_empty_graph_coloring() {
1027 let graph = create_graph::<i32, ()>();
1028 let coloring = greedy_coloring(&graph);
1029 assert_eq!(coloring.num_colors, 0);
1030 assert!(coloring.coloring.is_empty());
1031 }
1032
1033 #[test]
1034 fn test_single_node() {
1035 let mut graph = create_graph::<i32, ()>();
1036 let _ = graph.add_node(42);
1037 let coloring = greedy_coloring(&graph);
1038 assert_eq!(coloring.num_colors, 1);
1039 assert_eq!(coloring.coloring[&42], 0);
1040 }
1041}