1use super::*;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub(crate) enum SelectionForm {
7 OneBased,
8 Offset,
9 Matrix,
10 SetMatrix,
11 Conditional,
12}
13
14#[derive(Clone, Copy, Debug)]
15pub(crate) struct SelectionName {
16 pub kind: ValueType,
17 pub form: SelectionForm,
18 pub allows_variables: bool,
19}
20
21impl SelectionName {
22 pub fn from_name(name: &str) -> Option<Self> {
23 Some(match name {
24 "array_bool_element" | "gecode_array_bool_element" | "fzn_array_bool_element" => Self {
25 kind: ValueType::Boolean,
26 form: SelectionForm::OneBased,
27 allows_variables: false,
28 },
29 "array_var_bool_element"
30 | "gecode_array_var_bool_element"
31 | "fzn_array_var_bool_element" => Self {
32 kind: ValueType::Boolean,
33 form: SelectionForm::OneBased,
34 allows_variables: true,
35 },
36 "array_var_bool_element_nonshifted" => Self {
37 kind: ValueType::Boolean,
38 form: SelectionForm::OneBased,
39 allows_variables: true,
40 },
41 "array_int_element" | "gecode_array_int_element" | "fzn_array_int_element" => Self {
42 kind: ValueType::Integer,
43 form: SelectionForm::OneBased,
44 allows_variables: false,
45 },
46 "array_var_int_element"
47 | "gecode_array_var_int_element"
48 | "fzn_array_var_int_element" => Self {
49 kind: ValueType::Integer,
50 form: SelectionForm::OneBased,
51 allows_variables: true,
52 },
53 "array_var_int_element_nonshifted" => Self {
54 kind: ValueType::Integer,
55 form: SelectionForm::OneBased,
56 allows_variables: true,
57 },
58 "array_set_element" | "gecode_array_set_element" | "fzn_array_set_element" => Self {
59 kind: ValueType::IntegerSet,
60 form: SelectionForm::OneBased,
61 allows_variables: false,
62 },
63 "array_var_set_element"
64 | "gecode_array_var_set_element"
65 | "fzn_array_var_set_element" => Self {
66 kind: ValueType::IntegerSet,
67 form: SelectionForm::OneBased,
68 allows_variables: true,
69 },
70 "array_var_set_element_nonshifted" => Self {
71 kind: ValueType::IntegerSet,
72 form: SelectionForm::OneBased,
73 allows_variables: true,
74 },
75 "array_float_element" => Self {
76 kind: ValueType::Float,
77 form: SelectionForm::OneBased,
78 allows_variables: false,
79 },
80 "array_var_float_element" => Self {
81 kind: ValueType::Float,
82 form: SelectionForm::OneBased,
83 allows_variables: true,
84 },
85 "array_var_float_element_nonshifted" => Self {
86 kind: ValueType::Float,
87 form: SelectionForm::OneBased,
88 allows_variables: true,
89 },
90 "gecode_int_element" | "gecode_gecode_int_element" | "fzn_gecode_int_element" => Self {
91 kind: ValueType::Integer,
92 form: SelectionForm::Offset,
93 allows_variables: true,
94 },
95 "gecode_var_int_element"
96 | "gecode_gecode_var_int_element"
97 | "fzn_gecode_var_int_element" => Self {
98 kind: ValueType::Integer,
99 form: SelectionForm::Offset,
100 allows_variables: true,
101 },
102 "gecode_int_element2d" | "gecode_gecode_int_element2d" | "fzn_gecode_int_element2d" => {
103 Self {
104 kind: ValueType::Integer,
105 form: SelectionForm::Matrix,
106 allows_variables: true,
107 }
108 }
109 "gecode_bool_element" | "gecode_gecode_bool_element" | "fzn_gecode_bool_element" => {
110 Self {
111 kind: ValueType::Boolean,
112 form: SelectionForm::Offset,
113 allows_variables: true,
114 }
115 }
116 "gecode_var_bool_element"
117 | "gecode_gecode_var_bool_element"
118 | "fzn_gecode_var_bool_element" => Self {
119 kind: ValueType::Boolean,
120 form: SelectionForm::Offset,
121 allows_variables: true,
122 },
123 "gecode_bool_element2d"
124 | "gecode_gecode_bool_element2d"
125 | "fzn_gecode_bool_element2d" => Self {
126 kind: ValueType::Boolean,
127 form: SelectionForm::Matrix,
128 allows_variables: true,
129 },
130 "gecode_set_element2d" | "gecode_gecode_set_element2d" | "fzn_gecode_set_element2d" => {
131 Self {
132 kind: ValueType::IntegerSet,
133 form: SelectionForm::SetMatrix,
134 allows_variables: true,
135 }
136 }
137 "gecode_bool_ite" | "gecode_gecode_bool_ite" | "fzn_gecode_bool_ite" => Self {
138 kind: ValueType::Boolean,
139 form: SelectionForm::Conditional,
140 allows_variables: true,
141 },
142 "gecode_int_ite" | "gecode_gecode_int_ite" | "fzn_gecode_int_ite" => Self {
143 kind: ValueType::Integer,
144 form: SelectionForm::Conditional,
145 allows_variables: true,
146 },
147 "gecode_set_ite" | "gecode_gecode_set_ite" | "fzn_gecode_set_ite" => Self {
148 kind: ValueType::IntegerSet,
149 form: SelectionForm::Conditional,
150 allows_variables: true,
151 },
152 "gecode_float_ite" | "gecode_gecode_float_ite" | "fzn_gecode_float_ite" => Self {
153 kind: ValueType::Float,
154 form: SelectionForm::Conditional,
155 allows_variables: true,
156 },
157 _ => return None,
158 })
159 }
160
161 #[cfg(feature = "prepare")]
162 pub fn arity(self) -> usize {
163 match self.form {
164 SelectionForm::OneBased => 3,
165 SelectionForm::Offset | SelectionForm::Conditional => 4,
166 SelectionForm::Matrix | SelectionForm::SetMatrix => 6,
167 }
168 }
169
170 #[cfg(feature = "prepare")]
171 pub fn signature(self) -> Vec<prepare::ArgumentType> {
172 let scalar = |kind, is_var| prepare::ArgumentType {
173 kind,
174 is_var,
175 is_array: false,
176 };
177 let array = prepare::ArgumentType {
178 kind: self.kind,
179 is_var: self.allows_variables,
180 is_array: true,
181 };
182 let index = scalar(ValueType::Integer, true);
183 let fixed_int = scalar(ValueType::Integer, false);
184 let result = scalar(self.kind, true);
185 match self.form {
186 SelectionForm::OneBased => vec![index, array, result],
187 SelectionForm::Offset => vec![index, fixed_int, array, result],
188 SelectionForm::Matrix => vec![
189 index.clone(),
190 index,
191 array,
192 scalar(ValueType::IntegerSet, false),
193 scalar(ValueType::IntegerSet, false),
194 result,
195 ],
196 SelectionForm::SetMatrix => vec![
197 array,
198 index.clone(),
199 fixed_int.clone(),
200 index,
201 fixed_int,
202 result,
203 ],
204 SelectionForm::Conditional => {
205 vec![
206 scalar(ValueType::Boolean, true),
207 result.clone(),
208 result.clone(),
209 result,
210 ]
211 }
212 }
213 }
214}
215
216#[derive(Clone, Debug, Serialize, Deserialize)]
218#[serde(tag = "kind", rename_all = "snake_case")]
219pub enum ElementLayout {
220 OneBased,
221 Offset {
222 #[serde(
223 serialize_with = "serialize_integer",
224 deserialize_with = "deserialize_integer"
225 )]
226 offset: i64,
227 },
228 Matrix {
229 rows: IntegerSet,
230 columns: IntegerSet,
231 },
232 SetMatrix {
233 #[serde(
234 serialize_with = "serialize_integer",
235 deserialize_with = "deserialize_integer"
236 )]
237 width: i64,
238 #[serde(
239 serialize_with = "serialize_integer",
240 deserialize_with = "deserialize_integer"
241 )]
242 height: i64,
243 },
244}
245
246impl std::fmt::Display for ElementLayout {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 match self {
249 Self::OneBased => f.write_str("one-based positions"),
250 Self::Offset { offset } => write!(f, "offset {offset}"),
251 Self::Matrix { rows, columns } => write!(f, "rows {rows}, columns {columns}"),
252 Self::SetMatrix { width, height } => {
253 write!(f, "zero-based matrix with width {width}, height {height}")
254 }
255 }
256 }
257}
258
259pub(crate) enum SelectionPosition {
260 Missing,
261 OutOfRange,
262 Selected(usize),
263}
264
265impl ElementLayout {
266 pub(crate) fn form(&self) -> SelectionForm {
267 match self {
268 Self::OneBased => SelectionForm::OneBased,
269 Self::Offset { .. } => SelectionForm::Offset,
270 Self::Matrix { .. } => SelectionForm::Matrix,
271 Self::SetMatrix { .. } => SelectionForm::SetMatrix,
272 }
273 }
274
275 pub(crate) fn index_count(&self) -> usize {
276 match self {
277 Self::OneBased | Self::Offset { .. } => 1,
278 Self::Matrix { .. } | Self::SetMatrix { .. } => 2,
279 }
280 }
281
282 pub(crate) fn validate(&self, length: usize) -> Result<(), &'static str> {
283 let dimensions = match self {
284 Self::OneBased | Self::Offset { .. } => return Ok(()),
285 Self::Matrix { rows, columns } => {
286 let bounds = |set: &IntegerSet| {
287 let (lower, upper) = set
288 .contiguous_bounds()
289 .ok_or("element index sets must be nonempty and contiguous")?;
290 usize::try_from(i128::from(upper) - i128::from(lower) + 1)
291 .map_err(|_| "element dimensions exceed this target's addressable size")
292 };
293 (bounds(rows)?, bounds(columns)?)
294 }
295 Self::SetMatrix { width, height } => {
296 if *width <= 0 || *height <= 0 {
297 return Err("element width and height must be positive");
298 }
299 let size = |value| {
300 usize::try_from(value)
301 .map_err(|_| "element dimensions exceed this target's addressable size")
302 };
303 (size(*height)?, size(*width)?)
304 }
305 };
306 if dimensions.0.checked_mul(dimensions.1) != Some(length) {
307 return Err("element matrix dimensions must match the flat array length");
308 }
309 Ok(())
310 }
311
312 pub(crate) fn position(
313 &self,
314 indices: &[Operand],
315 length: usize,
316 values: &[Option<Value>],
317 ) -> SelectionPosition {
318 let axes = match self {
319 Self::OneBased => vec![(1, length as i128)],
320 Self::Offset { offset } => vec![(i128::from(*offset), length as i128)],
321 Self::Matrix { rows, columns } => [rows, columns]
322 .map(|set| {
323 let (lower, upper) =
324 set.contiguous_bounds().expect("admitted matrix index sets");
325 (i128::from(lower), i128::from(upper) - i128::from(lower) + 1)
326 })
327 .to_vec(),
328 Self::SetMatrix { width, height } => {
329 vec![(0, i128::from(*height)), (0, i128::from(*width))]
330 }
331 };
332 let mut has_missing_index = false;
333 let mut position = 0;
334 for (index, (lower, size)) in indices.iter().zip(axes) {
335 let Some(value) = index.value(values) else {
336 has_missing_index = true;
337 continue;
338 };
339 let relative = i128::from(value.integer()) - lower;
340 if relative < 0 || relative >= size {
341 return SelectionPosition::OutOfRange;
342 }
343 position = position * size as usize + relative as usize;
345 }
346 if has_missing_index {
347 SelectionPosition::Missing
348 } else {
349 SelectionPosition::Selected(position)
350 }
351 }
352}
353
354impl Predicate {
355 pub(crate) fn selected_operand<'a>(&'a self, values: &[Option<Value>]) -> Option<&'a Operand> {
356 match self {
357 Self::Element {
358 indices,
359 elements,
360 layout,
361 ..
362 } => match layout.position(indices, elements.len(), values) {
363 SelectionPosition::Selected(position) => Some(&elements[position]),
364 _ => None,
365 },
366 Self::Conditional {
367 condition,
368 then_value,
369 else_value,
370 ..
371 } => Some(if condition.value(values)?.boolean() {
372 then_value
373 } else {
374 else_value
375 }),
376 _ => None,
377 }
378 }
379}