Skip to main content

solverforge_solver/builder/context/scalar/
group.rs

1use std::fmt;
2
3use crate::planning::{ScalarCandidateProvider, ScalarEdit, ScalarGroup};
4
5use super::value_source::ValueSource;
6use super::variable::{ScalarGetter, ScalarSetter, ScalarVariableSlot};
7
8pub struct ScalarGroupMemberBinding<S> {
9    pub descriptor_index: usize,
10    pub variable_index: usize,
11    pub entity_type_name: &'static str,
12    pub variable_name: &'static str,
13    pub getter: ScalarGetter<S>,
14    pub setter: ScalarSetter<S>,
15    pub value_source: ValueSource<S>,
16    pub allows_unassigned: bool,
17}
18
19impl<S> Clone for ScalarGroupMemberBinding<S> {
20    fn clone(&self) -> Self {
21        *self
22    }
23}
24
25impl<S> Copy for ScalarGroupMemberBinding<S> {}
26
27impl<S> ScalarGroupMemberBinding<S> {
28    pub fn from_scalar_slot(slot: ScalarVariableSlot<S>) -> Self {
29        Self {
30            descriptor_index: slot.descriptor_index,
31            variable_index: slot.variable_index,
32            entity_type_name: slot.entity_type_name,
33            variable_name: slot.variable_name,
34            getter: slot.getter,
35            setter: slot.setter,
36            value_source: slot.value_source,
37            allows_unassigned: slot.allows_unassigned,
38        }
39    }
40
41    pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
42        (self.getter)(solution, entity_index, self.variable_index)
43    }
44
45    pub fn value_is_legal(
46        &self,
47        solution: &S,
48        entity_index: usize,
49        candidate: Option<usize>,
50    ) -> bool {
51        let Some(value) = candidate else {
52            return self.allows_unassigned;
53        };
54        match self.value_source {
55            ValueSource::Empty => false,
56            ValueSource::CountableRange { from, to } => from <= value && value < to,
57            ValueSource::SolutionCount {
58                count_fn,
59                provider_index,
60            } => value < count_fn(solution, provider_index),
61            ValueSource::EntitySlice { values_for_entity } => {
62                values_for_entity(solution, entity_index, self.variable_index).contains(&value)
63            }
64        }
65    }
66}
67
68impl<S> fmt::Debug for ScalarGroupMemberBinding<S> {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_struct("ScalarGroupMemberBinding")
71            .field("descriptor_index", &self.descriptor_index)
72            .field("variable_index", &self.variable_index)
73            .field("entity_type_name", &self.entity_type_name)
74            .field("variable_name", &self.variable_name)
75            .field("value_source", &self.value_source)
76            .field("allows_unassigned", &self.allows_unassigned)
77            .finish()
78    }
79}
80
81pub struct ScalarGroupBinding<S> {
82    pub group_name: &'static str,
83    pub members: Vec<ScalarGroupMemberBinding<S>>,
84    pub candidate_provider: ScalarCandidateProvider<S>,
85}
86
87impl<S> ScalarGroupBinding<S> {
88    pub fn bind(group: ScalarGroup<S>, scalar_slots: &[ScalarVariableSlot<S>]) -> Self {
89        let members = group
90            .targets()
91            .iter()
92            .map(|target| {
93                let descriptor_index = target.descriptor_index();
94                let variable_name = target.variable_name();
95                let slot = scalar_slots
96                    .iter()
97                    .copied()
98                    .find(|slot| {
99                        slot.descriptor_index == descriptor_index
100                            && slot.variable_name == variable_name
101                    })
102                    .unwrap_or_else(|| {
103                        panic!(
104                            "scalar group `{}` targets unknown scalar variable `{}` on descriptor {}",
105                            group.group_name(),
106                            variable_name,
107                            descriptor_index
108                        )
109                    });
110                ScalarGroupMemberBinding::from_scalar_slot(slot)
111            })
112            .collect();
113
114        Self {
115            group_name: group.group_name(),
116            members,
117            candidate_provider: group.candidate_provider(),
118        }
119    }
120
121    pub fn member_for_edit(&self, edit: &ScalarEdit<S>) -> Option<ScalarGroupMemberBinding<S>> {
122        self.members.iter().copied().find(|member| {
123            member.descriptor_index == edit.descriptor_index()
124                && member.variable_name == edit.variable_name()
125        })
126    }
127}
128
129impl<S> Clone for ScalarGroupBinding<S> {
130    fn clone(&self) -> Self {
131        Self {
132            group_name: self.group_name,
133            members: self.members.clone(),
134            candidate_provider: self.candidate_provider,
135        }
136    }
137}
138
139impl<S> fmt::Debug for ScalarGroupBinding<S> {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        f.debug_struct("ScalarGroupBinding")
142            .field("group_name", &self.group_name)
143            .field("members", &self.members)
144            .finish_non_exhaustive()
145    }
146}
147
148pub fn bind_scalar_groups<S>(
149    groups: Vec<ScalarGroup<S>>,
150    scalar_slots: &[ScalarVariableSlot<S>],
151) -> Vec<ScalarGroupBinding<S>> {
152    groups
153        .into_iter()
154        .map(|group| ScalarGroupBinding::bind(group, scalar_slots))
155        .collect()
156}