solverforge_solver/builder/context/
coverage.rs1use std::fmt;
2
3use crate::builder::context::ScalarVariableSlot;
4use crate::planning::{CoverageGroup, CoverageGroupLimits, ScalarEdit};
5
6pub struct CoverageGroupBinding<S> {
7 pub group_name: &'static str,
8 pub target: ScalarVariableSlot<S>,
9 pub required_slot: fn(&S, usize) -> bool,
10 pub capacity_key: Option<fn(&S, usize, usize) -> Option<usize>>,
11 pub entity_order: Option<fn(&S, usize) -> i64>,
12 pub value_order: Option<fn(&S, usize, usize) -> i64>,
13 pub limits: CoverageGroupLimits,
14}
15
16impl<S> Clone for CoverageGroupBinding<S> {
17 fn clone(&self) -> Self {
18 *self
19 }
20}
21
22impl<S> Copy for CoverageGroupBinding<S> {}
23
24impl<S> CoverageGroupBinding<S> {
25 pub fn bind(group: CoverageGroup<S>, scalar_slots: &[ScalarVariableSlot<S>]) -> Self {
26 let target = group.target();
27 let target_slot = scalar_slots
28 .iter()
29 .copied()
30 .find(|slot| {
31 slot.descriptor_index == target.descriptor_index()
32 && slot.variable_name == target.variable_name()
33 })
34 .unwrap_or_else(|| {
35 panic!(
36 "coverage group `{}` target {}.{} did not match a scalar planning variable",
37 group.group_name(),
38 target.descriptor_index(),
39 target.variable_name(),
40 )
41 });
42 assert!(
43 target_slot.allows_unassigned,
44 "coverage group `{}` target {}.{} must allow unassigned values",
45 group.group_name(),
46 target_slot.entity_type_name,
47 target_slot.variable_name,
48 );
49 let required_slot = group.required_slot().unwrap_or_else(|| {
50 panic!(
51 "coverage group `{}` requires a required-slot predicate",
52 group.group_name(),
53 )
54 });
55 Self {
56 group_name: group.group_name(),
57 target: target_slot,
58 required_slot,
59 capacity_key: group.capacity_key(),
60 entity_order: group.entity_order(),
61 value_order: group.value_order(),
62 limits: group.limits(),
63 }
64 }
65
66 pub fn entity_count(&self, solution: &S) -> usize {
67 (self.target.entity_count)(solution)
68 }
69
70 pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
71 self.target.current_value(solution, entity_index)
72 }
73
74 pub fn is_required(&self, solution: &S, entity_index: usize) -> bool {
75 (self.required_slot)(solution, entity_index)
76 }
77
78 pub fn capacity_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<usize> {
79 self.capacity_key
80 .and_then(|capacity_key| capacity_key(solution, entity_index, value))
81 }
82
83 pub fn entity_order_key(&self, solution: &S, entity_index: usize) -> i64 {
84 self.entity_order
85 .map(|entity_order| entity_order(solution, entity_index))
86 .unwrap_or(entity_index as i64)
87 }
88
89 pub fn value_order_key(&self, solution: &S, entity_index: usize, value: usize) -> i64 {
90 self.value_order
91 .map(|value_order| value_order(solution, entity_index, value))
92 .unwrap_or(value as i64)
93 }
94
95 pub fn candidate_values(
96 &self,
97 solution: &S,
98 entity_index: usize,
99 value_candidate_limit: Option<usize>,
100 ) -> Vec<usize> {
101 let mut values =
102 self.target
103 .candidate_values_for_entity(solution, entity_index, value_candidate_limit);
104 values.sort_by_key(|value| self.value_order_key(solution, entity_index, *value));
105 values
106 }
107
108 pub fn value_is_legal(&self, solution: &S, entity_index: usize, value: Option<usize>) -> bool {
109 self.target.value_is_legal(solution, entity_index, value)
110 }
111
112 pub fn edit(&self, entity_index: usize, value: Option<usize>) -> ScalarEdit<S> {
113 ScalarEdit::from_descriptor_index(
114 self.target.descriptor_index,
115 entity_index,
116 self.target.variable_name,
117 value,
118 )
119 }
120}
121
122impl<S> fmt::Debug for CoverageGroupBinding<S> {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.debug_struct("CoverageGroupBinding")
125 .field("group_name", &self.group_name)
126 .field("entity_type_name", &self.target.entity_type_name)
127 .field("variable_name", &self.target.variable_name)
128 .field("has_capacity_key", &self.capacity_key.is_some())
129 .field("limits", &self.limits)
130 .finish()
131 }
132}
133
134pub fn bind_coverage_groups<S>(
135 groups: Vec<CoverageGroup<S>>,
136 scalar_slots: &[ScalarVariableSlot<S>],
137) -> Vec<CoverageGroupBinding<S>> {
138 groups
139 .into_iter()
140 .map(|group| CoverageGroupBinding::bind(group, scalar_slots))
141 .collect()
142}