Skip to main content

solverforge_solver/builder/context/scalar/group/
assignment.rs

1use 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
43/// The semantic metadata for one assignment-backed scalar group.
44///
45/// The access representation is private so grouped construction and search can
46/// never distinguish typed hooks from a dynamic binding implementation.
47pub 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}