solverforge_solver/builder/context/scalar/group/
assignment.rs1use std::sync::Arc;
2
3use solverforge_core::domain::{
4 DynamicScalarAssignmentMetadata, DynamicScalarAssignmentMetadataCapabilities,
5};
6
7use crate::planning::{ScalarAssignmentDeclaration, ScalarAssignmentRule, ScalarEdit};
8
9use super::member::ScalarGroupMemberBinding;
10
11struct StaticAssignmentMetadata<S> {
12 required_entity: Option<fn(&S, usize) -> bool>,
13 capacity_key: Option<fn(&S, usize, usize) -> Option<usize>>,
14 position_key: Option<fn(&S, usize) -> i64>,
15 sequence_key: Option<fn(&S, usize, usize) -> Option<usize>>,
16 entity_order: Option<fn(&S, usize) -> i64>,
17 value_order: Option<fn(&S, usize, usize) -> i64>,
18 assignment_rule: Option<ScalarAssignmentRule<S>>,
19}
20
21impl<S> Copy for StaticAssignmentMetadata<S> {}
22
23impl<S> Clone for StaticAssignmentMetadata<S> {
24 fn clone(&self) -> Self {
25 *self
26 }
27}
28
29enum AssignmentMetadataAccess<S> {
30 Static(StaticAssignmentMetadata<S>),
31 Dynamic(Arc<dyn DynamicScalarAssignmentMetadata<S>>),
32}
33
34impl<S> Clone for AssignmentMetadataAccess<S> {
35 fn clone(&self) -> Self {
36 match self {
37 Self::Static(metadata) => Self::Static(*metadata),
38 Self::Dynamic(metadata) => Self::Dynamic(Arc::clone(metadata)),
39 }
40 }
41}
42
43pub struct ScalarAssignmentBinding<S> {
48 pub(crate) target: ScalarGroupMemberBinding<S>,
49 metadata: AssignmentMetadataAccess<S>,
50}
51
52impl<S> Clone for ScalarAssignmentBinding<S> {
53 fn clone(&self) -> Self {
54 Self {
55 target: self.target.clone(),
56 metadata: self.metadata.clone(),
57 }
58 }
59}
60
61impl<S> ScalarAssignmentBinding<S> {
62 pub(super) fn bind(
63 group_name: &'static str,
64 members: &[ScalarGroupMemberBinding<S>],
65 declaration: ScalarAssignmentDeclaration<S>,
66 ) -> Self {
67 assert_eq!(
68 members.len(),
69 1,
70 "assignment scalar group `{group_name}` must target exactly one scalar planning variable",
71 );
72 let target = members[0].clone();
73 assert!(
74 target.allows_unassigned,
75 "assignment scalar group `{group_name}` target {}.{} must allow unassigned values",
76 target.entity_type_name, target.variable_name,
77 );
78 assert!(
79 declaration.assignment_rule.is_none() || declaration.sequence_key.is_some(),
80 "assignment scalar group `{group_name}` with an assignment rule must declare a sequence key",
81 );
82 Self {
83 target,
84 metadata: AssignmentMetadataAccess::Static(StaticAssignmentMetadata {
85 required_entity: declaration.required_entity,
86 capacity_key: declaration.capacity_key,
87 position_key: declaration.position_key,
88 sequence_key: declaration.sequence_key,
89 entity_order: declaration.entity_order,
90 value_order: declaration.value_order,
91 assignment_rule: declaration.assignment_rule,
92 }),
93 }
94 }
95
96 pub(super) fn dynamic(
97 group_name: &'static str,
98 target: ScalarGroupMemberBinding<S>,
99 metadata: Arc<dyn DynamicScalarAssignmentMetadata<S>>,
100 ) -> Self {
101 assert!(
102 target.allows_unassigned,
103 "assignment scalar group `{group_name}` target {}.{} must allow unassigned values",
104 target.entity_type_name, target.variable_name,
105 );
106 let capabilities = metadata.capabilities();
107 assert!(
108 !capabilities.assignment_rule || capabilities.sequence_key,
109 "assignment scalar group `{group_name}` with an assignment rule must declare a sequence key",
110 );
111 Self {
112 target,
113 metadata: AssignmentMetadataAccess::Dynamic(metadata),
114 }
115 }
116
117 pub(crate) fn target(&self) -> &ScalarGroupMemberBinding<S> {
118 &self.target
119 }
120
121 pub(crate) fn target_mut(&mut self) -> &mut ScalarGroupMemberBinding<S> {
122 &mut self.target
123 }
124
125 pub(crate) fn capabilities(&self) -> DynamicScalarAssignmentMetadataCapabilities {
126 match &self.metadata {
127 AssignmentMetadataAccess::Static(metadata) => {
128 DynamicScalarAssignmentMetadataCapabilities {
129 required_entity: metadata.required_entity.is_some(),
130 capacity_key: metadata.capacity_key.is_some(),
131 position_key: metadata.position_key.is_some(),
132 sequence_key: metadata.sequence_key.is_some(),
133 entity_order: metadata.entity_order.is_some(),
134 value_order: metadata.value_order.is_some(),
135 assignment_rule: metadata.assignment_rule.is_some(),
136 }
137 }
138 AssignmentMetadataAccess::Dynamic(metadata) => metadata.capabilities(),
139 }
140 }
141
142 pub(crate) fn has_entity_order(&self) -> bool {
143 self.capabilities().entity_order
144 }
145
146 pub(crate) fn has_value_order(&self) -> bool {
147 self.capabilities().value_order
148 }
149
150 pub(crate) fn has_sequence_metadata(&self) -> bool {
151 self.capabilities().sequence_key
152 }
153
154 pub(crate) fn has_position_metadata(&self) -> bool {
155 self.capabilities().position_key
156 }
157
158 pub(crate) fn has_assignment_rule(&self) -> bool {
159 self.capabilities().assignment_rule
160 }
161}
162
163impl<S> ScalarAssignmentBinding<S> {
164 pub fn entity_count(&self, solution: &S) -> usize {
165 self.target.entity_count(solution)
166 }
167
168 pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
169 self.target.current_value(solution, entity_index)
170 }
171
172 pub fn is_required(&self, solution: &S, entity_index: usize) -> bool {
173 match &self.metadata {
174 AssignmentMetadataAccess::Static(metadata) => metadata
175 .required_entity
176 .map(|required_entity| required_entity(solution, entity_index))
177 .unwrap_or(false),
178 AssignmentMetadataAccess::Dynamic(metadata) => {
179 metadata.required_entity(solution, entity_index)
180 }
181 }
182 }
183
184 pub fn capacity_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<usize> {
185 match &self.metadata {
186 AssignmentMetadataAccess::Static(metadata) => metadata
187 .capacity_key
188 .and_then(|capacity_key| capacity_key(solution, entity_index, value)),
189 AssignmentMetadataAccess::Dynamic(metadata) => {
190 metadata.capacity_key(solution, entity_index, value)
191 }
192 }
193 }
194
195 pub fn position_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
196 match &self.metadata {
197 AssignmentMetadataAccess::Static(metadata) => metadata
198 .position_key
199 .map(|position_key| position_key(solution, entity_index)),
200 AssignmentMetadataAccess::Dynamic(metadata) => {
201 metadata.position_key(solution, entity_index)
202 }
203 }
204 }
205
206 pub fn sequence_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<usize> {
207 match &self.metadata {
208 AssignmentMetadataAccess::Static(metadata) => metadata
209 .sequence_key
210 .and_then(|sequence_key| sequence_key(solution, entity_index, value)),
211 AssignmentMetadataAccess::Dynamic(metadata) => {
212 metadata.sequence_key(solution, entity_index, value)
213 }
214 }
215 }
216
217 pub fn entity_order_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
218 match &self.metadata {
219 AssignmentMetadataAccess::Static(metadata) => metadata
220 .entity_order
221 .map(|entity_order| entity_order(solution, entity_index)),
222 AssignmentMetadataAccess::Dynamic(metadata) => {
223 metadata.entity_order_key(solution, entity_index)
224 }
225 }
226 }
227
228 pub fn value_order_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<i64> {
229 match &self.metadata {
230 AssignmentMetadataAccess::Static(metadata) => metadata
231 .value_order
232 .map(|value_order| value_order(solution, entity_index, value)),
233 AssignmentMetadataAccess::Dynamic(metadata) => {
234 metadata.value_order_key(solution, entity_index, value)
235 }
236 }
237 }
238
239 pub fn assignment_edge_allowed(
240 &self,
241 solution: &S,
242 left_entity: usize,
243 left_value: usize,
244 right_entity: usize,
245 right_value: usize,
246 ) -> bool {
247 match &self.metadata {
248 AssignmentMetadataAccess::Static(metadata) => metadata
249 .assignment_rule
250 .map(|assignment_rule| {
251 assignment_rule(solution, left_entity, left_value, right_entity, right_value)
252 })
253 .unwrap_or(true),
254 AssignmentMetadataAccess::Dynamic(metadata) => metadata.assignment_edge_allowed(
255 solution,
256 left_entity,
257 left_value,
258 right_entity,
259 right_value,
260 ),
261 }
262 }
263
264 pub fn candidate_values(
265 &self,
266 solution: &S,
267 entity_index: usize,
268 value_candidate_limit: Option<usize>,
269 ) -> Vec<usize> {
270 let mut values =
271 self.target
272 .candidate_values(solution, entity_index, value_candidate_limit);
273 values.sort_by_key(|value| (self.value_order_key(solution, entity_index, *value), *value));
274 values
275 }
276
277 pub fn value_is_legal(&self, solution: &S, entity_index: usize, value: Option<usize>) -> bool {
278 self.target.value_is_legal(solution, entity_index, value)
279 }
280
281 pub fn edit(&self, entity_index: usize, value: Option<usize>) -> ScalarEdit<S> {
282 ScalarEdit::from_descriptor_index(
283 self.target.descriptor_index,
284 entity_index,
285 self.target.variable_name,
286 value,
287 )
288 }
289
290 pub fn remaining_required_count(&self, solution: &S) -> u64 {
291 (0..self.entity_count(solution))
292 .filter(|entity_index| {
293 self.is_required(solution, *entity_index)
294 && self.current_value(solution, *entity_index).is_none()
295 })
296 .fold(0_u64, |count, _| count.saturating_add(1))
297 }
298
299 pub fn unassigned_count(&self, solution: &S) -> u64 {
300 (0..self.entity_count(solution))
301 .filter(|entity_index| self.current_value(solution, *entity_index).is_none())
302 .fold(0_u64, |count, _| count.saturating_add(1))
303 }
304}