Skip to main content

zincite_check/
set_channels.rs

1//! Selected set operations and channels with compact virtual prefixes.
2use super::*;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub(crate) enum SetChannelName {
6    Link,
7    IntSet,
8    Inverse,
9    Range,
10    Precede,
11    Sorted,
12    Selected {
13        operation: SetAggregate,
14        explicit_universe: bool,
15    },
16}
17impl SetChannelName {
18    pub fn from_name(name: &str) -> Option<Self> {
19        for base in [
20            name,
21            name.strip_prefix("gecode_").unwrap_or(""),
22            name.strip_prefix("fzn_").unwrap_or(""),
23        ] {
24            return Some(match base {
25                "gecode_link_set_to_booleans" => Self::Link,
26                "gecode_int_set_channel" => Self::IntSet,
27                "gecode_inverse_set" => Self::Inverse,
28                "gecode_range" => Self::Range,
29                "gecode_precede_set" => Self::Precede,
30                "gecode_set_channel_sorted" => Self::Sorted,
31                "gecode_array_set_element_union" => Self::Selected {
32                    operation: SetAggregate::Union,
33                    explicit_universe: false,
34                },
35                "gecode_array_set_element_intersect" => Self::Selected {
36                    operation: SetAggregate::Intersection,
37                    explicit_universe: false,
38                },
39                "gecode_array_set_element_partition" => Self::Selected {
40                    operation: SetAggregate::Partition,
41                    explicit_universe: false,
42                },
43                "gecode_array_set_element_intersect_in" => Self::Selected {
44                    operation: SetAggregate::Intersection,
45                    explicit_universe: true,
46                },
47                _ => continue,
48            });
49        }
50        None
51    }
52    #[cfg(feature = "prepare")]
53    pub fn signature(self) -> Vec<prepare::ArgumentType> {
54        use ValueType::*;
55        let scalar = |kind, is_var| prepare::ArgumentType {
56            kind,
57            is_var,
58            is_array: false,
59        };
60        let array = |kind| prepare::ArgumentType {
61            kind,
62            is_var: true,
63            is_array: true,
64        };
65        let set = scalar(IntegerSet, true);
66        let fixed = scalar(Integer, false);
67        match self {
68            Self::Link => vec![set, array(Boolean), fixed],
69            Self::IntSet => vec![array(Integer), fixed.clone(), array(IntegerSet), fixed],
70            Self::Inverse => vec![array(IntegerSet), array(IntegerSet), fixed.clone(), fixed],
71            Self::Range => vec![array(Integer), fixed, set.clone(), set],
72            Self::Precede => vec![array(IntegerSet), fixed.clone(), fixed],
73            Self::Sorted => vec![array(Integer), set],
74            Self::Selected {
75                explicit_universe, ..
76            } => {
77                let mut args = vec![set.clone(), array(IntegerSet), set];
78                if explicit_universe {
79                    args.push(scalar(IntegerSet, false));
80                }
81                args
82            }
83        }
84    }
85}
86
87#[derive(Debug, Serialize, Deserialize)]
88pub(crate) enum SetChannel {
89    Link {
90        set: Operand,
91        flags: Vec<Operand>,
92        #[serde(
93            serialize_with = "serialize_integer",
94            deserialize_with = "deserialize_integer"
95        )]
96        offset: i64,
97    },
98    IntSet {
99        integers: Vec<Operand>,
100        sets: Vec<Operand>,
101        #[serde(
102            serialize_with = "serialize_integer",
103            deserialize_with = "deserialize_integer"
104        )]
105        integer_offset: i64,
106        #[serde(
107            serialize_with = "serialize_integer",
108            deserialize_with = "deserialize_integer"
109        )]
110        set_offset: i64,
111    },
112    Inverse {
113        forward: Vec<Operand>,
114        inverse: Vec<Operand>,
115        #[serde(
116            serialize_with = "serialize_integer",
117            deserialize_with = "deserialize_integer"
118        )]
119        forward_offset: i64,
120        #[serde(
121            serialize_with = "serialize_integer",
122            deserialize_with = "deserialize_integer"
123        )]
124        inverse_offset: i64,
125    },
126    Range {
127        integers: Vec<Operand>,
128        #[serde(
129            serialize_with = "serialize_integer",
130            deserialize_with = "deserialize_integer"
131        )]
132        offset: i64,
133        selection: Operand,
134        result: Operand,
135    },
136    Precede {
137        sets: Vec<Operand>,
138        #[serde(
139            serialize_with = "serialize_integer",
140            deserialize_with = "deserialize_integer"
141        )]
142        first: i64,
143        #[serde(
144            serialize_with = "serialize_integer",
145            deserialize_with = "deserialize_integer"
146        )]
147        second: i64,
148    },
149    Sorted {
150        integers: Vec<Operand>,
151        set: Operand,
152    },
153    Selected {
154        operation: SetAggregate,
155        indices: Operand,
156        sets: Vec<Operand>,
157        result: Operand,
158        universe: Option<IntegerSet>,
159    },
160}
161
162/// Visible positions are one-based; logical labels include the caller's offset.
163#[derive(Clone, Debug, Serialize)]
164pub struct SetChannelArgument {
165    pub role: SetChannelSide,
166    pub position: Option<usize>,
167    #[serde(serialize_with = "serialize_optional_integer")]
168    pub index: Option<i64>,
169    pub value: Option<ArgumentValue>,
170    pub unresolved: Option<Box<VariableRef>>,
171}
172#[derive(Clone, Copy, Debug, Serialize)]
173#[serde(rename_all = "snake_case")]
174pub enum SetChannelSide {
175    Set,
176    Flags,
177    Integers,
178    Sets,
179    Forward,
180    Inverse,
181    Selection,
182    Result,
183}
184#[derive(Clone, Debug, Serialize)]
185#[serde(tag = "kind", rename_all = "snake_case")]
186pub enum SetChannelFault {
187    Outside {
188        side: SetChannelSide,
189        position: Option<usize>,
190        members: IntegerSet,
191        allowed: IntegerSet,
192    },
193    Membership {
194        position: usize,
195        #[serde(serialize_with = "serialize_integer")]
196        index: i64,
197        set_position: usize,
198        #[serde(serialize_with = "serialize_integer")]
199        set_index: i64,
200        expected: bool,
201    },
202    MembershipCount {
203        position: usize,
204        #[serde(serialize_with = "serialize_integer")]
205        index: i64,
206        memberships: IntegerSet,
207    },
208    Padding {
209        integer_indices: IntegerSet,
210        set_indices: IntegerSet,
211    },
212    Mismatch {
213        side: SetChannelSide,
214        position: Option<usize>,
215        expected: Value,
216    },
217    Partition {
218        first: usize,
219        second: usize,
220        intersection: IntegerSet,
221    },
222    Precedence {
223        position: usize,
224    },
225    Order {
226        first: usize,
227        second: usize,
228    },
229    Cardinality {
230        #[serde(serialize_with = "serialize_integer")]
231        expected: i64,
232        #[serde(serialize_with = "serialize_integer")]
233        actual: i64,
234    },
235}
236#[derive(Clone, Debug, Serialize)]
237#[serde(tag = "operation", rename_all = "snake_case")]
238pub enum SetChannelFinding {
239    Link {
240        arguments: Vec<SetChannelArgument>,
241        #[serde(serialize_with = "serialize_integer")]
242        offset: i64,
243        faults: Vec<SetChannelFault>,
244    },
245    IntSet {
246        arguments: Vec<SetChannelArgument>,
247        #[serde(serialize_with = "serialize_integer")]
248        integer_offset: i64,
249        #[serde(serialize_with = "serialize_integer")]
250        set_offset: i64,
251        faults: Vec<SetChannelFault>,
252    },
253    Inverse {
254        arguments: Vec<SetChannelArgument>,
255        #[serde(serialize_with = "serialize_integer")]
256        forward_offset: i64,
257        #[serde(serialize_with = "serialize_integer")]
258        inverse_offset: i64,
259        faults: Vec<SetChannelFault>,
260    },
261    Range {
262        arguments: Vec<SetChannelArgument>,
263        #[serde(serialize_with = "serialize_integer")]
264        offset: i64,
265        faults: Vec<SetChannelFault>,
266    },
267    Precede {
268        arguments: Vec<SetChannelArgument>,
269        #[serde(serialize_with = "serialize_integer")]
270        first: i64,
271        #[serde(serialize_with = "serialize_integer")]
272        second: i64,
273        faults: Vec<SetChannelFault>,
274    },
275    Sorted {
276        arguments: Vec<SetChannelArgument>,
277        faults: Vec<SetChannelFault>,
278    },
279    Selected {
280        arguments: Vec<SetChannelArgument>,
281        aggregate: SetAggregate,
282        universe: Option<IntegerSet>,
283        faults: Vec<SetChannelFault>,
284    },
285}
286
287impl SetChannel {
288    pub fn name(&self) -> SetChannelName {
289        match self {
290            Self::Link { .. } => SetChannelName::Link,
291            Self::IntSet { .. } => SetChannelName::IntSet,
292            Self::Inverse { .. } => SetChannelName::Inverse,
293            Self::Range { .. } => SetChannelName::Range,
294            Self::Precede { .. } => SetChannelName::Precede,
295            Self::Sorted { .. } => SetChannelName::Sorted,
296            Self::Selected {
297                operation,
298                universe,
299                ..
300            } => SetChannelName::Selected {
301                operation: *operation,
302                explicit_universe: universe.is_some(),
303            },
304        }
305    }
306    pub fn validate_shape(&self) -> Result<(), &'static str> {
307        match self {
308            Self::Link { flags, offset, .. } => {
309                checked_length(*offset, flags.len())?;
310                if *offset - 1 > GECODE_SET_MAX {
311                    return Err("Boolean channel prefix exceeds Gecode set limits");
312                }
313            }
314            Self::IntSet {
315                integers,
316                sets,
317                integer_offset,
318                set_offset,
319            } => {
320                let n = integer_length(*integer_offset, integers.len())?;
321                let m = checked_length(*set_offset, sets.len())?;
322                if *set_offset > 0 {
323                    check_set_constant(*integer_offset - 1)?;
324                }
325                // The registry posts dom on each visible bucket/integer, even for empty intervals.
326                if !sets.is_empty() {
327                    check_set_constant(*integer_offset)?;
328                    check_set_constant(n - 1)?;
329                }
330                if !integers.is_empty() && (*set_offset > 2147483646 || m - 1 > 2147483646) {
331                    return Err("integer channel domain exceeds Gecode integer limits");
332                }
333            }
334            Self::Inverse {
335                forward,
336                inverse,
337                forward_offset,
338                inverse_offset,
339            } => {
340                checked_length(*forward_offset, forward.len())?;
341                checked_length(*inverse_offset, inverse.len())?;
342            }
343            Self::Range {
344                integers, offset, ..
345            } => {
346                integer_length(*offset, integers.len())?;
347            }
348            Self::Precede { first, second, .. } => {
349                check_set_constant(*first)?;
350                check_set_constant(*second)?;
351            }
352            Self::Selected {
353                operation,
354                sets,
355                universe,
356                ..
357            } => {
358                if !matches!(
359                    operation,
360                    SetAggregate::Union | SetAggregate::Intersection | SetAggregate::Partition
361                ) || universe.is_some() && *operation != SetAggregate::Intersection
362                {
363                    return Err("invalid selected-set operation or universe");
364                }
365                checked_length(1, sets.len())?;
366                if let Some(universe) = universe
367                    && !universe.is_subset(&IntegerSet::interval(
368                        i64::from(i32::MIN),
369                        i64::from(i32::MAX),
370                    ))
371                {
372                    return Err("fixed intersection universe exceeds native integer bounds");
373                }
374            }
375            Self::Sorted { .. } => {}
376        }
377        Ok(())
378    }
379    pub fn typed_operands(&self) -> Vec<(&Operand, ValueType)> {
380        use ValueType::*;
381        fn array(entries: &[Operand], kind: ValueType) -> Vec<(&Operand, ValueType)> {
382            entries.iter().map(|entry| (entry, kind)).collect()
383        }
384        match self {
385            Self::Link { set, flags, .. } => std::iter::once((set, IntegerSet))
386                .chain(array(flags, Boolean))
387                .collect(),
388            Self::IntSet { integers, sets, .. } => array(integers, Integer)
389                .into_iter()
390                .chain(array(sets, IntegerSet))
391                .collect(),
392            Self::Inverse {
393                forward, inverse, ..
394            } => array(forward, IntegerSet)
395                .into_iter()
396                .chain(array(inverse, IntegerSet))
397                .collect(),
398            Self::Range {
399                integers,
400                selection,
401                result,
402                ..
403            } => array(integers, Integer)
404                .into_iter()
405                .chain([(selection, IntegerSet), (result, IntegerSet)])
406                .collect(),
407            Self::Precede { sets, .. } => array(sets, IntegerSet),
408            Self::Sorted { integers, set } => array(integers, Integer)
409                .into_iter()
410                .chain([(set, IntegerSet)])
411                .collect(),
412            Self::Selected {
413                indices,
414                sets,
415                result,
416                ..
417            } => std::iter::once((indices, IntegerSet))
418                .chain(array(sets, IntegerSet))
419                .chain([(result, IntegerSet)])
420                .collect(),
421        }
422    }
423    pub fn operands(&self) -> Vec<&Operand> {
424        self.typed_operands()
425            .into_iter()
426            .map(|(operand, _)| operand)
427            .collect()
428    }
429    pub fn required_operands(&self, values: &[Option<Value>]) -> Vec<&Operand> {
430        if self
431            .faults(values)
432            .map_or(true, |faults| !faults.is_empty())
433        {
434            return Vec::new();
435        }
436        match self {
437            Self::Selected {
438                indices,
439                sets,
440                result,
441                ..
442            } => {
443                let mut required = vec![indices, result];
444                if let Some(selected) = indices.value(values) {
445                    required.extend(
446                        sets.iter()
447                            .enumerate()
448                            .filter(|(i, _)| selected.set().contains(label(1, *i)))
449                            .map(|(_, operand)| operand),
450                    );
451                }
452                required
453            }
454            Self::Range {
455                integers,
456                offset,
457                selection,
458                result,
459            } => {
460                let mut required = vec![selection, result];
461                if let Some(selected) = selection.value(values) {
462                    required.extend(
463                        integers
464                            .iter()
465                            .enumerate()
466                            .filter(|(i, _)| selected.set().contains(label(*offset, *i)))
467                            .map(|(_, operand)| operand),
468                    );
469                }
470                required
471            }
472            Self::Precede {
473                sets,
474                first,
475                second,
476            } if sets.is_empty() || first == second => Vec::new(),
477            _ => self.operands(),
478        }
479    }
480    pub fn array_outputs(&self) -> Vec<(&[Operand], ValueType)> {
481        match self {
482            Self::Link { flags, .. } => vec![(flags, ValueType::Boolean)],
483            Self::IntSet { integers, sets, .. } => vec![
484                (integers, ValueType::Integer),
485                (sets, ValueType::IntegerSet),
486            ],
487            Self::Inverse {
488                forward, inverse, ..
489            } => vec![
490                (forward, ValueType::IntegerSet),
491                (inverse, ValueType::IntegerSet),
492            ],
493            Self::Sorted { integers, .. } => vec![(integers, ValueType::Integer)],
494            _ => Vec::new(),
495        }
496    }
497    pub fn can_define(&self, output: usize) -> bool {
498        let is_output =
499            |operand: &Operand| matches!(operand, Operand::Variable(slot) if *slot == output);
500        let contains_output = |operands: &[Operand]| operands.iter().any(is_output);
501        match self {
502            Self::Link { set, flags, .. } => is_output(set) != contains_output(flags),
503            Self::IntSet { integers, sets, .. } => {
504                contains_output(integers) != contains_output(sets)
505            }
506            Self::Inverse {
507                forward, inverse, ..
508            } => contains_output(forward) != contains_output(inverse),
509            Self::Sorted { integers, set } => is_output(set) != contains_output(integers),
510            Self::Range {
511                integers,
512                selection,
513                result,
514                ..
515            } => is_output(result) && !is_output(selection) && !contains_output(integers),
516            // A self-reference in an unselected set does not supply an input.
517            Self::Selected {
518                indices, result, ..
519            } => is_output(result) && !is_output(indices),
520            Self::Precede { .. } => false,
521        }
522    }
523    pub fn derive(
524        &self,
525        output: usize,
526        values: &[Option<Value>],
527        constraint: &Constraint,
528    ) -> Result<Option<Value>, CheckError> {
529        let is_output =
530            |operand: &Operand| matches!(operand, Operand::Variable(slot) if *slot == output);
531        let mut candidates = Vec::new();
532        match self {
533            Self::Selected {
534                operation,
535                indices,
536                sets,
537                universe,
538                ..
539            } => {
540                return Ok(
541                    selected_set(*operation, indices, sets, universe.as_ref(), values)
542                        .map(Value::IntegerSet),
543                );
544            }
545            Self::Range {
546                integers,
547                offset,
548                selection,
549                ..
550            } => return Ok(range_set(integers, *offset, selection, values).map(Value::IntegerSet)),
551            Self::Link { set, flags, offset } => {
552                if is_output(set) {
553                    return Ok(link_set(flags, *offset, values).map(Value::IntegerSet));
554                }
555                let Some(set) = set.value(values) else {
556                    return Ok(None);
557                };
558                for (i, _) in flags.iter().enumerate().filter(|(_, flag)| is_output(flag)) {
559                    candidates.push(Value::Boolean(set.set().contains(label(*offset, i))));
560                }
561            }
562            Self::IntSet {
563                integers,
564                sets,
565                integer_offset,
566                set_offset,
567            } => {
568                if sets.iter().any(is_output) {
569                    let Some(integers) = integer_values(integers, values) else {
570                        return Ok(None);
571                    };
572                    for (j, _) in sets.iter().enumerate().filter(|(_, set)| is_output(set)) {
573                        let bucket = label(*set_offset, j);
574                        let mut members = IntegerSet::from_values(
575                            integers
576                                .iter()
577                                .enumerate()
578                                .filter(|(_, value)| **value == bucket)
579                                .map(|(i, _)| label(*integer_offset, i)),
580                        );
581                        if bucket == 0 && !integers.is_empty() {
582                            members = members.union(&IntegerSet::interval(0, *integer_offset - 1));
583                        }
584                        candidates.push(Value::IntegerSet(members));
585                    }
586                } else {
587                    let Some(sets) = set_values(sets, values) else {
588                        return Ok(None);
589                    };
590                    for (i, _) in integers
591                        .iter()
592                        .enumerate()
593                        .filter(|(_, integer)| is_output(integer))
594                    {
595                        let memberships: Vec<_> = sets
596                            .iter()
597                            .enumerate()
598                            .filter(|(_, set)| set.contains(label(*integer_offset, i)))
599                            .map(|(j, _)| label(*set_offset, j))
600                            .collect();
601                        if memberships.len() != 1 {
602                            return Ok(None);
603                        }
604                        candidates.push(Value::Integer(memberships[0]));
605                    }
606                }
607            }
608            Self::Inverse {
609                forward,
610                inverse,
611                forward_offset,
612                inverse_offset,
613            } => {
614                let (inputs, input_offset, targets, target_offset) =
615                    if forward.iter().any(is_output) {
616                        (inverse, *inverse_offset, forward, *forward_offset)
617                    } else {
618                        (forward, *forward_offset, inverse, *inverse_offset)
619                    };
620                let Some(inputs) = set_values(inputs, values) else {
621                    return Ok(None);
622                };
623                for (i, _) in targets
624                    .iter()
625                    .enumerate()
626                    .filter(|(_, target)| is_output(target))
627                {
628                    candidates.push(Value::IntegerSet(IntegerSet::from_values(
629                        inputs
630                            .iter()
631                            .enumerate()
632                            .filter(|(_, set)| set.contains(label(target_offset, i)))
633                            .map(|(j, _)| label(input_offset, j)),
634                    )));
635                }
636            }
637            Self::Sorted { integers, set } => {
638                if is_output(set) {
639                    return Ok(integer_values(integers, values)
640                        .map(|integers| Value::IntegerSet(IntegerSet::from_values(integers))));
641                }
642                let Some(set) = set.value(values) else {
643                    return Ok(None);
644                };
645                let cardinality = checked_cardinality(set.set(), constraint)?;
646                if cardinality != integers.len() as i64 {
647                    return Ok(None);
648                }
649                // Enumerate only after the cardinality matches the actual output array.
650                let members = set.set().members();
651                for (_, member) in integers
652                    .iter()
653                    .zip(members)
654                    .filter(|(operand, _)| is_output(operand))
655                {
656                    candidates.push(Value::Integer(member));
657                }
658            }
659            Self::Precede { .. } => return Ok(None),
660        }
661        Ok(agreed(candidates))
662    }
663    fn faults(&self, values: &[Option<Value>]) -> Result<Vec<SetChannelFault>, &'static str> {
664        use SetChannelFault::*;
665        use SetChannelSide as Side;
666        let mut faults = Vec::new();
667        match self {
668            Self::Selected {
669                operation,
670                indices,
671                sets,
672                result,
673                universe,
674            } => {
675                outside(
676                    &mut faults,
677                    Side::Selection,
678                    None,
679                    indices,
680                    &IntegerSet::interval(1, sets.len() as i64),
681                    values,
682                );
683                if let Some(indices) = indices.value(values)
684                    && *operation == SetAggregate::Partition
685                {
686                    for (i, a) in sets
687                        .iter()
688                        .enumerate()
689                        .filter(|(i, _)| indices.set().contains(label(1, *i)))
690                    {
691                        if let Some(a) = a.value(values) {
692                            for (j, b) in sets
693                                .iter()
694                                .enumerate()
695                                .skip(i + 1)
696                                .filter(|(j, _)| indices.set().contains(label(1, *j)))
697                            {
698                                if let Some(b) = b.value(values) {
699                                    let intersection = a.set().intersection(b.set());
700                                    if !intersection.is_empty() {
701                                        faults.push(Partition {
702                                            first: i + 1,
703                                            second: j + 1,
704                                            intersection,
705                                        });
706                                    }
707                                }
708                            }
709                        }
710                    }
711                }
712                if let Some(universe) = universe {
713                    outside(&mut faults, Side::Result, None, result, universe, values);
714                }
715                mismatch(
716                    &mut faults,
717                    Side::Result,
718                    None,
719                    result,
720                    selected_set(*operation, indices, sets, universe.as_ref(), values)
721                        .map(Value::IntegerSet),
722                    values,
723                );
724            }
725            Self::Link { set, flags, offset } => {
726                outside(
727                    &mut faults,
728                    Side::Set,
729                    None,
730                    set,
731                    &visible(*offset, flags.len()),
732                    values,
733                );
734                if let Some(set) = set.value(values) {
735                    for (i, flag) in flags.iter().enumerate() {
736                        mismatch(
737                            &mut faults,
738                            Side::Flags,
739                            Some(i + 1),
740                            flag,
741                            Some(Value::Boolean(set.set().contains(label(*offset, i)))),
742                            values,
743                        );
744                    }
745                }
746                mismatch(
747                    &mut faults,
748                    Side::Set,
749                    None,
750                    set,
751                    link_set(flags, *offset, values).map(Value::IntegerSet),
752                    values,
753                );
754            }
755            Self::Range {
756                integers,
757                offset,
758                selection,
759                result,
760            } => {
761                let n = integer_length(*offset, integers.len()).expect("validated interval");
762                outside(
763                    &mut faults,
764                    Side::Selection,
765                    None,
766                    selection,
767                    &IntegerSet::interval(0, n - 1),
768                    values,
769                );
770                mismatch(
771                    &mut faults,
772                    Side::Result,
773                    None,
774                    result,
775                    range_set(integers, *offset, selection, values).map(Value::IntegerSet),
776                    values,
777                );
778            }
779            Self::IntSet {
780                integers,
781                sets,
782                integer_offset: a,
783                set_offset: b,
784            } => {
785                let n = integer_length(*a, integers.len()).expect("validated interval");
786                let prefix = if integers.is_empty() {
787                    IntegerSet::empty()
788                } else {
789                    IntegerSet::interval(0, *a - 1)
790                };
791                if !integers.is_empty() && sets.is_empty() {
792                    faults.push(Padding {
793                        integer_indices: visible(*a, integers.len()).union(&prefix),
794                        set_indices: IntegerSet::empty(),
795                    });
796                }
797                if *b > 0 && integers.is_empty() && *a > 0 {
798                    faults.push(Padding {
799                        integer_indices: IntegerSet::interval(0, *a - 1),
800                        set_indices: IntegerSet::interval(0, 0),
801                    });
802                }
803                // Visible bucket zero cannot contain integer-prefix positions: its dom excludes them.
804                if *b == 0 && !sets.is_empty() && !prefix.is_empty() {
805                    faults.push(Padding {
806                        integer_indices: prefix.clone(),
807                        set_indices: IntegerSet::interval(0, 0),
808                    });
809                }
810                for (i, integer) in integers.iter().enumerate() {
811                    if let Some(value) = integer.value(values)
812                        && !visible(*b, sets.len()).contains(value.integer())
813                    {
814                        faults.push(Outside {
815                            side: Side::Integers,
816                            position: Some(i + 1),
817                            members: IntegerSet::from_values([value.integer()]),
818                            allowed: visible(*b, sets.len()),
819                        });
820                    }
821                }
822                for (j, set) in sets.iter().enumerate() {
823                    outside(
824                        &mut faults,
825                        Side::Sets,
826                        Some(j + 1),
827                        set,
828                        &IntegerSet::interval(*a, n - 1),
829                        values,
830                    );
831                    if let Some(set) = set.value(values) {
832                        let expected_prefix = if label(*b, j) == 0 {
833                            prefix.clone()
834                        } else {
835                            IntegerSet::empty()
836                        };
837                        let actual_prefix =
838                            set.set().intersection(&IntegerSet::interval(0, *a - 1));
839                        if expected_prefix != actual_prefix {
840                            faults.push(Padding {
841                                integer_indices: expected_prefix
842                                    .symmetric_difference(&actual_prefix),
843                                set_indices: IntegerSet::interval(label(*b, j), label(*b, j)),
844                            });
845                        }
846                    }
847                }
848                if let Some(sets) = set_values(sets, values) {
849                    for i in 0..integers.len() {
850                        let index = label(*a, i);
851                        let memberships = IntegerSet::from_values(
852                            sets.iter()
853                                .enumerate()
854                                .filter(|(_, set)| set.contains(index))
855                                .map(|(j, _)| label(*b, j)),
856                        );
857                        if memberships.cardinality() != Some(1) {
858                            faults.push(MembershipCount {
859                                position: i + 1,
860                                index,
861                                memberships,
862                            });
863                        }
864                    }
865                }
866                membership_faults(&mut faults, integers, sets, *a, *b, values, false);
867            }
868            Self::Inverse {
869                forward,
870                inverse,
871                forward_offset: a,
872                inverse_offset: b,
873            } => {
874                for (i, set) in forward.iter().enumerate() {
875                    outside(
876                        &mut faults,
877                        Side::Forward,
878                        Some(i + 1),
879                        set,
880                        &visible(*b, inverse.len()),
881                        values,
882                    );
883                }
884                for (i, set) in inverse.iter().enumerate() {
885                    outside(
886                        &mut faults,
887                        Side::Inverse,
888                        Some(i + 1),
889                        set,
890                        &visible(*a, forward.len()),
891                        values,
892                    );
893                }
894                membership_faults(&mut faults, forward, inverse, *a, *b, values, true);
895            }
896            Self::Precede {
897                sets,
898                first,
899                second,
900            } => {
901                let mut earlier = false;
902                let mut unknown = false;
903                for (i, set) in sets.iter().enumerate() {
904                    let Some(set) = set.value(values) else {
905                        unknown = true;
906                        continue;
907                    };
908                    let (s, t) = (set.set().contains(*first), set.set().contains(*second));
909                    if t && !s && !earlier && !unknown {
910                        faults.push(Precedence { position: i + 1 });
911                    }
912                    earlier |= s && !t;
913                }
914            }
915            Self::Sorted { integers, set } => {
916                for (i, pair) in integers.windows(2).enumerate() {
917                    if let (Some(a), Some(b)) = (pair[0].value(values), pair[1].value(values))
918                        && a.integer() >= b.integer()
919                    {
920                        faults.push(Order {
921                            first: i + 1,
922                            second: i + 2,
923                        });
924                    }
925                }
926                if let Some(set) = set.value(values) {
927                    let actual = set
928                        .set()
929                        .cardinality()
930                        .ok_or("checked i64 overflow in sorted channel cardinality")?;
931                    let expected = integers.len() as i64;
932                    if actual != expected {
933                        faults.push(Cardinality { expected, actual });
934                    }
935                    for (i, integer) in integers.iter().enumerate() {
936                        if let Some(integer) = integer.value(values)
937                            && !set.set().contains(integer.integer())
938                        {
939                            faults.push(Outside {
940                                side: Side::Integers,
941                                position: Some(i + 1),
942                                members: IntegerSet::from_values([integer.integer()]),
943                                allowed: set.set().clone(),
944                            });
945                        }
946                    }
947                }
948                mismatch(
949                    &mut faults,
950                    Side::Set,
951                    None,
952                    set,
953                    integer_values(integers, values)
954                        .map(|integers| Value::IntegerSet(IntegerSet::from_values(integers))),
955                    values,
956                );
957            }
958        }
959        Ok(faults)
960    }
961}
962fn check_set_constant(value: i64) -> Result<(), &'static str> {
963    if (GECODE_SET_MIN..=GECODE_SET_MAX).contains(&value) {
964        Ok(())
965    } else {
966        Err("fixed parameter exceeds Gecode set limits")
967    }
968}
969fn checked_length(offset: i64, length: usize) -> Result<i64, &'static str> {
970    if offset < 0 {
971        return Err("set channel offsets must be nonnegative");
972    }
973    let length = i64::try_from(length).map_err(|_| "set channel array length exceeds i64")?;
974    offset
975        .checked_add(length)
976        .filter(|n| *n <= i64::from(i32::MAX))
977        .ok_or("fixed set channel interval exceeds native index bounds")
978}
979fn integer_length(offset: i64, length: usize) -> Result<i64, &'static str> {
980    let n = checked_length(offset, length)?;
981    Ok(if length == 0 { 0 } else { n })
982}
983fn label(offset: i64, position: usize) -> i64 {
984    offset + position as i64
985}
986fn visible(offset: i64, length: usize) -> IntegerSet {
987    IntegerSet::interval(offset, label(offset, length) - 1)
988}
989fn integer_values(entries: &[Operand], values: &[Option<Value>]) -> Option<Vec<i64>> {
990    entries
991        .iter()
992        .map(|entry| entry.value(values).map(Value::integer))
993        .collect()
994}
995fn set_values<'a>(
996    entries: &'a [Operand],
997    values: &'a [Option<Value>],
998) -> Option<Vec<&'a IntegerSet>> {
999    entries
1000        .iter()
1001        .map(|entry| entry.value(values).map(Value::set))
1002        .collect()
1003}
1004fn agreed(values: Vec<Value>) -> Option<Value> {
1005    let first = values.first()?;
1006    values
1007        .iter()
1008        .all(|value| value == first)
1009        .then(|| first.clone())
1010}
1011fn checked_cardinality(set: &IntegerSet, constraint: &Constraint) -> Result<i64, CheckError> {
1012    set.cardinality().ok_or_else(|| {
1013        constraint.execution_error("checked i64 overflow in sorted channel cardinality")
1014    })
1015}
1016fn selected_set(
1017    operation: SetAggregate,
1018    indices: &Operand,
1019    sets: &[Operand],
1020    universe: Option<&IntegerSet>,
1021    values: &[Option<Value>],
1022) -> Option<IntegerSet> {
1023    let indices = indices.value(values)?.set();
1024    if !indices.is_subset(&IntegerSet::interval(1, sets.len() as i64)) {
1025        return None;
1026    }
1027    let mut result = None;
1028    for (_, set) in sets
1029        .iter()
1030        .enumerate()
1031        .filter(|(i, _)| indices.contains(label(1, *i)))
1032    {
1033        let set = set.value(values)?.set();
1034        result = Some(match result {
1035            None => set.clone(),
1036            Some(result) => {
1037                if operation == SetAggregate::Intersection {
1038                    set.intersection(&result)
1039                } else {
1040                    set.union(&result)
1041                }
1042            }
1043        });
1044    }
1045    Some(result.unwrap_or_else(|| {
1046        if operation == SetAggregate::Intersection {
1047            universe
1048                .cloned()
1049                .unwrap_or_else(|| IntegerSet::interval(GECODE_SET_MIN, GECODE_SET_MAX))
1050        } else {
1051            IntegerSet::empty()
1052        }
1053    }))
1054}
1055fn range_set(
1056    integers: &[Operand],
1057    offset: i64,
1058    selection: &Operand,
1059    values: &[Option<Value>],
1060) -> Option<IntegerSet> {
1061    let selection = selection.value(values)?.set();
1062    let n = integer_length(offset, integers.len()).expect("validated interval");
1063    if !selection.is_subset(&IntegerSet::interval(0, n - 1)) {
1064        return None;
1065    }
1066    let mut result = Vec::new();
1067    if !selection
1068        .intersection(&IntegerSet::interval(0, offset - 1))
1069        .is_empty()
1070    {
1071        result.push(0);
1072    }
1073    for (_, integer) in integers
1074        .iter()
1075        .enumerate()
1076        .filter(|(i, _)| selection.contains(label(offset, *i)))
1077    {
1078        result.push(integer.value(values)?.integer());
1079    }
1080    Some(IntegerSet::from_values(result))
1081}
1082fn link_set(flags: &[Operand], offset: i64, values: &[Option<Value>]) -> Option<IntegerSet> {
1083    let flags: Vec<_> = flags
1084        .iter()
1085        .map(|flag| flag.value(values).map(Value::boolean))
1086        .collect::<Option<_>>()?;
1087    Some(IntegerSet::from_values(
1088        flags
1089            .iter()
1090            .enumerate()
1091            .filter(|(_, flag)| **flag)
1092            .map(|(i, _)| label(offset, i)),
1093    ))
1094}
1095fn outside(
1096    faults: &mut Vec<SetChannelFault>,
1097    side: SetChannelSide,
1098    position: Option<usize>,
1099    operand: &Operand,
1100    allowed: &IntegerSet,
1101    values: &[Option<Value>],
1102) {
1103    if let Some(set) = operand.value(values) {
1104        let members = set.set().difference(allowed);
1105        if !members.is_empty() {
1106            faults.push(SetChannelFault::Outside {
1107                side,
1108                position,
1109                members,
1110                allowed: allowed.clone(),
1111            });
1112        }
1113    }
1114}
1115fn mismatch(
1116    faults: &mut Vec<SetChannelFault>,
1117    side: SetChannelSide,
1118    position: Option<usize>,
1119    operand: &Operand,
1120    expected: Option<Value>,
1121    values: &[Option<Value>],
1122) {
1123    if let Some(expected) = expected
1124        && operand
1125            .value(values)
1126            .is_some_and(|actual| *actual != expected)
1127    {
1128        faults.push(SetChannelFault::Mismatch {
1129            side,
1130            position,
1131            expected,
1132        });
1133    }
1134}
1135fn membership_faults(
1136    faults: &mut Vec<SetChannelFault>,
1137    left: &[Operand],
1138    right: &[Operand],
1139    a: i64,
1140    b: i64,
1141    values: &[Option<Value>],
1142    left_is_set: bool,
1143) {
1144    for (i, left) in left.iter().enumerate() {
1145        if let Some(left) = left.value(values) {
1146            for (j, right) in right.iter().enumerate() {
1147                if let Some(right) = right.value(values) {
1148                    let expected = if left_is_set {
1149                        left.set().contains(label(b, j))
1150                    } else {
1151                        left.integer() == label(b, j)
1152                    };
1153                    if expected != right.set().contains(label(a, i)) {
1154                        faults.push(SetChannelFault::Membership {
1155                            position: i + 1,
1156                            index: label(a, i),
1157                            set_position: j + 1,
1158                            set_index: label(b, j),
1159                            expected,
1160                        });
1161                    }
1162                }
1163            }
1164        }
1165    }
1166}
1167
1168impl PreparedModel {
1169    pub(crate) fn evaluate_set_channel(
1170        &self,
1171        constraint: &Constraint,
1172        channel: &SetChannel,
1173        values: &[Option<Value>],
1174    ) -> Result<Option<ViolationKind>, CheckError> {
1175        let faults = channel
1176            .faults(values)
1177            .map_err(|message| constraint.execution_error(message))?;
1178        if faults.is_empty() {
1179            return Ok(None);
1180        }
1181        let argument = |operand: &Operand, role, position, index| SetChannelArgument {
1182            role,
1183            position,
1184            index,
1185            value: self.argument(operand, values),
1186            unresolved: match operand {
1187                Operand::Variable(slot) if values[*slot].is_none() => {
1188                    Some(Box::new(self.slots[*slot].reference.clone()))
1189                }
1190                _ => None,
1191            },
1192        };
1193        let array = |entries: &[Operand], role, offset| {
1194            entries
1195                .iter()
1196                .enumerate()
1197                .map(|(i, operand)| argument(operand, role, Some(i + 1), Some(label(offset, i))))
1198                .collect::<Vec<_>>()
1199        };
1200        use SetChannelSide as Side;
1201        let finding = match channel {
1202            SetChannel::Link { set, flags, offset } => SetChannelFinding::Link {
1203                arguments: std::iter::once(argument(set, Side::Set, None, None))
1204                    .chain(array(flags, Side::Flags, *offset))
1205                    .collect(),
1206                offset: *offset,
1207                faults,
1208            },
1209            SetChannel::IntSet {
1210                integers,
1211                sets,
1212                integer_offset,
1213                set_offset,
1214            } => SetChannelFinding::IntSet {
1215                arguments: array(integers, Side::Integers, *integer_offset)
1216                    .into_iter()
1217                    .chain(array(sets, Side::Sets, *set_offset))
1218                    .collect(),
1219                integer_offset: *integer_offset,
1220                set_offset: *set_offset,
1221                faults,
1222            },
1223            SetChannel::Inverse {
1224                forward,
1225                inverse,
1226                forward_offset,
1227                inverse_offset,
1228            } => SetChannelFinding::Inverse {
1229                arguments: array(forward, Side::Forward, *forward_offset)
1230                    .into_iter()
1231                    .chain(array(inverse, Side::Inverse, *inverse_offset))
1232                    .collect(),
1233                forward_offset: *forward_offset,
1234                inverse_offset: *inverse_offset,
1235                faults,
1236            },
1237            SetChannel::Range {
1238                integers,
1239                offset,
1240                selection,
1241                result,
1242            } => SetChannelFinding::Range {
1243                arguments: array(integers, Side::Integers, *offset)
1244                    .into_iter()
1245                    .chain([
1246                        argument(selection, Side::Selection, None, None),
1247                        argument(result, Side::Result, None, None),
1248                    ])
1249                    .collect(),
1250                offset: *offset,
1251                faults,
1252            },
1253            SetChannel::Precede {
1254                sets,
1255                first,
1256                second,
1257            } => SetChannelFinding::Precede {
1258                arguments: array(sets, Side::Sets, 0),
1259                first: *first,
1260                second: *second,
1261                faults,
1262            },
1263            SetChannel::Sorted { integers, set } => SetChannelFinding::Sorted {
1264                arguments: array(integers, Side::Integers, 0)
1265                    .into_iter()
1266                    .chain([argument(set, Side::Set, None, None)])
1267                    .collect(),
1268                faults,
1269            },
1270            SetChannel::Selected {
1271                operation,
1272                indices,
1273                sets,
1274                result,
1275                universe,
1276            } => SetChannelFinding::Selected {
1277                arguments: std::iter::once(argument(indices, Side::Selection, None, None))
1278                    .chain(array(sets, Side::Sets, 1))
1279                    .chain([argument(result, Side::Result, None, None)])
1280                    .collect(),
1281                aggregate: *operation,
1282                universe: universe.clone(),
1283                faults,
1284            },
1285        };
1286        Ok(Some(ViolationKind::SetChannel { finding }))
1287    }
1288}