1use std::fmt;
2
3use super::value_source::ValueSource;
4
5pub type ScalarGetter<S> = fn(&S, usize, usize) -> Option<usize>;
6pub type ScalarSetter<S> = fn(&mut S, usize, usize, Option<usize>);
7pub type ScalarCandidateValues<S> = for<'a> fn(&'a S, usize, usize) -> &'a [usize];
8pub type NearbyValueDistanceMeter<S> = fn(&S, usize, usize, usize) -> Option<f64>;
9pub type NearbyEntityDistanceMeter<S> = fn(&S, usize, usize, usize) -> Option<f64>;
10pub type ConstructionEntityOrderKey<S> = fn(&S, usize, usize) -> Option<i64>;
11pub type ConstructionValueOrderKey<S> = fn(&S, usize, usize, usize) -> Option<i64>;
12pub type SearchEntityOrderKey<S> = fn(&S, usize, usize) -> Option<i64>;
13pub type SearchValueOrderKey<S> = fn(&S, usize, usize, usize) -> Option<i64>;
14
15pub struct ScalarVariableSlot<S> {
16 pub descriptor_index: usize,
17 pub variable_index: usize,
18 pub entity_type_name: &'static str,
19 pub entity_count: fn(&S) -> usize,
20 pub variable_name: &'static str,
21 pub getter: ScalarGetter<S>,
22 pub setter: ScalarSetter<S>,
23 pub value_source: ValueSource<S>,
24 pub allows_unassigned: bool,
25 pub candidate_values: Option<ScalarCandidateValues<S>>,
26 pub nearby_value_candidates: Option<ScalarCandidateValues<S>>,
27 pub nearby_entity_candidates: Option<ScalarCandidateValues<S>>,
28 pub nearby_value_distance_meter: Option<NearbyValueDistanceMeter<S>>,
29 pub nearby_entity_distance_meter: Option<NearbyEntityDistanceMeter<S>>,
30 pub construction_entity_order_key: Option<ConstructionEntityOrderKey<S>>,
31 pub construction_value_order_key: Option<ConstructionValueOrderKey<S>>,
32 pub search_entity_order_key: Option<SearchEntityOrderKey<S>>,
33 pub search_value_order_key: Option<SearchValueOrderKey<S>>,
34}
35
36impl<S> Clone for ScalarVariableSlot<S> {
37 fn clone(&self) -> Self {
38 *self
39 }
40}
41
42impl<S> Copy for ScalarVariableSlot<S> {}
43
44impl<S> ScalarVariableSlot<S> {
45 #[allow(clippy::too_many_arguments)]
46 pub fn new(
47 descriptor_index: usize,
48 variable_index: usize,
49 entity_type_name: &'static str,
50 entity_count: fn(&S) -> usize,
51 variable_name: &'static str,
52 getter: ScalarGetter<S>,
53 setter: ScalarSetter<S>,
54 value_source: ValueSource<S>,
55 allows_unassigned: bool,
56 ) -> Self {
57 Self {
58 descriptor_index,
59 variable_index,
60 entity_type_name,
61 entity_count,
62 variable_name,
63 getter,
64 setter,
65 value_source,
66 allows_unassigned,
67 candidate_values: None,
68 nearby_value_candidates: None,
69 nearby_entity_candidates: None,
70 nearby_value_distance_meter: None,
71 nearby_entity_distance_meter: None,
72 construction_entity_order_key: None,
73 construction_value_order_key: None,
74 search_entity_order_key: None,
75 search_value_order_key: None,
76 }
77 }
78
79 pub fn with_nearby_value_distance_meter(mut self, meter: NearbyValueDistanceMeter<S>) -> Self {
80 self.nearby_value_distance_meter = Some(meter);
81 self
82 }
83
84 pub fn with_candidate_values(mut self, provider: ScalarCandidateValues<S>) -> Self {
85 self.candidate_values = Some(provider);
86 self
87 }
88
89 pub fn with_nearby_value_candidates(mut self, provider: ScalarCandidateValues<S>) -> Self {
90 self.nearby_value_candidates = Some(provider);
91 self
92 }
93
94 pub fn with_nearby_entity_candidates(mut self, provider: ScalarCandidateValues<S>) -> Self {
95 self.nearby_entity_candidates = Some(provider);
96 self
97 }
98
99 pub fn with_nearby_entity_distance_meter(
100 mut self,
101 meter: NearbyEntityDistanceMeter<S>,
102 ) -> Self {
103 self.nearby_entity_distance_meter = Some(meter);
104 self
105 }
106
107 pub fn with_construction_entity_order_key(
108 mut self,
109 order_key: ConstructionEntityOrderKey<S>,
110 ) -> Self {
111 self.construction_entity_order_key = Some(order_key);
112 self
113 }
114
115 pub fn with_construction_value_order_key(
116 mut self,
117 order_key: ConstructionValueOrderKey<S>,
118 ) -> Self {
119 self.construction_value_order_key = Some(order_key);
120 self
121 }
122
123 pub fn with_search_entity_order_key(mut self, order_key: SearchEntityOrderKey<S>) -> Self {
124 self.search_entity_order_key = Some(order_key);
125 self
126 }
127
128 pub fn with_search_value_order_key(mut self, order_key: SearchValueOrderKey<S>) -> Self {
129 self.search_value_order_key = Some(order_key);
130 self
131 }
132
133 pub fn matches_target(&self, entity_class: Option<&str>, variable_name: Option<&str>) -> bool {
134 entity_class.is_none_or(|name| name == self.entity_type_name)
135 && variable_name.is_none_or(|name| name == self.variable_name)
136 }
137
138 pub fn supports_nearby_change(&self) -> bool {
139 self.nearby_value_candidates.is_some()
140 }
141
142 pub fn supports_nearby_swap(&self) -> bool {
143 self.nearby_entity_candidates.is_some()
144 }
145
146 pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
147 (self.getter)(solution, entity_index, self.variable_index)
148 }
149
150 pub fn set_value(&self, solution: &mut S, entity_index: usize, value: Option<usize>) {
151 (self.setter)(solution, entity_index, self.variable_index, value);
152 }
153
154 pub fn values_for_entity(&self, solution: &S, entity_index: usize) -> Vec<usize> {
155 match self.value_source {
156 ValueSource::Empty => Vec::new(),
157 ValueSource::CountableRange { from, to } => (from..to).collect(),
158 ValueSource::SolutionCount {
159 count_fn,
160 provider_index,
161 } => (0..count_fn(solution, provider_index)).collect(),
162 ValueSource::EntitySlice { values_for_entity } => {
163 values_for_entity(solution, entity_index, self.variable_index).to_vec()
164 }
165 }
166 }
167
168 pub fn candidate_values_for_entity(
169 &self,
170 solution: &S,
171 entity_index: usize,
172 value_candidate_limit: Option<usize>,
173 ) -> Vec<usize> {
174 if let Some(provider) = self.candidate_values {
175 let values = provider(solution, entity_index, self.variable_index);
176 return match value_candidate_limit {
177 Some(limit) => values.iter().copied().take(limit).collect(),
178 None => values.to_vec(),
179 };
180 }
181
182 match self.value_source {
183 ValueSource::Empty => Vec::new(),
184 ValueSource::CountableRange { from, to } => {
185 let end = value_candidate_limit
186 .map(|limit| from.saturating_add(limit).min(to))
187 .unwrap_or(to);
188 (from..end).collect()
189 }
190 ValueSource::SolutionCount {
191 count_fn,
192 provider_index,
193 } => {
194 let count = count_fn(solution, provider_index);
195 let end = value_candidate_limit
196 .map(|limit| limit.min(count))
197 .unwrap_or(count);
198 (0..end).collect()
199 }
200 ValueSource::EntitySlice { values_for_entity } => {
201 let values = values_for_entity(solution, entity_index, self.variable_index);
202 match value_candidate_limit {
203 Some(limit) => values.iter().copied().take(limit).collect(),
204 None => values.to_vec(),
205 }
206 }
207 }
208 }
209
210 pub fn has_values_for_entity(&self, solution: &S, entity_index: usize) -> bool {
211 match self.value_source {
212 ValueSource::Empty => false,
213 ValueSource::CountableRange { from, to } => from < to,
214 ValueSource::SolutionCount {
215 count_fn,
216 provider_index,
217 } => count_fn(solution, provider_index) > 0,
218 ValueSource::EntitySlice { values_for_entity } => {
219 !values_for_entity(solution, entity_index, self.variable_index).is_empty()
220 }
221 }
222 }
223
224 pub fn value_is_legal(
225 &self,
226 solution: &S,
227 entity_index: usize,
228 candidate: Option<usize>,
229 ) -> bool {
230 let Some(value) = candidate else {
231 return self.allows_unassigned;
232 };
233 match self.value_source {
234 ValueSource::Empty => false,
235 ValueSource::CountableRange { from, to } => from <= value && value < to,
236 ValueSource::SolutionCount {
237 count_fn,
238 provider_index,
239 } => value < count_fn(solution, provider_index),
240 ValueSource::EntitySlice { values_for_entity } => {
241 values_for_entity(solution, entity_index, self.variable_index).contains(&value)
242 }
243 }
244 }
245
246 pub fn nearby_value_distance(
247 &self,
248 solution: &S,
249 entity_index: usize,
250 value: usize,
251 ) -> Option<f64> {
252 self.nearby_value_distance_meter
253 .and_then(|meter| meter(solution, entity_index, self.variable_index, value))
254 }
255
256 pub fn nearby_entity_distance(
257 &self,
258 solution: &S,
259 left_entity_index: usize,
260 right_entity_index: usize,
261 ) -> Option<f64> {
262 self.nearby_entity_distance_meter.and_then(|meter| {
263 meter(
264 solution,
265 left_entity_index,
266 right_entity_index,
267 self.variable_index,
268 )
269 })
270 }
271
272 pub fn construction_entity_order_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
273 self.construction_entity_order_key
274 .and_then(|order_key| order_key(solution, entity_index, self.variable_index))
275 }
276
277 pub fn construction_value_order_key(
278 &self,
279 solution: &S,
280 entity_index: usize,
281 value: usize,
282 ) -> Option<i64> {
283 self.construction_value_order_key
284 .and_then(|order_key| order_key(solution, entity_index, self.variable_index, value))
285 }
286
287 pub fn search_entity_order_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
288 self.search_entity_order_key
289 .and_then(|order_key| order_key(solution, entity_index, self.variable_index))
290 }
291
292 pub fn search_value_order_key(
293 &self,
294 solution: &S,
295 entity_index: usize,
296 value: usize,
297 ) -> Option<i64> {
298 self.search_value_order_key
299 .and_then(|order_key| order_key(solution, entity_index, self.variable_index, value))
300 }
301}
302
303impl<S> fmt::Debug for ScalarVariableSlot<S> {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 f.debug_struct("ScalarVariableSlot")
306 .field("descriptor_index", &self.descriptor_index)
307 .field("variable_index", &self.variable_index)
308 .field("entity_type_name", &self.entity_type_name)
309 .field("variable_name", &self.variable_name)
310 .field("value_source", &self.value_source)
311 .field("allows_unassigned", &self.allows_unassigned)
312 .field("has_candidate_values", &self.candidate_values.is_some())
313 .field(
314 "has_nearby_value_candidates",
315 &self.nearby_value_candidates.is_some(),
316 )
317 .field(
318 "has_nearby_entity_candidates",
319 &self.nearby_entity_candidates.is_some(),
320 )
321 .field(
322 "has_nearby_value_distance_meter",
323 &self.nearby_value_distance_meter.is_some(),
324 )
325 .field(
326 "has_nearby_entity_distance_meter",
327 &self.nearby_entity_distance_meter.is_some(),
328 )
329 .field(
330 "has_construction_entity_order_key",
331 &self.construction_entity_order_key.is_some(),
332 )
333 .field(
334 "has_construction_value_order_key",
335 &self.construction_value_order_key.is_some(),
336 )
337 .field(
338 "has_search_entity_order_key",
339 &self.search_entity_order_key.is_some(),
340 )
341 .field(
342 "has_search_value_order_key",
343 &self.search_value_order_key.is_some(),
344 )
345 .finish()
346 }
347}