Skip to main content

solverforge_solver/builder/context/scalar/
variable.rs

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}