solverforge_solver/builder/context/scalar/group/
member.rs1use std::fmt;
2
3use solverforge_core::domain::{DynamicScalarVariableSlot, EntityClassId, VariableId};
4
5use crate::heuristic::r#move::CompoundScalarEdit;
6
7use super::super::variable::ScalarVariableSlot;
8
9enum ScalarGroupMemberAccess<S> {
10 Static(ScalarVariableSlot<S>),
11 Dynamic(DynamicScalarVariableSlot<S>),
12}
13
14impl<S> Clone for ScalarGroupMemberAccess<S> {
15 fn clone(&self) -> Self {
16 match self {
17 Self::Static(slot) => Self::Static(*slot),
18 Self::Dynamic(slot) => Self::Dynamic(slot.clone()),
19 }
20 }
21}
22
23pub struct ScalarGroupMemberBinding<S> {
29 pub descriptor_index: usize,
30 pub variable_index: usize,
31 pub entity_type_name: &'static str,
32 pub variable_name: &'static str,
33 pub allows_unassigned: bool,
34 construction_binding_index: Option<usize>,
35 access: ScalarGroupMemberAccess<S>,
36}
37
38impl<S> Clone for ScalarGroupMemberBinding<S> {
39 fn clone(&self) -> Self {
40 Self {
41 descriptor_index: self.descriptor_index,
42 variable_index: self.variable_index,
43 entity_type_name: self.entity_type_name,
44 variable_name: self.variable_name,
45 allows_unassigned: self.allows_unassigned,
46 construction_binding_index: self.construction_binding_index,
47 access: self.access.clone(),
48 }
49 }
50}
51
52impl<S> ScalarGroupMemberBinding<S> {
53 pub fn from_scalar_slot(slot: ScalarVariableSlot<S>) -> Self {
54 Self {
55 descriptor_index: slot.descriptor_index,
56 variable_index: slot.variable_index,
57 entity_type_name: slot.entity_type_name,
58 variable_name: slot.variable_name,
59 allows_unassigned: slot.allows_unassigned,
60 construction_binding_index: None,
61 access: ScalarGroupMemberAccess::Static(slot),
62 }
63 }
64
65 pub fn from_dynamic_slot(slot: DynamicScalarVariableSlot<S>) -> Self {
66 Self {
67 descriptor_index: usize::MAX,
71 variable_index: usize::MAX,
72 entity_type_name: slot.entity_type_name,
73 variable_name: slot.variable_name,
74 allows_unassigned: slot.allows_unassigned,
75 construction_binding_index: None,
76 access: ScalarGroupMemberAccess::Dynamic(slot),
77 }
78 }
79
80 pub(crate) fn is_dynamic(&self) -> bool {
81 matches!(self.access, ScalarGroupMemberAccess::Dynamic(_))
82 }
83
84 pub(crate) fn dynamic_identity(&self) -> Option<(EntityClassId, VariableId)> {
85 match &self.access {
86 ScalarGroupMemberAccess::Static(_) => None,
87 ScalarGroupMemberAccess::Dynamic(slot) => Some((slot.entity, slot.variable)),
88 }
89 }
90
91 pub(crate) fn canonicalize_dynamic_slot(
92 &mut self,
93 slot: DynamicScalarVariableSlot<S>,
94 ) -> Result<(), String> {
95 let Some((entity, variable)) = self.dynamic_identity() else {
96 return Ok(());
97 };
98 if slot.entity != entity || slot.variable != variable {
99 return Err(format!(
100 "dynamic scalar group target {}.{} does not match the registered dynamic scalar slot {}.{}",
101 self.entity_type_name,
102 self.variable_name,
103 slot.entity_type_name,
104 slot.variable_name
105 ));
106 }
107 self.descriptor_index = slot.descriptor_index();
112 self.variable_index = slot.descriptor_variable_index();
113 self.entity_type_name = slot.entity_type_name;
114 self.variable_name = slot.variable_name;
115 self.allows_unassigned = slot.allows_unassigned;
116 self.access = ScalarGroupMemberAccess::Dynamic(slot);
117 Ok(())
118 }
119
120 pub(crate) fn set_construction_binding_index(&mut self, binding_index: usize) {
121 self.construction_binding_index = Some(binding_index);
122 }
123
124 pub(crate) fn construction_binding_index(&self) -> usize {
125 self.construction_binding_index.unwrap_or_else(|| {
126 panic!(
127 "scalar group member {}.{} was not bound to a construction slot",
128 self.entity_type_name, self.variable_name
129 )
130 })
131 }
132}
133
134impl<S> ScalarGroupMemberBinding<S> {
135 pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
136 match &self.access {
137 ScalarGroupMemberAccess::Static(slot) => slot.current_value(solution, entity_index),
138 ScalarGroupMemberAccess::Dynamic(slot) => slot.current_value(solution, entity_index),
139 }
140 }
141
142 pub fn set_value(&self, solution: &mut S, entity_index: usize, value: Option<usize>) {
143 match &self.access {
144 ScalarGroupMemberAccess::Static(slot) => slot.set_value(solution, entity_index, value),
145 ScalarGroupMemberAccess::Dynamic(slot) => slot.set_value(solution, entity_index, value),
146 }
147 }
148
149 pub fn value_is_legal(
150 &self,
151 solution: &S,
152 entity_index: usize,
153 candidate: Option<usize>,
154 ) -> bool {
155 match &self.access {
156 ScalarGroupMemberAccess::Static(slot) => {
157 slot.value_is_legal(solution, entity_index, candidate)
158 }
159 ScalarGroupMemberAccess::Dynamic(slot) => {
160 slot.value_is_legal(solution, entity_index, candidate)
161 }
162 }
163 }
164
165 pub fn entity_count(&self, solution: &S) -> usize {
166 match &self.access {
167 ScalarGroupMemberAccess::Static(slot) => (slot.entity_count)(solution),
168 ScalarGroupMemberAccess::Dynamic(slot) => slot.entity_count(solution),
169 }
170 }
171
172 pub fn candidate_values(
173 &self,
174 solution: &S,
175 entity_index: usize,
176 value_candidate_limit: Option<usize>,
177 ) -> Vec<usize> {
178 match &self.access {
179 ScalarGroupMemberAccess::Static(slot) => {
180 slot.candidate_values_for_entity(solution, entity_index, value_candidate_limit)
181 }
182 ScalarGroupMemberAccess::Dynamic(slot) => {
183 let values = slot.candidate_values(solution, entity_index);
184 match value_candidate_limit {
185 Some(limit) => values.iter().copied().take(limit).collect(),
186 None => values.to_vec(),
187 }
188 }
189 }
190 }
191
192 pub(crate) fn compound_edit(
193 &self,
194 entity_index: usize,
195 to_value: Option<usize>,
196 ) -> CompoundScalarEdit<S> {
197 match &self.access {
198 ScalarGroupMemberAccess::Static(slot) => CompoundScalarEdit::static_edit(
199 self.descriptor_index,
200 entity_index,
201 self.variable_index,
202 self.variable_name,
203 to_value,
204 slot.getter,
205 slot.setter,
206 None,
207 ),
208 ScalarGroupMemberAccess::Dynamic(slot) => CompoundScalarEdit::dynamic_edit(
209 self.descriptor_index,
210 entity_index,
211 self.variable_index,
212 self.variable_name,
213 to_value,
214 slot.clone(),
215 ),
216 }
217 }
218}
219
220impl<S> fmt::Debug for ScalarGroupMemberBinding<S> {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 f.debug_struct("ScalarGroupMemberBinding")
223 .field("descriptor_index", &self.descriptor_index)
224 .field("variable_index", &self.variable_index)
225 .field("entity_type_name", &self.entity_type_name)
226 .field("variable_name", &self.variable_name)
227 .field("allows_unassigned", &self.allows_unassigned)
228 .field(
229 "construction_binding_index",
230 &self.construction_binding_index,
231 )
232 .field("dynamic", &self.is_dynamic())
233 .finish()
234 }
235}