Skip to main content

conjure_cp_core/ast/
expressions.rs

1use std::borrow::Cow;
2use std::collections::{BTreeSet, HashSet, VecDeque};
3use std::fmt::{Display, Formatter};
4use std::hash::{DefaultHasher, Hash, Hasher};
5use std::sync::atomic::{AtomicU64, Ordering};
6
7static HASH_HITS: AtomicU64 = AtomicU64::new(0);
8static HASH_MISSES: AtomicU64 = AtomicU64::new(0);
9
10pub fn print_hash_stats() {
11    println!(
12        "Expression content hash stats: hits={}, misses={}",
13        HASH_HITS.load(Ordering::Relaxed),
14        HASH_MISSES.load(Ordering::Relaxed)
15    );
16}
17use tracing::trace;
18
19use conjure_cp_enum_compatibility_macro::{document_compatibility, generate_discriminants};
20use itertools::Itertools;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use polyquine::Quine;
25use uniplate::{Biplate, Uniplate};
26
27use crate::ast::FuncAttr;
28use crate::ast::metadata::NO_HASH;
29use crate::bug;
30
31use super::ac_operators::ACOperatorKind;
32use super::categories::{Category, CategoryOf};
33use super::comprehension::{Comprehension, ComprehensionQualifier};
34use super::declaration::DeclarationKind;
35use super::domains::HasDomain as _;
36use super::eval::{eval_constant, factorial_i32};
37use super::pretty::{pretty_expression_domain_annotation, pretty_expression_type_annotation};
38use super::pretty::{pretty_expressions_as_top_level, pretty_vec};
39use super::records::Field;
40use super::sat_encoding::SATIntEncoding;
41use super::{
42    AbstractLiteral, Atom, DeclarationPtr, Domain, DomainPtr, GroundDomain, IntVal, JectivityAttr,
43    Literal, MSetAttr, Metadata, Model, Moo, Name, PartialityAttr, Range, Reference, RelAttr,
44    ReturnType, SetAttr, SymbolTable, SymbolTablePtr, Typeable, UnresolvedDomain, matrix,
45};
46
47// Ensure that this type doesn't get too big
48//
49// If you triggered this assertion, you either made a variant of this enum that is too big, or you
50// made Name,Literal,AbstractLiteral,Atom bigger, which made this bigger! To fix this, put some
51// stuff in boxes.
52//
53// Enums take the size of their largest variant, so an enum with mostly small variants and a few
54// large ones wastes memory... A larger Expression type also slows down Oxide.
55//
56// For more information, and more details on type sizes and how to measure them, see the commit
57// message for 6012de809 (perf: reduce size of AST types, 2025-06-18).
58//
59// You can also see type sizes in the rustdoc documentation, generated by ./tools/gen_docs.sh
60//
61// https://github.com/conjure-cp/conjure-oxide/commit/6012de8096ca491ded91ecec61352fdf4e994f2e
62
63// TODO: box all usages of Metadata to bring this down a bit more - I have added variants to
64// ReturnType, and Metadata contains ReturnType, so Metadata has got bigger. Metadata will get a
65// lot bigger still when we start using it for memoisation, so it should really be
66// boxed ~niklasdewally
67
68// Metadata's mutex makes the exact layout platform-dependent, so enforce only the intended
69// upper bound. The largest known layout is 152 bytes.
70static_assertions::const_assert!(std::mem::size_of::<Expression>() <= 152);
71
72/// Represents different types of expressions used to define rules and constraints in the model.
73///
74/// The `Expression` enum includes operations, constants, and variable references
75/// used to build rules and conditions for the model.
76#[generate_discriminants]
77#[document_compatibility]
78#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Uniplate, Quine)]
79#[biplate(to=AbstractLiteral<Expression>)]
80#[biplate(to=AbstractLiteral<Literal>)]
81#[biplate(to=Atom)]
82#[biplate(to=Comprehension)]
83#[biplate(to=DeclarationPtr)]
84#[biplate(to=DomainPtr)]
85#[biplate(to=Literal)]
86#[biplate(to=Metadata)]
87#[biplate(to=Name)]
88#[biplate(to=Option<Expression>)]
89#[biplate(to=Field<Expression>)]
90#[biplate(to=Field<Literal>)]
91#[biplate(to=Reference)]
92#[biplate(to=Model)]
93#[biplate(to=SymbolTable)]
94#[biplate(to=SymbolTablePtr)]
95#[biplate(to=Vec<Expression>)]
96#[path_prefix(conjure_cp::ast)]
97pub enum Expression {
98    AbstractLiteral(Metadata, AbstractLiteral<Expression>),
99    /// The top of the model
100    Root(Metadata, Vec<Expression>),
101
102    /// An expression representing "A is valid as long as B is true"
103    /// Turns into a conjunction when it reaches a boolean context
104    Bubble(Metadata, Moo<Expression>, Moo<Expression>),
105
106    /// A comprehension.
107    ///
108    /// The inside of the comprehension opens a new scope.
109    // todo (gskorokhod): Comprehension contains a symbol table which contains a bunch of pointers.
110    // This makes implementing Quine tricky (it doesnt support Rc, by design). Skip it for now.
111    #[polyquine_skip]
112    Comprehension(Metadata, Moo<Comprehension>),
113
114    /// Defines dominance ("Solution A is preferred over Solution B")
115    DominanceRelation(Metadata, Moo<Expression>),
116    /// `fromSolution(name)` - Used in dominance relation definitions
117    FromSolution(Metadata, Moo<Atom>),
118
119    #[polyquine_with(arm = (_, name) => {
120        let ident = proc_macro2::Ident::new(name.as_str(), proc_macro2::Span::call_site());
121        quote::quote! { #ident.clone().into() }
122    })]
123    Metavar(Metadata, Ustr),
124
125    Atomic(Metadata, Atom),
126
127    /// Type annotation expression: `expr :: type`.
128    TypeAnnotation(Metadata, Moo<Expression>, DomainPtr),
129
130    /// Domain annotation expression: `expr : domain`.
131    DomainAnnotation(Metadata, Moo<Expression>, DomainPtr),
132
133    /// Asserts that the given variant of a variant expression is in use.
134    /// See also: [GroundDomain::Variant]
135    #[compatible(JsonInput)]
136    Active(Metadata, Moo<Expression>, Name),
137
138    /// Indexing into a record expression, e.g `{foo = 1, bar = true}[foo]`
139    /// See also: [GroundDomain::Record]
140    #[compatible(JsonInput)]
141    RecordField(Metadata, Moo<Expression>, Name),
142
143    /// A matrix index.
144    ///
145    /// Defined iff the indices are within their respective index domains.
146    #[compatible(JsonInput)]
147    UnsafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
148
149    /// A safe matrix index.
150    ///
151    /// See [`Expression::UnsafeIndex`]
152    #[compatible(SMT)]
153    SafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
154
155    /// A matrix slice: `a[indices]`.
156    ///
157    /// One of the indicies may be `None`, representing the dimension of the matrix we want to take
158    /// a slice of. For example, for some 3d matrix a, `a[1,..,2]` has the indices
159    /// `Some(1),None,Some(2)`.
160    ///
161    /// It is assumed that the slice only has one "wild-card" dimension and thus is 1 dimensional.
162    ///
163    /// Defined iff the defined indices are within their respective index domains.
164    #[compatible(JsonInput)]
165    UnsafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
166
167    /// A safe matrix slice: `a[indices]`.
168    ///
169    /// See [`Expression::UnsafeSlice`].
170    SafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
171
172    /// `inDomain(x,domain)` iff `x` is in the domain `domain`.
173    ///
174    /// This cannot be constructed from Essence input, nor passed to a solver: this expression is
175    /// mainly used during the conversion of `UnsafeIndex` and `UnsafeSlice` to `SafeIndex` and
176    /// `SafeSlice` respectively.
177    InDomain(Metadata, Moo<Expression>, DomainPtr),
178
179    /// `toInt(b)` casts boolean expression b to an integer.
180    ///
181    /// - If b is false, then `toInt(b) == 0`
182    ///
183    /// - If b is true, then `toInt(b) == 1`
184    #[compatible(SMT)]
185    ToInt(Metadata, Moo<Expression>),
186
187    /// `|x|` - absolute value of `x`
188    #[compatible(JsonInput, SMT)]
189    Abs(Metadata, Moo<Expression>),
190
191    /// `sum(<vec_expr>)`
192    #[compatible(JsonInput, SMT)]
193    Sum(Metadata, Moo<Expression>),
194
195    /// `a * b * c * ...`
196    #[compatible(JsonInput, SMT)]
197    Product(Metadata, Moo<Expression>),
198
199    /// `min(<vec_expr>)`
200    #[compatible(JsonInput, SMT)]
201    Min(Metadata, Moo<Expression>),
202
203    /// `max(<vec_expr>)`
204    #[compatible(JsonInput, SMT)]
205    Max(Metadata, Moo<Expression>),
206
207    /// `not(a)`
208    #[compatible(JsonInput, SAT, SMT)]
209    Not(Metadata, Moo<Expression>),
210
211    /// `or(<vec_expr>)`
212    #[compatible(JsonInput, SAT, SMT)]
213    Or(Metadata, Moo<Expression>),
214
215    /// `and(<vec_expr>)`
216    #[compatible(JsonInput, SAT, SMT)]
217    And(Metadata, Moo<Expression>),
218
219    /// Ensures that `a->b` (material implication).
220    #[compatible(JsonInput, SMT)]
221    Imply(Metadata, Moo<Expression>, Moo<Expression>),
222
223    /// `iff(a, b)` a <-> b
224    #[compatible(JsonInput, SMT)]
225    Iff(Metadata, Moo<Expression>, Moo<Expression>),
226
227    #[compatible(JsonInput)]
228    Union(Metadata, Moo<Expression>, Moo<Expression>),
229
230    #[compatible(JsonInput)]
231    In(Metadata, Moo<Expression>, Moo<Expression>),
232
233    #[compatible(JsonInput)]
234    Intersect(Metadata, Moo<Expression>, Moo<Expression>),
235
236    /// Set difference, `a - b`.
237    ///
238    /// Spelled with minus in Essence, but kept apart from [`Expression::Minus`]: the two share a
239    /// symbol and nothing else, and overloading one node for both made every arithmetic rule have
240    /// to ask whether its operands were really sets.
241    #[compatible(JsonInput)]
242    Difference(Metadata, Moo<Expression>, Moo<Expression>),
243
244    #[compatible(JsonInput)]
245    Supset(Metadata, Moo<Expression>, Moo<Expression>),
246
247    #[compatible(JsonInput)]
248    SupsetEq(Metadata, Moo<Expression>, Moo<Expression>),
249
250    #[compatible(JsonInput)]
251    Subset(Metadata, Moo<Expression>, Moo<Expression>),
252
253    #[compatible(JsonInput)]
254    SubsetEq(Metadata, Moo<Expression>, Moo<Expression>),
255
256    #[compatible(JsonInput, SMT)]
257    Eq(Metadata, Moo<Expression>, Moo<Expression>),
258
259    #[compatible(JsonInput, SMT)]
260    Neq(Metadata, Moo<Expression>, Moo<Expression>),
261
262    #[compatible(JsonInput, SMT)]
263    Geq(Metadata, Moo<Expression>, Moo<Expression>),
264
265    #[compatible(JsonInput, SMT)]
266    Leq(Metadata, Moo<Expression>, Moo<Expression>),
267
268    #[compatible(JsonInput, SMT)]
269    Gt(Metadata, Moo<Expression>, Moo<Expression>),
270
271    #[compatible(JsonInput, SMT)]
272    Lt(Metadata, Moo<Expression>, Moo<Expression>),
273
274    /// `s subsequence t` tests whether the list of values taken by s occurs in the same order
275    /// in the list of values taken by t
276    #[compatible(JsonInput)]
277    Subsequence(Metadata, Moo<Expression>, Moo<Expression>),
278
279    /// `s substring t` tests whether the list of values taken by s occurs in the same order
280    /// and contiguously in the list of values taken by t
281    #[compatible(JsonInput)]
282    Substring(Metadata, Moo<Expression>, Moo<Expression>),
283
284    /// `catchUndef(e, d)`: `e` where `e` is defined, `d` where it is not.
285    ///
286    /// Definedness is only known once the bubble rules have run, so this survives until then and
287    /// is lowered against the bubble condition the inner expression produces.
288    #[compatible(JsonInput)]
289    CatchUndef(Metadata, Moo<Expression>, Moo<Expression>),
290
291    /// Division after preventing division by zero, usually with a bubble
292    #[compatible(SMT)]
293    SafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
294
295    /// Division with a possibly undefined value (division by 0)
296    #[compatible(JsonInput)]
297    UnsafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
298
299    /// Modulo after preventing mod 0, usually with a bubble
300    #[compatible(SMT)]
301    SafeMod(Metadata, Moo<Expression>, Moo<Expression>),
302
303    /// Modulo with a possibly undefined value (mod 0)
304    #[compatible(JsonInput)]
305    UnsafeMod(Metadata, Moo<Expression>, Moo<Expression>),
306
307    /// Negation: `-x`
308    #[compatible(JsonInput, SMT)]
309    Neg(Metadata, Moo<Expression>),
310
311    /// Factorial: `x!` or 'factorial(x)`
312    #[compatible(JsonInput)]
313    Factorial(Metadata, Moo<Expression>),
314
315    /// Set of domain values function is defined for
316    #[compatible(JsonInput)]
317    Defined(Metadata, Moo<Expression>),
318
319    /// Set of codomain values function is defined for
320    #[compatible(JsonInput)]
321    Range(Metadata, Moo<Expression>),
322
323    #[compatible(JsonInput)]
324    ToSet(Metadata, Moo<Expression>),
325
326    #[compatible(JsonInput)]
327    ToMSet(Metadata, Moo<Expression>),
328
329    #[compatible(JsonInput)]
330    ToRelation(Metadata, Moo<Expression>),
331
332    /// Unsafe power`x**y` (possibly undefined)
333    ///
334    /// Defined when (X!=0 \\/ Y!=0) /\ Y>=0
335    #[compatible(JsonInput)]
336    UnsafePow(Metadata, Moo<Expression>, Moo<Expression>),
337
338    /// `UnsafePow` after preventing undefinedness
339    SafePow(Metadata, Moo<Expression>, Moo<Expression>),
340
341    /// Flatten matrix operator
342    /// `flatten(M)` or `flatten(n, M)`
343    /// where M is a matrix and n is an optional integer argument indicating depth of flattening
344    Flatten(Metadata, Option<Moo<Expression>>, Moo<Expression>),
345
346    /// An attribute predicate used as a constraint, e.g. `reflexive(r)` or `size(s, 3)`.
347    /// Lifted into the target's declaration domain attributes when possible (see
348    /// `passes::attribute_as_constraint`); expanded in place to the equivalent formula otherwise.
349    AttributeAsConstraint(Metadata, Moo<Expression>, Ustr, Option<Moo<Expression>>),
350
351    /// `allDifferent(<vec_expr>)`
352    #[compatible(JsonInput)]
353    AllDiff(Metadata, Moo<Expression>),
354
355    /// Z3's native `distinct` over the elements of `<vec_expr>`.
356    ///
357    /// This is emitted for the SMT backend only, and never appears in a model bound for any other
358    /// solver. It exists so that "hand `allDifferent` to Z3 as `distinct`" and "encode
359    /// `allDifferent` explicitly" are two rules competing at the same choice site, which is what
360    /// lets the heuristics pick between them -- and what records the choice in the model, so it is
361    /// not asked again every time the expression is visited.
362    #[compatible(SMT)]
363    SmtDistinct(Metadata, Moo<Expression>),
364
365    /// `allDifferentExcept(<matrix>, <except>)`
366    #[compatible(JsonInput)]
367    AllDifferentExcept(Metadata, Moo<Expression>, Moo<Expression>),
368
369    /// `elementId(<matrix>, <value>)` — 1-based index of value in matrix
370    ElementId(Metadata, Moo<Expression>, Moo<Expression>),
371
372    /// `table([x1, x2, ...], [[r11, r12, ...], [r21, r22, ...], ...])`
373    ///
374    /// Represents a positive table constraint: the tuple `[x1, x2, ...]` must match one of the
375    /// allowed rows.
376    #[compatible(JsonInput)]
377    Table(Metadata, Moo<Expression>, Moo<Expression>),
378
379    /// `negativeTable([x1, x2, ...], [[r11, r12, ...], [r21, r22, ...], ...])`
380    ///
381    /// Represents a negative table constraint: the tuple `[x1, x2, ...]` must NOT match any of the
382    /// forbidden rows.
383    #[compatible(JsonInput)]
384    NegativeTable(Metadata, Moo<Expression>, Moo<Expression>),
385
386    /// `atleast(vars, counts, values)`
387    ///
388    /// For each `values[i]`, requires at least `counts[i]` occurrences in `vars`.
389    #[compatible(JsonInput)]
390    AtLeast(Metadata, Moo<Expression>, Moo<Expression>, Moo<Expression>),
391
392    /// `atmost(vars, counts, values)`
393    ///
394    /// For each `values[i]`, requires at most `counts[i]` occurrences in `vars`.
395    #[compatible(JsonInput)]
396    AtMost(Metadata, Moo<Expression>, Moo<Expression>, Moo<Expression>),
397
398    /// `gcc(vars, values, counts)`
399    ///
400    /// Global cardinality constraint. For each `values[i]`, requires exactly `counts[i]`
401    /// occurrences in `vars`.
402    #[compatible(JsonInput)]
403    Gcc(Metadata, Moo<Expression>, Moo<Expression>, Moo<Expression>),
404
405    /// Minion `gccweak(vars, values, counts)` — weaker propagation variant of `gcc`.
406    #[compatible(Minion)]
407    GccWeak(Metadata, Moo<Expression>, Moo<Expression>, Moo<Expression>),
408
409    /// Binary subtraction operator
410    ///
411    /// This is a parser-level construct, and is immediately normalised to `Sum([a,-b])`.
412    /// TODO: make this compatible with Set Difference calculations - need to change return type and domain for this expression and write a set comprehension rule.
413    /// have already edited minus_to_sum to prevent this from applying to sets
414    #[compatible(JsonInput)]
415    Minus(Metadata, Moo<Expression>, Moo<Expression>),
416
417    /// Partition Operator: test if a list of elements are not all contained in one part of the partition
418    /// First Expr Arg is a list of elements
419    /// Second Expr Arg is the partition
420    #[compatible(JsonInput)]
421    Apart(Metadata, Moo<Expression>, Moo<Expression>),
422
423    /// Partition Operator: union of all parts of a partition
424    /// Expr Arg is a partition
425    #[compatible(JsonInput)]
426    Participants(Metadata, Moo<Expression>),
427
428    /// Partition Operator: part of partition that contains specified element
429    /// First Expr Arg is an element that should be contained
430    /// Second Expr Arg is the partition that should contain that element
431    #[compatible(JsonInput)]
432    Party(Metadata, Moo<Expression>, Moo<Expression>),
433
434    /// Partition Operator: partition to its set of parts
435    /// Expr Arg is the partition from which the parts come from
436    #[compatible(JsonInput)]
437    Parts(Metadata, Moo<Expression>),
438
439    /// Partition Operator: test if a list of elements are all in the same part of the partition
440    /// First Expr Arg is the list of elements to test with
441    /// Second Expr Arg is the partition to test on
442    #[compatible(JsonInput)]
443    Together(Metadata, Moo<Expression>, Moo<Expression>),
444
445    /// Ensures that x=|y| i.e. x is the absolute value of y.
446    ///
447    /// Low-level Minion constraint.
448    ///
449    /// # See also
450    ///
451    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#abs)
452    #[compatible(Minion)]
453    FlatAbsEq(Metadata, Moo<Atom>, Moo<Atom>),
454
455    /// Ensures that `alldiff([a,b,...])`.
456    ///
457    /// Low-level Minion constraint.
458    ///
459    /// # See also
460    ///
461    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#alldiff)
462    #[compatible(Minion)]
463    FlatAllDiff(Metadata, Vec<Atom>),
464
465    /// Ensures that `result = min(vars)`.
466    ///
467    /// Low-level Minion constraint. Prefer this over expanding [`Expression::Min`] into
468    /// leq/or/eq constraints when targeting Minion.
469    ///
470    /// # See also
471    ///
472    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#min)
473    #[compatible(Minion)]
474    FlatMinEq(Metadata, Vec<Atom>, Atom),
475
476    /// Ensures that sum(vec) >= x.
477    ///
478    /// Low-level Minion constraint.
479    ///
480    /// # See also
481    ///
482    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#sumgeq)
483    #[compatible(Minion)]
484    FlatSumGeq(Metadata, Vec<Atom>, Atom),
485
486    /// Ensures that sum(vec) <= x.
487    ///
488    /// Low-level Minion constraint.
489    ///
490    /// # See also
491    ///
492    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#sumleq)
493    #[compatible(Minion)]
494    FlatSumLeq(Metadata, Vec<Atom>, Atom),
495
496    /// `ineq(x,y,k)` ensures that x <= y + k.
497    ///
498    /// Low-level Minion constraint.
499    ///
500    /// # See also
501    ///
502    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#ineq)
503    #[compatible(Minion)]
504    FlatIneq(Metadata, Moo<Atom>, Moo<Atom>, Box<Literal>),
505
506    /// `w-literal(x,k)` ensures that x == k, where x is a variable and k a constant.
507    ///
508    /// Low-level Minion constraint.
509    ///
510    /// This is a low-level Minion constraint and you should probably use Eq instead. The main use
511    /// of w-literal is to convert boolean variables to constraints so that they can be used inside
512    /// watched-and and watched-or.
513    ///
514    /// # See also
515    ///
516    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#minuseq)
517    /// + `rules::minion::boolean_literal_to_wliteral`.
518    #[compatible(Minion)]
519    #[polyquine_skip]
520    FlatWatchedLiteral(Metadata, Reference, Literal),
521
522    /// `weightedsumleq(cs,xs,total)` ensures that cs.xs <= total, where cs.xs is the scalar dot
523    /// product of cs and xs.
524    ///
525    /// Low-level Minion constraint.
526    ///
527    /// Represents a weighted sum of the form `ax + by + cz + ...`
528    ///
529    /// # See also
530    ///
531    /// + [Minion
532    /// documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#weightedsumleq)
533    FlatWeightedSumLeq(Metadata, Vec<Literal>, Vec<Atom>, Moo<Atom>),
534
535    /// `weightedsumgeq(cs,xs,total)` ensures that cs.xs >= total, where cs.xs is the scalar dot
536    /// product of cs and xs.
537    ///
538    /// Low-level Minion constraint.
539    ///
540    /// Represents a weighted sum of the form `ax + by + cz + ...`
541    ///
542    /// # See also
543    ///
544    /// + [Minion
545    /// documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#weightedsumleq)
546    FlatWeightedSumGeq(Metadata, Vec<Literal>, Vec<Atom>, Moo<Atom>),
547
548    /// Ensures that x =-y, where x and y are atoms.
549    ///
550    /// Low-level Minion constraint.
551    ///
552    /// # See also
553    ///
554    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#minuseq)
555    #[compatible(Minion)]
556    FlatMinusEq(Metadata, Moo<Atom>, Moo<Atom>),
557
558    /// Ensures that x*y=z.
559    ///
560    /// Low-level Minion constraint.
561    ///
562    /// # See also
563    ///
564    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#product)
565    #[compatible(Minion)]
566    FlatProductEq(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
567
568    /// Ensures that floor(x/y)=z. Always true when y=0.
569    ///
570    /// Low-level Minion constraint.
571    ///
572    /// # See also
573    ///
574    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#div_undefzero)
575    #[compatible(Minion)]
576    MinionDivEqUndefZero(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
577
578    /// Ensures that x%y=z. Always true when y=0.
579    ///
580    /// Low-level Minion constraint.
581    ///
582    /// # See also
583    ///
584    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#mod_undefzero)
585    #[compatible(Minion)]
586    MinionModuloEqUndefZero(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
587
588    /// Ensures that `x**y = z`.
589    ///
590    /// Low-level Minion constraint.
591    ///
592    /// This constraint is false when `y<0` except for `1**y=1` and `(-1)**y=z` (where z is 1 if y
593    /// is odd and z is -1 if y is even).
594    ///
595    /// # See also
596    ///
597    /// + [Github comment about `pow` semantics](https://github.com/minion/minion/issues/40#issuecomment-2595914891)
598    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#pow)
599    MinionPow(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
600
601    /// `reify(constraint,r)` ensures that r=1 iff `constraint` is satisfied, where r is a 0/1
602    /// variable.
603    ///
604    /// Low-level Minion constraint.
605    ///
606    /// # See also
607    ///
608    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#reify)
609    #[compatible(Minion)]
610    MinionReify(Metadata, Moo<Expression>, Atom),
611
612    /// `reifyimply(constraint,r)` ensures that `r->constraint`, where r is a 0/1 variable.
613    /// variable.
614    ///
615    /// Low-level Minion constraint.
616    ///
617    /// # See also
618    ///
619    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#reifyimply)
620    #[compatible(Minion)]
621    MinionReifyImply(Metadata, Moo<Expression>, Atom),
622
623    /// `w-inintervalset(x, [a1,a2, b1,b2, … ])` ensures that the value of x belongs to one of the
624    /// intervals {a1,…,a2}, {b1,…,b2} etc.
625    ///
626    /// The list of intervals must be given in numerical order.
627    ///
628    /// Low-level Minion constraint.
629    ///
630    /// # See also
631    ///>
632    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inintervalset)
633    #[compatible(Minion)]
634    MinionWInIntervalSet(Metadata, Atom, Vec<i32>),
635
636    /// `w-inset(x, [v1, v2, … ])` ensures that the value of `x` is one of the explicitly given values `v1`, `v2`, etc.
637    ///
638    /// This constraint enforces membership in a specific set of discrete values rather than intervals.
639    ///
640    /// The list of values must be given in numerical order.
641    ///
642    /// Low-level Minion constraint.
643    ///
644    /// # See also
645    ///
646    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inset)
647    #[compatible(Minion)]
648    MinionWInSet(Metadata, Atom, Vec<i32>),
649
650    /// `element_one(vec, i, e)` specifies that `vec[i] = e`. This implies that i is
651    /// in the range `[1..len(vec)]`.
652    ///
653    /// Low-level Minion constraint.
654    ///
655    /// # See also
656    ///
657    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#element_one)
658    #[compatible(Minion)]
659    MinionElementOne(Metadata, Vec<Atom>, Moo<Atom>, Moo<Atom>),
660
661    /// Declaration of an auxiliary variable.
662    ///
663    /// As with Savile Row, we semantically distinguish this from `Eq`.
664    #[compatible(Minion)]
665    #[polyquine_skip]
666    AuxDeclaration(Metadata, Reference, Moo<Expression>),
667
668    /// This expression is for encoding ints for the SAT solver, it stores the encoding type, the vector of booleans and the min/max for the int.
669    #[compatible(SAT)]
670    SATInt(Metadata, SATIntEncoding, Moo<Expression>, (i32, i32)),
671
672    /// Addition over a pair of expressions (i.e. a + b) rather than a vec-expr like Expression::Sum.
673    /// This is for compatibility with backends that do not support addition over vectors.
674    #[compatible(SMT)]
675    PairwiseSum(Metadata, Moo<Expression>, Moo<Expression>),
676
677    /// Multiplication over a pair of expressions (i.e. a * b) rather than a vec-expr like Expression::Product.
678    /// This is for compatibility with backends that do not support multiplication over vectors.
679    #[compatible(SMT)]
680    PairwiseProduct(Metadata, Moo<Expression>, Moo<Expression>),
681
682    #[compatible(JsonInput)]
683    Image(Metadata, Moo<Expression>, Moo<Expression>),
684
685    #[compatible(JsonInput)]
686    ImageSet(Metadata, Moo<Expression>, Moo<Expression>),
687
688    #[compatible(JsonInput)]
689    PreImage(Metadata, Moo<Expression>, Moo<Expression>),
690
691    #[compatible(JsonInput)]
692    Inverse(Metadata, Moo<Expression>, Moo<Expression>),
693
694    /// `permInverse(p)`: the inverse of permutation `p`, as a new permutation value. Not to be
695    /// confused with [`Expression::Inverse`], which is a boolean predicate over a pair of
696    /// permutations, not a value-returning operator.
697    #[compatible(JsonInput)]
698    PermInverse(Metadata, Moo<Expression>),
699
700    /// `compose(g, h)`: the permutation obtained by applying `h` then `g`, as a new permutation
701    /// value (`image(compose(g, h), i) = image(g, image(h, i))`).
702    #[compatible(JsonInput)]
703    Compose(Metadata, Moo<Expression>, Moo<Expression>),
704
705    #[compatible(JsonInput)]
706    Restrict(Metadata, Moo<Expression>, Moo<Expression>),
707
708    /// Lexicographical < between two matrices.
709    ///
710    /// A <lex B iff: A[i] < B[i] for some i /\ (A[j] > B[j] for some j -> i < j)
711    /// I.e. A must be less than B at some index i, and if it is greater than B at another index j,
712    /// then j comes after i.
713    /// I.e. A must be greater than B at the first index where they differ.
714    ///
715    /// E.g. [1, 1] <lex [2, 1] and [1, 1] <lex [1, 2]
716    LexLt(Metadata, Moo<Expression>, Moo<Expression>),
717
718    /// Lexicographical <= between two matrices
719    LexLeq(Metadata, Moo<Expression>, Moo<Expression>),
720
721    /// Lexicographical > between two matrices
722    /// This is a parser-level construct, and is immediately normalised to LexLt(b, a)
723    LexGt(Metadata, Moo<Expression>, Moo<Expression>),
724
725    /// Lexicographical >= between two matrices
726    /// This is a parser-level construct, and is immediately normalised to LexLeq(b, a)
727    LexGeq(Metadata, Moo<Expression>, Moo<Expression>),
728
729    /// Low-level minion constraint. See Expression::LexLt
730    FlatLexLt(Metadata, Vec<Atom>, Vec<Atom>),
731
732    /// Low-level minion constraint. See Expression::LexLeq
733    FlatLexLeq(Metadata, Vec<Atom>, Vec<Atom>),
734
735    /// Alters the shape of relations by projection
736    #[compatible(JsonInput)]
737    RelationProj(Metadata, Moo<Expression>, Vec<Option<Expression>>),
738
739    /// Cardinality of a collection type
740    #[compatible(JsonInput)]
741    Card(Metadata, Moo<Expression>),
742}
743
744// for the given matrix literal, return a bounded domain from the min to max of applying op to each
745// child expression.
746//
747// Op must be monotonic.
748//
749// Returns none if unbounded
750fn bounded_i32_domain_for_matrix_literal_monotonic(
751    e: &Expression,
752    op: fn(i32, i32) -> Option<i32>,
753) -> Option<DomainPtr> {
754    // only care about the elements, not the indices
755    let (mut exprs, _) = e.clone().unwrap_matrix_unchecked()?;
756    //
757    // here, I assume that op is monotone. This means that the bounds of op([a1,a2],[b1,b2])  for
758    // the ranges [a1,a2], [b1,b2] will be
759    // [min(op(a1,b1),op(a2,b1),op(a1,b2),op(a2,b2)),max(op(a1,b1),op(a2,b1),op(a1,b2),op(a2,b2))].
760    //
761    // We used to not assume this, and work out the bounds by applying op on the Cartesian product
762    // of A and B; however, this caused a combinatorial explosion and my computer to run out of
763    // memory (on the hakank_eprime_xkcd test)...
764    //Int
765    // For example, to find the bounds of the intervals [1,4], [1,5] combined using op, we used to do
766    //  [min(op(1,1), op(1,2),op(1,3),op(1,4),op(1,5),op(2,1)..
767    //
768    // +,-,/,* are all monotone, so this assumption should be fine for now...
769
770    let expr = exprs.pop()?;
771    let dom = expr.domain_of()?;
772    let resolved = dom.resolve().ok()?;
773    let GroundDomain::Int(ranges) = resolved.as_ref() else {
774        return None;
775    };
776
777    let (mut current_min, mut current_max) = range_vec_bounds_i32(ranges)?;
778
779    for expr in exprs {
780        let dom = expr.domain_of()?;
781        let resolved = dom.resolve().ok()?;
782        let GroundDomain::Int(ranges) = resolved.as_ref() else {
783            return None;
784        };
785
786        let (min, max) = range_vec_bounds_i32(ranges)?;
787
788        // all the possible new values for current_min / current_max
789        let minmax = op(min, current_max)?;
790        let minmin = op(min, current_min)?;
791        let maxmin = op(max, current_min)?;
792        let maxmax = op(max, current_max)?;
793        let vals = [minmax, minmin, maxmin, maxmax];
794
795        current_min = *vals
796            .iter()
797            .min()
798            .expect("vals iterator should not be empty, and should have a minimum.");
799        current_max = *vals
800            .iter()
801            .max()
802            .expect("vals iterator should not be empty, and should have a maximum.");
803    }
804
805    if current_min == current_max {
806        Some(Domain::int(vec![Range::Single(current_min)]))
807    } else {
808        Some(Domain::int(vec![Range::Bounded(current_min, current_max)]))
809    }
810}
811
812fn matrix_element_domain(e: &Expression) -> Option<DomainPtr> {
813    let (elem_domain, _) = e.domain_of()?.as_matrix()?;
814    elem_domain.as_ref().as_int()?;
815    Some(elem_domain)
816}
817
818fn empty_matrix_integer_element_domain(e: &Expression) -> Option<DomainPtr> {
819    match e {
820        Expression::TypeAnnotation(_, inner, domain) => {
821            if !Moo::unwrap_or_clone(inner.clone())
822                .unwrap_matrix_unchecked()
823                .is_some_and(|(elems, _)| elems.is_empty())
824            {
825                return None;
826            }
827            if let ReturnType::Matrix(elem_type) = domain.return_type()
828                && elem_type.as_ref() == &ReturnType::Int
829            {
830                Some(Domain::int_ground(vec![Range::Unbounded]))
831            } else {
832                None
833            }
834        }
835        Expression::DomainAnnotation(_, inner, domain) => {
836            if !Moo::unwrap_or_clone(inner.clone())
837                .unwrap_matrix_unchecked()
838                .is_some_and(|(elems, _)| elems.is_empty())
839            {
840                return None;
841            }
842            let (elem_domain, _) = domain.as_matrix()?;
843            elem_domain.as_ref().as_int()?;
844            Some(elem_domain)
845        }
846        _ => {
847            if !e
848                .clone()
849                .unwrap_matrix_unchecked()
850                .is_some_and(|(elems, _)| elems.is_empty())
851            {
852                return None;
853            }
854            matrix_element_domain(e)
855        }
856    }
857}
858
859// Returns none if unbounded
860fn range_vec_bounds_i32(ranges: &Vec<Range<i32>>) -> Option<(i32, i32)> {
861    let mut min = i32::MAX;
862    let mut max = i32::MIN;
863    for r in ranges {
864        match r {
865            Range::Single(i) => {
866                if *i < min {
867                    min = *i;
868                }
869                if *i > max {
870                    max = *i;
871                }
872            }
873            Range::Bounded(i, j) => {
874                if *i < min {
875                    min = *i;
876                }
877                if *j > max {
878                    max = *j;
879                }
880            }
881            Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => return None,
882        }
883    }
884    Some((min, max))
885}
886
887/// Integer domain bounds for [`Expression::Sum`] over a comprehension.
888fn sum_domain_for_comprehension(expr: &Expression) -> Option<DomainPtr> {
889    let Expression::Comprehension(_, comp) = expr else {
890        return None;
891    };
892    let comp = comp.as_ref();
893    let resolved = comp.return_expression.domain_of()?.resolve().ok()?;
894    let GroundDomain::Int(return_ranges) = resolved.as_ref() else {
895        return None;
896    };
897    let (term_min, term_max) = range_vec_bounds_i32(return_ranges)?;
898
899    let mut term_count = 1i64;
900    for qual in &comp.qualifiers {
901        match qual {
902            ComprehensionQualifier::Generator { ptr } => {
903                let count = ptr.domain()?.resolve().ok()?.length().ok()?;
904                term_count = term_count.saturating_mul(count as i64);
905                if term_count > i32::MAX as i64 {
906                    return None;
907                }
908            }
909            ComprehensionQualifier::Condition(_)
910            | ComprehensionQualifier::ExpressionGenerator { .. } => {
911                return None;
912            }
913        }
914    }
915
916    if term_count == 0 {
917        return Some(Domain::int(vec![Range::Single(0)]));
918    }
919
920    let n = term_count as i32;
921    let sum_min = n.saturating_mul(term_min);
922    let sum_max = n.saturating_mul(term_max);
923    if sum_min == sum_max {
924        Some(Domain::int(vec![Range::Single(sum_min)]))
925    } else {
926        Some(Domain::int(vec![Range::Bounded(sum_min, sum_max)]))
927    }
928}
929
930fn sum_domain_of_child(child: &Expression) -> Option<DomainPtr> {
931    sum_domain_for_comprehension(child).or_else(|| {
932        let (elements, _) = child.clone().unwrap_matrix_unchecked()?;
933        if elements.len() == 1 {
934            sum_domain_for_comprehension(&elements[0])
935        } else {
936            None
937        }
938    })
939}
940
941fn finite_mset_bounds(attrs: &MSetAttr<i32>, inner_len: i32) -> Option<((i32, i32), i32)> {
942    let min_size = attrs.size.low().copied().unwrap_or(0);
943    let max_from_size = attrs.size.high().copied();
944    let max_from_occurrence = attrs
945        .occurrence
946        .high()
947        .copied()
948        .and_then(|max| max.checked_mul(inner_len));
949    let max_size = match (max_from_size, max_from_occurrence) {
950        (Some(size), Some(occurrence)) => size.min(occurrence),
951        (Some(size), None) => size,
952        (None, Some(occurrence)) => occurrence,
953        (None, None) => return None,
954    };
955    let max_occurrence = attrs.occurrence.high().copied().unwrap_or(max_size);
956    Some(((min_size, max_size), max_occurrence))
957}
958
959/// The value-level union of two multisets adds their cardinalities and occurrence counts.
960///
961/// [`Domain::union`] instead computes a common type envelope and intentionally drops collection
962/// attributes, so it is too weak for the domain of an `a union b` expression.
963fn selected_representation(expr: &Expression) -> Option<String> {
964    match expr {
965        Expression::Atomic(_, Atom::Reference(reference)) => reference
966            .repr
967            .map(|rule| rule.short_name().to_owned())
968            .or_else(|| {
969                reference
970                    .domain_of()
971                    .representation_preference()
972                    .map(str::to_owned)
973            }),
974        Expression::TypeAnnotation(_, inner, domain)
975        | Expression::DomainAnnotation(_, inner, domain) => domain
976            .representation_preference()
977            .map(str::to_owned)
978            .or_else(|| selected_representation(inner)),
979        _ => None,
980    }
981}
982
983fn mset_union_result_domain(lhs: &Expression, rhs: &Expression) -> Option<DomainPtr> {
984    let lhs_selected = selected_representation(lhs);
985    let rhs_selected = selected_representation(rhs);
986    let lhs = lhs.domain_of()?.resolve().ok()?;
987    let rhs = rhs.domain_of()?.resolve().ok()?;
988    let GroundDomain::MSet(lhs_attrs, lhs_inner) = lhs.as_ref() else {
989        return None;
990    };
991    let GroundDomain::MSet(rhs_attrs, rhs_inner) = rhs.as_ref() else {
992        return None;
993    };
994
995    let lhs_len = i32::try_from(lhs_inner.length().ok()?).ok()?;
996    let rhs_len = i32::try_from(rhs_inner.length().ok()?).ok()?;
997    let ((lhs_min, lhs_max), lhs_max_occurrence) = finite_mset_bounds(lhs_attrs, lhs_len)?;
998    let ((rhs_min, rhs_max), rhs_max_occurrence) = finite_mset_bounds(rhs_attrs, rhs_len)?;
999
1000    let min_size = lhs_min.checked_add(rhs_min)?;
1001    let max_size = lhs_max.checked_add(rhs_max)?;
1002    let max_occurrence = lhs_max_occurrence.checked_add(rhs_max_occurrence)?;
1003    let size = Range::new(Some(min_size), Some(max_size));
1004    let occurrence = Range::new(Some(1), Some(max_occurrence));
1005    let representation = match (
1006        lhs_selected
1007            .as_deref()
1008            .or(lhs_attrs.representation.as_deref()),
1009        rhs_selected
1010            .as_deref()
1011            .or(rhs_attrs.representation.as_deref()),
1012    ) {
1013        (Some(lhs), Some(rhs)) if lhs == rhs => Some(lhs.to_owned()),
1014        (Some(preference), None) | (None, Some(preference)) => Some(preference.to_owned()),
1015        _ => None,
1016    };
1017    let inner = lhs_inner.union(rhs_inner).ok()?;
1018
1019    let mut attrs = MSetAttr::new(size, occurrence);
1020    attrs.representation = representation;
1021    Some(Domain::mset(attrs, DomainPtr::from(inner)))
1022}
1023
1024impl Expression {
1025    /// Returns the possible values of the expression, recursing to leaf expressions.
1026    ///
1027    /// Cached in this node's own `Metadata`: `domain_of_uncached` recomputes recursively from
1028    /// scratch with no memoisation of its own, so repeatedly calling `domain_of` on the same
1029    /// subtree (as rules that check "is this operand scalar/abstract" tend to, once per rule
1030    /// attempt) is quadratic in the worst case without a cache. The cache is invalidated when
1031    /// this expression or a descendant changes.
1032    pub fn domain_of(&self) -> Option<DomainPtr> {
1033        self.meta_ref().domain_or_init(|| self.domain_of_uncached())
1034    }
1035
1036    /// Bypasses the cache in `domain_of`. Needed by callers that read an expression stored
1037    /// *inside a declaration* (e.g. `Declaration::domain`, for `ValueLetting`/`QuantifiedExpr`):
1038    /// such expressions live outside the tree the rewrite engine walks to invalidate
1039    /// `Metadata::domain`, the same "reference embedded outside the standard rewrite tree" gap
1040    /// documented for `Reference::get_repr_as` and comprehension `ExpressionGenerator` sources.
1041    pub(crate) fn domain_of_uncached(&self) -> Option<DomainPtr> {
1042        match self {
1043            Expression::Union(_, a, b) => {
1044                let lhs = a.domain_of()?;
1045                let rhs = b.domain_of()?;
1046                mset_union_result_domain(a, b).or_else(|| lhs.union(&rhs).ok())
1047            }
1048            Expression::Intersect(_, a, b) => a.domain_of()?.intersect(&b.domain_of()?).ok(),
1049            // Removing elements can only shrink the left operand, never widen it.
1050            Expression::Difference(_, a, _) => a.domain_of(),
1051            Expression::In(_, _, _) => Some(Domain::bool()),
1052            Expression::Supset(_, _, _) => Some(Domain::bool()),
1053            Expression::SupsetEq(_, _, _) => Some(Domain::bool()),
1054            Expression::Subset(_, _, _) => Some(Domain::bool()),
1055            Expression::SubsetEq(_, _, _) => Some(Domain::bool()),
1056            Expression::AbstractLiteral(_, abslit) => abslit.domain_of(),
1057            Expression::DominanceRelation(_, _) => Some(Domain::bool()),
1058            Expression::FromSolution(_, expr) => Some(expr.domain_of()),
1059            Expression::Metavar(_, _) => None,
1060            Expression::Comprehension(_, comprehension) => comprehension.domain_of(),
1061            Expression::RecordField(_, rec, field_name) => {
1062                let rec_ents = rec.domain_of()?.as_record()?;
1063                for ent in rec_ents {
1064                    if ent.name.eq(field_name) {
1065                        return Some(ent.value);
1066                    }
1067                }
1068                None
1069            }
1070            Expression::UnsafeIndex(_, matrix, index) | Expression::SafeIndex(_, matrix, index) => {
1071                let dom = matrix.domain_of()?;
1072                let resolved_dom = dom.resolve().ok().map(Domain::Ground);
1073                if dom.as_matrix().is_some()
1074                    || resolved_dom
1075                        .as_ref()
1076                        .is_some_and(|dom| dom.as_matrix().is_some())
1077                {
1078                    // A matrix domain may be written flat -- `Matrix(int, [d1, d2])` -- or nested
1079                    // -- `Matrix(Matrix(int, [d2]), [d1])` -- and both mean the same thing. Each
1080                    // level consumes as many indices as it declares, so peel a level at a time
1081                    // until the indices run out rather than assuming one level covers them all.
1082                    let mut current = dom;
1083                    let mut remaining = index.len();
1084                    while remaining > 0 {
1085                        let resolved = current.resolve().ok().map(Domain::Ground);
1086                        let Some((elem_domain, idx_domains)) = current
1087                            .as_matrix()
1088                            .or_else(|| resolved.as_ref()?.as_matrix())
1089                        else {
1090                            break;
1091                        };
1092                        // Fewer indices than this level declares is a partial index: what is left
1093                        // is a matrix over the dimensions that were not given.
1094                        if idx_domains.len() > remaining {
1095                            return Some(Domain::matrix(
1096                                elem_domain,
1097                                idx_domains[remaining..].to_vec(),
1098                            ));
1099                        }
1100                        remaining -= idx_domains.len();
1101                        current = elem_domain;
1102                    }
1103                    return Some(current);
1104                }
1105
1106                // Indexing a tuple picks one component, so the domain is that component's --
1107                // known only when the position is a literal, as tuple components need not share
1108                // a domain.
1109                if let Some(components) =
1110                    dom.as_tuple().or_else(|| resolved_dom.as_ref()?.as_tuple())
1111                {
1112                    let Expression::Atomic(_, Atom::Literal(Literal::Int(index))) =
1113                        index.first()?
1114                    else {
1115                        return None;
1116                    };
1117                    let index: usize = (*index - 1).try_into().ok()?;
1118                    return components.get(index).cloned();
1119                }
1120
1121                if let Some(doms) = dom.as_variant().or(dom.as_record()).or_else(|| {
1122                    let resolved_dom = resolved_dom.as_ref()?;
1123                    resolved_dom.as_variant().or(resolved_dom.as_record())
1124                }) {
1125                    let index_expr = index.first()?;
1126                    return match index_expr {
1127                        Expression::Atomic(_, Atom::Reference(reference)) => {
1128                            for inner_dom in doms {
1129                                if *reference.name() == inner_dom.name {
1130                                    return Some(inner_dom.value);
1131                                }
1132                            }
1133                            None
1134                        }
1135                        Expression::Atomic(_, Atom::Literal(Literal::Int(index))) => {
1136                            let index: usize = (*index - 1).try_into().ok()?;
1137                            doms.get(index).map(|inner_dom| inner_dom.value.clone())
1138                        }
1139                        _ => None,
1140                    };
1141                }
1142
1143                bug!(
1144                    "subject of an index operation should support indexing, but got {matrix}: {dom}"
1145                )
1146            }
1147            Expression::UnsafeSlice(_, matrix, indices)
1148            | Expression::SafeSlice(_, matrix, indices) => {
1149                let sliced_dimension = indices.iter().position(Option::is_none);
1150
1151                let dom = matrix.domain_of()?;
1152                let Some((elem_domain, index_domains)) = dom.as_matrix() else {
1153                    bug!("subject of an index operation should be a matrix");
1154                };
1155
1156                match sliced_dimension {
1157                    Some(dimension) => Some(Domain::matrix(
1158                        elem_domain,
1159                        vec![index_domains[dimension].clone()],
1160                    )),
1161
1162                    // same as index
1163                    None => Some(elem_domain),
1164                }
1165            }
1166            Expression::InDomain(_, _, _) => Some(Domain::bool()),
1167            Expression::Atomic(_, atom) => Some(atom.domain_of()),
1168            Expression::TypeAnnotation(_, expr, _) => expr.domain_of(),
1169            Expression::DomainAnnotation(_, _, domain) => Some(domain.clone()),
1170            Expression::Sum(_, e) => sum_domain_of_child(e).or_else(|| {
1171                // Fall back when corner products of bounds overflow i32.
1172                bounded_i32_domain_for_matrix_literal_monotonic(e, i32::checked_add)
1173            }),
1174            Expression::Product(_, e) => {
1175                // Grocery-style products (e.g. four int(0..711) factors) overflow i32;
1176                // return None so callers can omit a precise product domain rather than panic.
1177                bounded_i32_domain_for_matrix_literal_monotonic(e, i32::checked_mul)
1178            }
1179            Expression::Min(_, e) => {
1180                if empty_matrix_integer_element_domain(e).is_some() {
1181                    return Some(Domain::empty(ReturnType::Int));
1182                }
1183                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
1184                    Some(if x < y { x } else { y })
1185                })
1186                .or_else(|| matrix_element_domain(e))
1187            }
1188            Expression::Max(_, e) => {
1189                if empty_matrix_integer_element_domain(e).is_some() {
1190                    return Some(Domain::empty(ReturnType::Int));
1191                }
1192                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
1193                    Some(if x > y { x } else { y })
1194                })
1195                .or_else(|| matrix_element_domain(e))
1196            }
1197            Expression::UnsafeDiv(_, a, b) => a
1198                .domain_of()?
1199                .resolve()
1200                .ok()?
1201                .apply_i32(
1202                    // rust integer division is truncating; however, we want to always round down,
1203                    // including for negative numbers.
1204                    |x, y| {
1205                        if y != 0 {
1206                            Some((x as f32 / y as f32).floor() as i32)
1207                        } else {
1208                            None
1209                        }
1210                    },
1211                    b.domain_of()?.resolve().ok()?.as_ref(),
1212                )
1213                .map(DomainPtr::from)
1214                .ok(),
1215            // Either operand's value can survive, so the domain must cover both.
1216            Expression::CatchUndef(_, a, b) => {
1217                let inner = a.domain_of()?;
1218                let default = b.domain_of()?;
1219                inner.union(&default).ok()
1220            }
1221            Expression::SafeDiv(_, a, b) => {
1222                // rust integer division is truncating; however, we want to always round down
1223                // including for negative numbers.
1224                let domain = a
1225                    .domain_of()?
1226                    .resolve()
1227                    .ok()?
1228                    .apply_i32(
1229                        |x, y| {
1230                            if y != 0 {
1231                                Some((x as f32 / y as f32).floor() as i32)
1232                            } else {
1233                                None
1234                            }
1235                        },
1236                        b.domain_of()?.resolve().ok()?.as_ref(),
1237                    )
1238                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
1239
1240                if let GroundDomain::Int(ranges) = domain {
1241                    let mut ranges = ranges;
1242                    ranges.push(Range::Single(0));
1243                    Some(Domain::int(ranges))
1244                } else if matches!(domain, GroundDomain::Empty(ReturnType::Int)) {
1245                    Some(Domain::int(vec![Range::Single(0)]))
1246                } else {
1247                    bug!("Domain of {self} was not integer")
1248                }
1249            }
1250            Expression::UnsafeMod(_, a, b) => a
1251                .domain_of()?
1252                .resolve()
1253                .ok()?
1254                .apply_i32(
1255                    |x, y| if y != 0 { Some(x % y) } else { None },
1256                    b.domain_of()?.resolve().ok()?.as_ref(),
1257                )
1258                .map(DomainPtr::from)
1259                .ok(),
1260            Expression::SafeMod(_, a, b) => {
1261                let domain = a
1262                    .domain_of()?
1263                    .resolve()
1264                    .ok()?
1265                    .apply_i32(
1266                        |x, y| if y != 0 { Some(x % y) } else { None },
1267                        b.domain_of()?.resolve().ok()?.as_ref(),
1268                    )
1269                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
1270
1271                if let GroundDomain::Int(ranges) = domain {
1272                    let mut ranges = ranges;
1273                    ranges.push(Range::Single(0));
1274                    Some(Domain::int(ranges))
1275                } else if matches!(domain, GroundDomain::Empty(ReturnType::Int)) {
1276                    Some(Domain::int(vec![Range::Single(0)]))
1277                } else {
1278                    bug!("Domain of {self} was not integer")
1279                }
1280            }
1281            Expression::SafePow(_, a, b) | Expression::UnsafePow(_, a, b) => a
1282                .domain_of()?
1283                .resolve()
1284                .ok()?
1285                .apply_i32(
1286                    |x, y| {
1287                        if (x != 0 || y != 0) && y >= 0 {
1288                            Some(x.pow(y as u32))
1289                        } else {
1290                            None
1291                        }
1292                    },
1293                    b.domain_of()?.resolve().ok()?.as_ref(),
1294                )
1295                .map(DomainPtr::from)
1296                .ok(),
1297            Expression::Root(_, _) => None,
1298            Expression::Bubble(_, inner, _) => inner.domain_of(),
1299            Expression::AuxDeclaration(_, _, _) => Some(Domain::bool()),
1300            Expression::And(_, _) => Some(Domain::bool()),
1301            Expression::Not(_, _) => Some(Domain::bool()),
1302            Expression::Or(_, _) => Some(Domain::bool()),
1303            Expression::Imply(_, _, _) => Some(Domain::bool()),
1304            Expression::Iff(_, _, _) => Some(Domain::bool()),
1305            Expression::Eq(_, _, _) => Some(Domain::bool()),
1306            Expression::Neq(_, _, _) => Some(Domain::bool()),
1307            Expression::Geq(_, _, _) => Some(Domain::bool()),
1308            Expression::Leq(_, _, _) => Some(Domain::bool()),
1309            Expression::Gt(_, _, _) => Some(Domain::bool()),
1310            Expression::Lt(_, _, _) => Some(Domain::bool()),
1311            Expression::Factorial(_, a) => {
1312                let dom = a.domain_of()?.resolve().ok()?;
1313                let GroundDomain::Int(_) = dom.as_ref() else {
1314                    return None;
1315                };
1316                let values = dom.values_i32().ok()?;
1317                let values = values
1318                    .into_iter()
1319                    .map(factorial_i32)
1320                    .collect::<Option<BTreeSet<_>>>()?;
1321
1322                Some(DomainPtr::from(GroundDomain::from_set_i32(&values)))
1323            }
1324            Expression::FlatAbsEq(_, _, _) => Some(Domain::bool()),
1325            Expression::FlatMinEq(_, _, _) => Some(Domain::bool()),
1326            Expression::FlatSumGeq(_, _, _) => Some(Domain::bool()),
1327            Expression::FlatSumLeq(_, _, _) => Some(Domain::bool()),
1328            Expression::MinionDivEqUndefZero(_, _, _, _) => Some(Domain::bool()),
1329            Expression::MinionModuloEqUndefZero(_, _, _, _) => Some(Domain::bool()),
1330            Expression::FlatIneq(_, _, _, _) => Some(Domain::bool()),
1331            Expression::Flatten(_, n, m) => {
1332                if let Some(expr) = n {
1333                    if expr.return_type() == ReturnType::Int {
1334                        // TODO: handle flatten with depth argument
1335                        return None;
1336                    }
1337                } else {
1338                    // TODO: currently only works for matrices
1339                    let dom = m.domain_of()?.resolve().ok()?;
1340                    let (val_dom, idx_doms) = match dom.as_ref() {
1341                        GroundDomain::Matrix(val, idx) => (val, idx),
1342                        _ => return None,
1343                    };
1344                    let num_elems = matrix::num_elements(idx_doms).ok()? as i32;
1345
1346                    let new_index_domain = Domain::int(vec![Range::Bounded(1, num_elems)]);
1347                    return Some(Domain::matrix(
1348                        val_dom.clone().into(),
1349                        vec![new_index_domain],
1350                    ));
1351                }
1352                None
1353            }
1354            Expression::AllDiff(_, _) => Some(Domain::bool()),
1355            Expression::SmtDistinct(_, _) => Some(Domain::bool()),
1356            Expression::AllDifferentExcept(_, _, _) => Some(Domain::bool()),
1357            Expression::ElementId(_, matrix, value) => {
1358                let dom = matrix.domain_of()?.resolve().ok()?;
1359                let idx_doms = match dom.as_ref() {
1360                    GroundDomain::Matrix(_, idx) => idx,
1361                    _ => return None,
1362                };
1363                if let [idx_dom] = idx_doms.as_slice() {
1364                    let index_domain = Domain::Ground(idx_dom.clone());
1365                    if let Some(n) = matrix.list_len() {
1366                        if n > 0 {
1367                            return Some(Domain::int(vec![Range::Bounded(1, n as i32)]));
1368                        }
1369                        Some(Moo::new(index_domain))
1370                    } else {
1371                        let matrix_is_literal = matches!(
1372                            matrix.as_ref(),
1373                            Expression::Atomic(_, Atom::Literal(_))
1374                                | Expression::AbstractLiteral(_, _)
1375                        );
1376                        if !matrix_is_literal || eval_constant(value.as_ref()).is_none() {
1377                            value
1378                                .domain_of()
1379                                .and_then(|value_domain| index_domain.union(&value_domain).ok())
1380                                .map(Moo::new)
1381                                .or_else(|| Some(Moo::new(index_domain)))
1382                        } else {
1383                            Some(Moo::new(index_domain))
1384                        }
1385                    }
1386                } else {
1387                    let num_elems = matrix::num_elements(idx_doms).ok()? as i32;
1388                    Some(Domain::int(vec![Range::Bounded(1, num_elems)]))
1389                }
1390            }
1391            Expression::Table(_, _, _) => Some(Domain::bool()),
1392            Expression::NegativeTable(_, _, _) => Some(Domain::bool()),
1393            Expression::AtLeast(_, _, _, _) => Some(Domain::bool()),
1394            Expression::AtMost(_, _, _, _) => Some(Domain::bool()),
1395            Expression::Gcc(_, _, _, _) | Expression::GccWeak(_, _, _, _) => Some(Domain::bool()),
1396            Expression::FlatWatchedLiteral(_, _, _) => Some(Domain::bool()),
1397            Expression::MinionReify(_, _, _) => Some(Domain::bool()),
1398            Expression::MinionReifyImply(_, _, _) => Some(Domain::bool()),
1399            Expression::MinionWInIntervalSet(_, _, _) => Some(Domain::bool()),
1400            Expression::MinionWInSet(_, _, _) => Some(Domain::bool()),
1401            Expression::MinionElementOne(_, _, _, _) => Some(Domain::bool()),
1402            Expression::Neg(_, x) => {
1403                let dom = x.domain_of()?;
1404                let mut ranges = dom.as_int()?;
1405
1406                ranges = ranges
1407                    .into_iter()
1408                    .map(|r| match r {
1409                        Range::Single(x) => Range::Single(-x),
1410                        Range::Bounded(x, y) => Range::Bounded(-y, -x),
1411                        Range::UnboundedR(i) => Range::UnboundedL(-i),
1412                        Range::UnboundedL(i) => Range::UnboundedR(-i),
1413                        Range::Unbounded => Range::Unbounded,
1414                    })
1415                    .collect();
1416
1417                Some(Domain::int(ranges))
1418            }
1419            Expression::Minus(_, a, b) => {
1420                let a_resolved = a.domain_of()?.resolve().ok()?;
1421                let b_resolved = b.domain_of()?.resolve().ok()?;
1422
1423                if matches!(a_resolved.as_ref(), GroundDomain::Int(_))
1424                    && matches!(b_resolved.as_ref(), GroundDomain::Int(_))
1425                {
1426                    a_resolved
1427                        .apply_i32(|x, y| Some(x - y), b_resolved.as_ref())
1428                        .map(DomainPtr::from)
1429                        .ok()
1430                } else if matches!(a_resolved.as_ref(), GroundDomain::Set(_, _))
1431                    && matches!(b_resolved.as_ref(), GroundDomain::Set(_, _))
1432                {
1433                    Some(DomainPtr::from(a_resolved))
1434                } else {
1435                    None
1436                }
1437            }
1438            Expression::FlatAllDiff(_, _) => Some(Domain::bool()),
1439            Expression::FlatMinusEq(_, _, _) => Some(Domain::bool()),
1440            Expression::FlatProductEq(_, _, _, _) => Some(Domain::bool()),
1441            Expression::FlatWeightedSumLeq(_, _, _, _) => Some(Domain::bool()),
1442            Expression::FlatWeightedSumGeq(_, _, _, _) => Some(Domain::bool()),
1443            Expression::Abs(_, a) => a
1444                .domain_of()?
1445                .resolve()
1446                .ok()?
1447                .apply_i32(
1448                    |a, _| Some(a.abs()),
1449                    a.domain_of()?.resolve().ok()?.as_ref(),
1450                )
1451                .map(DomainPtr::from)
1452                .ok(),
1453            Expression::MinionPow(_, _, _, _) => Some(Domain::bool()),
1454            Expression::ToInt(_, _) => Some(Domain::int(vec![Range::Bounded(0, 1)])),
1455            Expression::SATInt(_, _, _, (low, high)) => {
1456                Some(Domain::int_ground(vec![Range::Bounded(*low, *high)]))
1457            }
1458            Expression::PairwiseSum(_, a, b) => a
1459                .domain_of()?
1460                .resolve()
1461                .ok()?
1462                .apply_i32(|a, b| Some(a + b), b.domain_of()?.resolve().ok()?.as_ref())
1463                .map(DomainPtr::from)
1464                .ok(),
1465            Expression::PairwiseProduct(_, a, b) => a
1466                .domain_of()?
1467                .resolve()
1468                .ok()?
1469                .apply_i32(|a, b| Some(a * b), b.domain_of()?.resolve().ok()?.as_ref())
1470                .map(DomainPtr::from)
1471                .ok(),
1472            Expression::Defined(_, function) => {
1473                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
1474                let size = Self::function_elements_size(attrs, &domain, &codomain);
1475                if let Some(size) = size {
1476                    Some(Domain::set(SetAttr::new(size), domain))
1477                } else {
1478                    Some(Domain::empty(ReturnType::Set(Box::new(
1479                        domain.return_type(),
1480                    ))))
1481                }
1482            }
1483            Expression::Range(_, function) => {
1484                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
1485                let jectivity = attrs.resolve().ok()?.jectivity;
1486
1487                let size_size = attrs.resolve().ok()?.size;
1488                let size_size = match size_size {
1489                    Range::Unbounded => Range::UnboundedR(0),
1490                    // If lower bound we can guarantee one mapping (unless size = 0)
1491                    Range::Single(x) => match jectivity {
1492                        JectivityAttr::Injective | JectivityAttr::Surjective => Range::Single(x),
1493                        _ => Range::Bounded(Ord::min(1, x), x),
1494                    },
1495                    // Upper bound guarantees the same upper bound
1496                    Range::UnboundedL(x) => Range::Bounded(0, x),
1497                    // If not bounded by 0 can guarantee min 1
1498                    Range::UnboundedR(x) => match jectivity {
1499                        JectivityAttr::Injective | JectivityAttr::Surjective => {
1500                            Range::UnboundedR(x)
1501                        }
1502                        _ => Range::UnboundedR(Ord::min(1, x)),
1503                    },
1504                    Range::Bounded(x, y) => Range::Bounded(Ord::min(1, x), y),
1505                };
1506
1507                // Gets the size imposed by the partiality and jectivity attributes
1508                let partiality = attrs.resolve().ok()?.partiality;
1509                let codomain_length = codomain.length_signed();
1510                let attr_size = match jectivity {
1511                    // Bijective and surjective functions must have every element in the codomain mapped to
1512                    JectivityAttr::Bijective | JectivityAttr::Surjective => match codomain_length {
1513                        Ok(co_len) => Some(Range::Single(co_len)),
1514                        Err(_) => None,
1515                    },
1516                    JectivityAttr::Injective => {
1517                        let domain_length = domain.length_signed();
1518                        match domain_length {
1519                            Ok(len) => match codomain_length {
1520                                Ok(co_len) => match partiality {
1521                                    // When its injective we can guarantee 1 to 1, so the maximum domain length is a single bound
1522                                    PartialityAttr::Total => {
1523                                        Some(Range::Single(Ord::min(len, co_len)))
1524                                    }
1525                                    PartialityAttr::Partial => {
1526                                        Some(Range::Bounded(0, Ord::min(len, co_len)))
1527                                    }
1528                                },
1529                                Err(_) => None,
1530                            },
1531                            Err(_) => None,
1532                        }
1533                    }
1534                    JectivityAttr::None => {
1535                        let domain_length = domain.length_signed();
1536                        match domain_length {
1537                            // This is the general case, where we know there cannot be more codomain elements mapped to that domain elements
1538                            Ok(len) => Some(Range::Bounded(0, len)),
1539                            Err(_) => None,
1540                        }
1541                    }
1542                };
1543
1544                let size = match attr_size {
1545                    Some(attr_size) => {
1546                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
1547                        match unsafe_range {
1548                            Ok(range) => range,
1549                            Err(_) => {
1550                                return Some(Domain::empty(ReturnType::Set(Box::new(
1551                                    domain.return_type(),
1552                                ))));
1553                            }
1554                        }
1555                    }
1556                    None => size_size,
1557                };
1558                Some(Domain::set(SetAttr::new(size), codomain))
1559            }
1560            Expression::Image(_, function, _) => get_function_codomain(function),
1561            Expression::PermInverse(_, perm) => perm.domain_of(),
1562            Expression::Compose(_, g, _h) => g.domain_of(),
1563            Expression::ImageSet(_, function, _) => {
1564                let codomain = get_function_codomain(function);
1565                // An imageSet is the converted to a set, and can be empty
1566                codomain.map(|inner_dom| Domain::set(SetAttr::new(Range::Bounded(0, 1)), inner_dom))
1567            }
1568            Expression::PreImage(_, function, _) => {
1569                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
1570
1571                let size_size = attrs.resolve().ok()?.size;
1572                let size_size = match size_size {
1573                    // Our only guarantee is an upper bound is the same
1574                    Range::Unbounded => Range::UnboundedR(0),
1575                    Range::Single(x) => Range::Bounded(0, x),
1576                    Range::UnboundedL(x) => Range::Bounded(0, x),
1577                    Range::UnboundedR(_) => Range::UnboundedR(0),
1578                    Range::Bounded(_, y) => Range::Bounded(0, y),
1579                };
1580
1581                let jectivity = attrs.resolve().ok()?.jectivity;
1582                let codomain_length = codomain.length_signed();
1583                let attr_size = match jectivity {
1584                    // When there is 1-to-1 mapping we can guarantee no more than 1 occurrence
1585                    JectivityAttr::Bijective => Some(Range::Single(1)),
1586                    JectivityAttr::Injective => match size_size {
1587                        Range::Single(x) | Range::UnboundedL(x) | Range::Bounded(x, _) => {
1588                            match codomain_length {
1589                                Ok(co_len) => {
1590                                    if x >= co_len {
1591                                        Some(Range::Single(1))
1592                                    } else {
1593                                        Some(Range::Bounded(0, 1))
1594                                    }
1595                                }
1596                                Err(_) => Some(Range::Bounded(0, 1)),
1597                            }
1598                        }
1599                        _ => Some(Range::Bounded(0, 1)),
1600                    },
1601                    JectivityAttr::Surjective => {
1602                        let domain_length = domain.length_signed();
1603                        match domain_length {
1604                            Ok(len) => match codomain_length {
1605                                // We know the element is mapped but not how many times
1606                                // Every element must be mapped so it cannot be every element of domain
1607                                Ok(co_len) => match size_size {
1608                                    Range::Bounded(_, x)
1609                                    | Range::UnboundedL(x)
1610                                    | Range::Single(x) => Some(Range::Bounded(
1611                                        1,
1612                                        Ord::max(Ord::min(len, x) - co_len + 1, 0),
1613                                    )),
1614                                    _ => Some(Range::Bounded(1, Ord::max(len - co_len + 1, 0))),
1615                                },
1616                                Err(_) => Some(Range::UnboundedR(1)),
1617                            },
1618                            Err(_) => Some(Range::UnboundedR(1)),
1619                        }
1620                    }
1621                    JectivityAttr::None => {
1622                        let domain_length = domain.length_signed();
1623                        match domain_length {
1624                            Ok(len) => Some(Range::Bounded(0, len)),
1625                            Err(_) => Some(Range::UnboundedR(0)),
1626                        }
1627                    }
1628                };
1629
1630                let size = match attr_size {
1631                    Some(attr_size) => {
1632                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
1633                        match unsafe_range {
1634                            Ok(range) => range,
1635                            Err(_) => {
1636                                return Some(Domain::empty(ReturnType::Set(Box::new(
1637                                    domain.return_type(),
1638                                ))));
1639                            }
1640                        }
1641                    }
1642                    None => size_size,
1643                };
1644                Some(Domain::set(SetAttr::new(size), domain))
1645            }
1646            Expression::Restrict(_, function, new_domain) => {
1647                let mut domain = function.domain_of()?;
1648                let (attrs_mut, dom, codom_mut) = domain.as_function_mut()?;
1649
1650                // Stops other references being mutable
1651                let attrs: &FuncAttr<IntVal> = attrs_mut;
1652                let codom: &Moo<Domain> = codom_mut;
1653
1654                // Gets the minimal range between the old domain and new domain
1655                let mut new_dom = new_domain.domain_of()?;
1656                // If domains cannot be resolved we just stick to the restricted one
1657                if let Some(new_rng) = new_dom.as_int_ground_mut()
1658                    && let Some(old_rng) = dom.as_int_ground_mut()
1659                {
1660                    new_rng.append(old_rng);
1661                    if let Ok(rng) = Range::minimal(new_rng) {
1662                        let ranges = vec![rng];
1663                        new_dom = Domain::int(ranges);
1664                    }
1665                }
1666                let attr_size = attrs.resolve().ok()?.size;
1667                let new_size = match new_dom.length_signed() {
1668                    // Combines current size attributes with length of new domain
1669                    Ok(len) => match Range::minimal(&[attr_size, Range::Bounded(0, len)]) {
1670                        Ok(size) => size,
1671                        Err(_) => {
1672                            // Means the restriction is impossible
1673                            return Some(Domain::empty(ReturnType::Function(
1674                                Box::new(new_dom.return_type()),
1675                                Box::new(codom.return_type()),
1676                            )));
1677                        }
1678                    },
1679                    Err(_) => attr_size,
1680                };
1681                let jectivity = attrs.jectivity.clone();
1682                let partiality = attrs.partiality.clone();
1683                let new_attrs = FuncAttr {
1684                    size: new_size,
1685                    jectivity,
1686                    partiality,
1687                };
1688                Some(Domain::function(new_attrs, new_dom, codom.clone()))
1689            }
1690            Expression::Subsequence(_, _, _) => Some(Domain::bool()),
1691            Expression::Substring(_, _, _) => Some(Domain::bool()),
1692            Expression::AttributeAsConstraint(_, _, _, _) => Some(Domain::bool()),
1693            Expression::Inverse(..) => Some(Domain::bool()),
1694            Expression::LexLt(..) => Some(Domain::bool()),
1695            Expression::LexLeq(..) => Some(Domain::bool()),
1696            Expression::LexGt(..) => Some(Domain::bool()),
1697            Expression::LexGeq(..) => Some(Domain::bool()),
1698            Expression::FlatLexLt(..) => Some(Domain::bool()),
1699            Expression::FlatLexLeq(..) => Some(Domain::bool()),
1700            Expression::Active(..) => Some(Domain::bool()),
1701            Expression::ToSet(_, other) => {
1702                if let Some((attrs, dom, codom)) = other.domain_of()?.as_function() {
1703                    let set_attrs = SetAttr::new(attrs.size);
1704                    Some(Domain::set(set_attrs, Domain::tuple(vec![dom, codom])))
1705                } else if let Some((attrs, doms)) = other.domain_of()?.as_relation() {
1706                    let set_attrs = SetAttr::new(attrs.size);
1707                    Some(Domain::set(set_attrs, Domain::tuple(doms)))
1708                } else if let Some((attrs, dom)) = other.domain_of()?.as_mset() {
1709                    let set_attrs = SetAttr::new(attrs.size);
1710                    Some(Domain::set(set_attrs, dom))
1711                } else if let Some((dom, dimensions)) = other.domain_of()?.as_matrix() {
1712                    // We combine all matrix domains into a tuple
1713                    let mut doms = vec![];
1714                    for _ in dimensions {
1715                        doms.push(dom.clone());
1716                    }
1717                    let doms_sizes: Result<Vec<i32>, _> =
1718                        doms.iter().map(|x| x.length_signed()).collect();
1719                    let attr = match doms_sizes {
1720                        Ok(vals) => {
1721                            if let Some(&size) = vals.iter().min() {
1722                                SetAttr::new(Range::Single(size))
1723                            } else {
1724                                SetAttr::<i32>::default()
1725                            }
1726                        }
1727                        // We do not know the ground dimensions yet so default is chosen
1728                        Err(_) => SetAttr::<i32>::default(),
1729                    };
1730                    Some(Domain::set(attr, Domain::tuple(doms)))
1731                } else {
1732                    bug!(
1733                        "Domain of {self} needed to be a function, relation, mset, or matrix for ToSet"
1734                    )
1735                }
1736            }
1737            Expression::ToMSet(_, other) => {
1738                if let Some((attrs, dom, codom)) = other.domain_of()?.as_function() {
1739                    let set_attrs = MSetAttr {
1740                        size: attrs.size,
1741                        occurrence: Range::Single(IntVal::Const(1)),
1742                        representation: None,
1743                    };
1744                    Some(Domain::mset(set_attrs, Domain::tuple(vec![dom, codom])))
1745                } else if let Some((attrs, doms)) = other.domain_of()?.as_relation() {
1746                    let set_attrs = MSetAttr {
1747                        size: attrs.size,
1748                        occurrence: Range::Single(IntVal::Const(1)),
1749                        representation: None,
1750                    };
1751                    Some(Domain::mset(set_attrs, Domain::tuple(doms)))
1752                } else if let Some((attrs, dom)) = other.domain_of()?.as_set() {
1753                    let set_attrs = MSetAttr {
1754                        size: attrs.size,
1755                        occurrence: Range::Single(IntVal::Const(1)),
1756                        representation: None,
1757                    };
1758                    Some(Domain::mset(set_attrs, dom))
1759                } else {
1760                    bug!("Domain of {self} needed to be a function, relation, or set for ToMSet")
1761                }
1762            }
1763            Expression::ToRelation(_, function) => {
1764                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
1765                // Function attributes apply to the relation
1766                let rel_attrs = RelAttr {
1767                    size: attrs.size,
1768                    binary: vec![],
1769                };
1770                Some(Domain::relation(rel_attrs, vec![domain, codomain]))
1771            }
1772            Expression::RelationProj(_, relation, projections) => {
1773                let (_, domains) = relation.domain_of()?.as_relation()?;
1774                let new_doms = domains
1775                    .iter()
1776                    .zip(projections.iter())
1777                    .filter_map(|(domain, included)| {
1778                        if included.is_none() {
1779                            // The domains corresponding to projections which are None remain in the relation
1780                            Some(domain.clone())
1781                        } else {
1782                            None
1783                        }
1784                    })
1785                    .collect();
1786                Some(Domain::relation(RelAttr::<IntVal>::default(), new_doms))
1787            }
1788            Expression::Apart(_, _, _) => Some(Domain::bool()),
1789            Expression::Together(_, _, _) => Some(Domain::bool()),
1790            Expression::Participants(_, p) => {
1791                // Every single element of the domain _must_ be in the set, so fixed size on that.
1792                let (attr, inner) = p.domain_of()?.as_partition()?;
1793                let len = inner.length_signed().ok()?;
1794
1795                let p_parts = attr.resolve().ok()?.num_parts;
1796                let p_card = attr.resolve().ok()?.part_len;
1797
1798                // if
1799                match (p_parts.low(), p_parts.high(), p_card.low(), p_card.high()) {
1800                    (Some(p), Some(q), Some(r), Some(s)) => {
1801                        let lo = p * r;
1802                        let hi = q * s;
1803                        if len < lo || len > hi {
1804                            return Some(Domain::empty(ReturnType::Set(Box::new(
1805                                inner.return_type(),
1806                            ))));
1807                        }
1808                    }
1809
1810                    (None, Some(q), None, Some(s)) => {
1811                        let hi = q * s;
1812                        if len > hi {
1813                            return Some(Domain::empty(ReturnType::Set(Box::new(
1814                                inner.return_type(),
1815                            ))));
1816                        }
1817                    }
1818
1819                    (Some(p), None, Some(r), None) => {
1820                        let lo = p * r;
1821                        if len < lo {
1822                            return Some(Domain::empty(ReturnType::Set(Box::new(
1823                                inner.return_type(),
1824                            ))));
1825                        }
1826                    }
1827
1828                    _ => {}
1829                }
1830
1831                Some(Domain::set(
1832                    SetAttr::new_size(len),
1833                    Domain::int(inner.as_int()?),
1834                ))
1835            }
1836            Expression::Party(_, _, p) => {
1837                // Will pick a part, so set will share same attrs
1838                let (attr, inner) = p.domain_of()?.as_partition()?;
1839
1840                Some(Domain::set(SetAttr::new(attr.part_len), inner))
1841            }
1842            Expression::Parts(_, p) => {
1843                let (attr, inner) = p.domain_of()?.as_partition()?;
1844
1845                Some(Domain::set(
1846                    SetAttr::new(attr.num_parts.clone()),
1847                    Domain::set(SetAttr::new(attr.part_len), inner),
1848                ))
1849            }
1850            Expression::Card(_, collection) => {
1851                let domain = collection.domain_of()?;
1852                if let Some((_, dimensions)) = domain.as_matrix() {
1853                    let doms_ground: Result<Vec<i32>, _> =
1854                        dimensions.iter().map(|x| x.length_signed()).collect();
1855                    if let Ok(doms_ground) = doms_ground {
1856                        let size: Range<i32> = Range::Single(doms_ground.iter().product());
1857                        Some(Domain::int(vec![size]))
1858                    } else {
1859                        Some(Domain::int(vec![Range::<i32>::Unbounded]))
1860                    }
1861                } else if let Some((attr, dom)) = domain.as_set() {
1862                    let attr_size = attr.resolve().ok()?.size;
1863                    if let Ok(length) = dom.length_signed() {
1864                        let unsafe_range = Range::minimal(&[attr_size, Range::Bounded(0, length)]);
1865                        return match unsafe_range {
1866                            Ok(range) => Some(Domain::int(vec![range])),
1867                            Err(_) => None,
1868                        };
1869                    }
1870                    // If the domain is not known we just need to go off of attributes
1871                    Some(Domain::int(vec![attr_size]))
1872                } else if let Some((attrs, dom)) = domain.as_mset() {
1873                    let attrs_gd = attrs.resolve().ok()?;
1874                    // Gets maximum value of the occurrence
1875                    let attr_occ = match attrs_gd.occurrence {
1876                        Range::Single(x) => Some(x),
1877                        Range::Unbounded | Range::UnboundedR(_) => None,
1878                        Range::Bounded(_, x) => Some(x),
1879                        Range::UnboundedL(x) => Some(x),
1880                    };
1881                    if let Some(occ) = attr_occ {
1882                        if let Ok(length) = dom.length_signed() {
1883                            let unsafe_range =
1884                                Range::minimal(&[attrs_gd.size, Range::Bounded(0, length * occ)]);
1885                            match unsafe_range {
1886                                Ok(range) => Some(Domain::int(vec![range])),
1887                                Err(_) => None,
1888                            }
1889                        } else {
1890                            // If the domain is not known we just need to go off of attributes
1891                            Some(Domain::int(vec![attrs_gd.size]))
1892                        }
1893                    } else {
1894                        // If no occurrence is provided then it must have bounded size
1895                        Some(Domain::int(vec![attrs_gd.size]))
1896                    }
1897                } else if let Some((attrs, doms)) = domain.as_relation() {
1898                    // TODO: Further inference may be possible using the binary attributes
1899
1900                    let attrs_gd = attrs.resolve().ok()?;
1901                    // See if all domains are ground
1902                    let doms_sizes: Result<Vec<i32>, _> =
1903                        doms.iter().map(|x| x.length_signed()).collect();
1904                    if let Ok(doms_sizes) = doms_sizes {
1905                        let length = Range::Bounded(0, doms_sizes.iter().product());
1906                        // Combine the attributes and the domain possibilities
1907                        let unsafe_range = Range::minimal(&[attrs_gd.size, length]);
1908                        return match unsafe_range {
1909                            Ok(range) => Some(Domain::int(vec![range])),
1910                            Err(_) => None,
1911                        };
1912                    }
1913                    // If the domain is not known we just need to go off of attributes
1914                    Some(Domain::int(vec![attrs_gd.size]))
1915                } else if let Some((attrs, dom, codom)) = domain.as_function() {
1916                    let size = Self::function_elements_size(attrs, &dom, &codom);
1917                    size.map(|size| Domain::int(vec![size]))
1918                } else if let Some((attrs, _dom)) = domain.as_sequence() {
1919                    // Unlike a set/mset, a sequence's length is not bounded by its inner
1920                    // domain's size (repeated elements are allowed), so the attribute's own
1921                    // size range is already the tightest bound available here.
1922                    let attrs_gd = attrs.resolve().ok()?;
1923                    Some(Domain::int(vec![attrs_gd.size]))
1924                } else if let Some((_attrs, inner)) = domain.as_partition() {
1925                    // A partition always covers its whole "from" domain exactly (parts are
1926                    // disjoint and total), so |p| (== |participants(p)|, per the `partition-card`
1927                    // horizontal rule) is always exactly the inner domain's size, regardless of
1928                    // numParts/partSize attributes.
1929                    match inner.length_signed() {
1930                        Ok(length) => Some(Domain::int(vec![Range::Single(length)])),
1931                        Err(_) => None,
1932                    }
1933                } else {
1934                    bug!(
1935                        "Domain of {self} needed to be a matrix, set, mset, sequence, relation, function, or partition for cardinality"
1936                    )
1937                }
1938            }
1939        }
1940    }
1941
1942    // Gets the number of domain elements mapped in a function. This is the cardinality and also the defined
1943    fn function_elements_size(
1944        attrs: FuncAttr<IntVal>,
1945        domain: &DomainPtr,
1946        codomain: &DomainPtr,
1947    ) -> Option<Range> {
1948        let attrs_gd = attrs.resolve().ok()?;
1949        let domain_length = domain.length_signed();
1950        // We can only infer if the domain is ground and the length is known
1951        let attr_size = match domain_length {
1952            Ok(len) => match attrs_gd.partiality {
1953                PartialityAttr::Total => Some(Range::Single(len)),
1954                PartialityAttr::Partial => {
1955                    // When partial we also need the codomain to be ground and known
1956                    let codomain_length = codomain.length_signed();
1957                    match codomain_length {
1958                        Ok(co_len) => match attrs_gd.jectivity {
1959                            JectivityAttr::Bijective => Some(Range::Single(co_len)),
1960                            JectivityAttr::Surjective => Some(Range::Bounded(co_len, len)),
1961                            JectivityAttr::Injective => {
1962                                Some(Range::Bounded(0, Ord::min(len, co_len)))
1963                            }
1964                            JectivityAttr::None => Some(Range::Bounded(0, len)),
1965                        },
1966                        Err(_) => None,
1967                    }
1968                }
1969            },
1970            Err(_) => None,
1971        };
1972        // We combine the sizes:
1973        // attrs_gd.size relates to size constraints imposed by the size attributes of the function
1974        // attr_size relates to size constraints imposed by the jectivity and partiality attributes.
1975        //       This uses inference from the domain and codomain lengths.
1976        // If the attributes clash the function is unsolveable, and an empty domain is returned
1977        match attr_size {
1978            Some(attr_size) => {
1979                let unsafe_range = Range::minimal(&[attrs_gd.size, attr_size]);
1980                unsafe_range.ok()
1981            }
1982            None => Some(attrs_gd.size),
1983        }
1984    }
1985
1986    /// Returns a reference to this expression's metadata without cloning.
1987    pub fn meta_ref(&self) -> &Metadata {
1988        macro_rules! match_meta_ref {
1989            ($($variant:ident),* $(,)?) => {
1990                match self {
1991                    $(Expression::$variant(meta, ..) => meta,)*
1992                }
1993            };
1994        }
1995        match_meta_ref!(
1996            AbstractLiteral,
1997            Root,
1998            Bubble,
1999            Comprehension,
2000            DominanceRelation,
2001            TypeAnnotation,
2002            DomainAnnotation,
2003            FromSolution,
2004            Metavar,
2005            Atomic,
2006            RecordField,
2007            UnsafeIndex,
2008            SafeIndex,
2009            UnsafeSlice,
2010            SafeSlice,
2011            InDomain,
2012            ToInt,
2013            Abs,
2014            Sum,
2015            Product,
2016            Min,
2017            Max,
2018            Not,
2019            Or,
2020            And,
2021            Imply,
2022            Iff,
2023            Union,
2024            Difference,
2025            In,
2026            Intersect,
2027            Supset,
2028            SupsetEq,
2029            Subset,
2030            SubsetEq,
2031            Eq,
2032            Neq,
2033            Geq,
2034            Leq,
2035            Gt,
2036            Lt,
2037            CatchUndef,
2038            SafeDiv,
2039            UnsafeDiv,
2040            SafeMod,
2041            UnsafeMod,
2042            Apart,
2043            Together,
2044            Participants,
2045            Party,
2046            Parts,
2047            Neg,
2048            Defined,
2049            Range,
2050            UnsafePow,
2051            SafePow,
2052            Flatten,
2053            AttributeAsConstraint,
2054            AllDiff,
2055            SmtDistinct,
2056            AllDifferentExcept,
2057            ElementId,
2058            Minus,
2059            Factorial,
2060            FlatAbsEq,
2061            FlatAllDiff,
2062            FlatMinEq,
2063            FlatSumGeq,
2064            FlatSumLeq,
2065            FlatIneq,
2066            FlatWatchedLiteral,
2067            FlatWeightedSumLeq,
2068            FlatWeightedSumGeq,
2069            FlatMinusEq,
2070            FlatProductEq,
2071            MinionDivEqUndefZero,
2072            MinionModuloEqUndefZero,
2073            MinionPow,
2074            MinionReify,
2075            MinionReifyImply,
2076            MinionWInIntervalSet,
2077            MinionWInSet,
2078            MinionElementOne,
2079            AuxDeclaration,
2080            SATInt,
2081            PairwiseSum,
2082            PairwiseProduct,
2083            Image,
2084            ImageSet,
2085            PreImage,
2086            Inverse,
2087            PermInverse,
2088            Compose,
2089            Restrict,
2090            LexLt,
2091            LexLeq,
2092            LexGt,
2093            LexGeq,
2094            FlatLexLt,
2095            FlatLexLeq,
2096            NegativeTable,
2097            Table,
2098            AtLeast,
2099            AtMost,
2100            Gcc,
2101            GccWeak,
2102            Active,
2103            ToSet,
2104            ToMSet,
2105            ToRelation,
2106            RelationProj,
2107            Card,
2108            Subsequence,
2109            Substring,
2110        )
2111    }
2112
2113    pub fn get_meta(&self) -> Metadata {
2114        let metas: VecDeque<Metadata> = self.children_bi();
2115        metas[0].clone()
2116    }
2117
2118    pub fn set_meta(&self, meta: Metadata) {
2119        self.transform_bi(&|_| meta.clone());
2120    }
2121
2122    /// Checks whether this expression is safe.
2123    ///
2124    /// An expression is unsafe if can be undefined, or if any of its children can be undefined.
2125    ///
2126    /// Unsafe expressions are (typically) prefixed with Unsafe in our AST, and can be made
2127    /// safe through the use of bubble rules.
2128    pub fn is_safe(&self) -> bool {
2129        // TODO: memoise in Metadata
2130        !self.any_expression(|expr| {
2131            matches!(
2132                expr,
2133                Expression::UnsafeDiv(_, _, _)
2134                    | Expression::UnsafeMod(_, _, _)
2135                    | Expression::UnsafePow(_, _, _)
2136                    | Expression::UnsafeIndex(_, _, _)
2137                    | Expression::Bubble(_, _, _)
2138                    | Expression::UnsafeSlice(_, _, _)
2139            ) || matches!(expr, Expression::Image(_, subject, argument)
2140                if image_can_be_undefined(subject, argument))
2141        })
2142    }
2143
2144    /// True if the expression is an associative and commutative operator
2145    pub fn is_associative_commutative_operator(&self) -> bool {
2146        TryInto::<ACOperatorKind>::try_into(self).is_ok()
2147    }
2148
2149    /// True if the expression is a matrix literal.
2150    ///
2151    /// This is true for both forms of matrix literals: those with elements of type [`Literal`] and
2152    /// [`Expression`].
2153    pub fn is_matrix_literal(&self) -> bool {
2154        matches!(
2155            self,
2156            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(_, _))
2157                | Expression::Atomic(
2158                    _,
2159                    Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _))),
2160                )
2161        )
2162    }
2163
2164    /// True iff self and other are both atomic and identical.
2165    ///
2166    /// This method is useful to cheaply check equivalence. Assuming CSE is enabled, any unifiable
2167    /// expressions will be rewritten to a common variable. This is much cheaper than checking the
2168    /// entire subtrees of `self` and `other`.
2169    pub fn identical_atom_to(&self, other: &Expression) -> bool {
2170        let atom1: Result<&Atom, _> = self.try_into();
2171        let atom2: Result<&Atom, _> = other.try_into();
2172
2173        if let (Ok(atom1), Ok(atom2)) = (atom1, atom2) {
2174            atom2 == atom1
2175        } else {
2176            false
2177        }
2178    }
2179
2180    /// If the expression is a list, borrows the inner expressions.
2181    ///
2182    /// Prefer this over [`Expression::unwrap_list`] when the elements are only inspected: copying
2183    /// them costs the whole list, which matters on wide matrices that rules test on every visit.
2184    ///
2185    /// Unlike [`Expression::unwrap_list`], a matrix held as an [`Atom::Literal`] is not unwrapped,
2186    /// since its elements are literals with no `Expression` to borrow.
2187    pub fn unwrap_list_ref(&self) -> Option<&[Expression]> {
2188        match self {
2189            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2190                expr.unwrap_list_ref()
2191            }
2192            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
2193                matrix.unwrap_list().map(Vec::as_slice)
2194            }
2195            _ => None,
2196        }
2197    }
2198
2199    /// If the expression is a list, borrows the inner expressions where it can.
2200    ///
2201    /// This accepts exactly the same expressions as [`Expression::unwrap_list`], but avoids the
2202    /// copy in the common case. Only a matrix held as an [`Atom::Literal`] allocates, because its
2203    /// elements are literals that have to be materialised as `Expression`s; every other list
2204    /// borrows.
2205    ///
2206    /// Prefer this over [`Expression::unwrap_list`] when the elements are usually only inspected,
2207    /// and over [`Expression::unwrap_list_ref`] when the narrower set of accepted expressions
2208    /// would change behaviour.
2209    pub fn unwrap_list_cow(&self) -> Option<Cow<'_, [Expression]>> {
2210        match self {
2211            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2212                expr.unwrap_list_cow()
2213            }
2214            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => matrix
2215                .unwrap_list()
2216                .map(|elems| Cow::Borrowed(elems.as_slice())),
2217            Expression::Atomic(
2218                _,
2219                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
2220            ) => matrix.unwrap_list().map(|elems| {
2221                Cow::Owned(
2222                    elems
2223                        .iter()
2224                        .cloned()
2225                        .map(|literal| Expression::Atomic(Metadata::new(), Atom::Literal(literal)))
2226                        .collect(),
2227                )
2228            }),
2229            _ => None,
2230        }
2231    }
2232
2233    /// Returns the number of elements when this expression is a list, without cloning them.
2234    ///
2235    /// Unlike [`Expression::unwrap_list`], this never converts literal elements into expressions.
2236    /// Use it for length, emptiness, and list-shape checks.
2237    pub fn list_len(&self) -> Option<usize> {
2238        match self {
2239            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2240                expr.list_len()
2241            }
2242            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
2243                matrix.unwrap_list().map(Vec::len)
2244            }
2245            Expression::Atomic(
2246                _,
2247                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
2248            ) => matrix.unwrap_list().map(Vec::len),
2249            _ => None,
2250        }
2251    }
2252
2253    /// Whether this expression is a list, without cloning or materialising its elements.
2254    pub fn is_list(&self) -> bool {
2255        self.list_len().is_some()
2256    }
2257
2258    /// If the expression is a list, returns a *copied* vector of the inner expressions.
2259    ///
2260    /// A list is any a matrix with the domain `int(1..)`. This includes matrix literals without
2261    /// any explicitly specified domain.
2262    ///
2263    /// The vector is owned: a matrix stored inside [`Atom::Literal`] holds [`Literal`]s that have
2264    /// to be materialised as [`Expression`]s, and callers that rewrite the elements need them by
2265    /// value. For inspection use [`Expression::unwrap_list_cow`]; for shape checks use
2266    /// [`Expression::list_len`] or [`Expression::is_list`].
2267    pub fn unwrap_list(&self) -> Option<Vec<Expression>> {
2268        match self {
2269            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2270                expr.unwrap_list()
2271            }
2272            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
2273                matrix.unwrap_list().cloned()
2274            }
2275            Expression::Atomic(
2276                _,
2277                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
2278            ) => matrix.unwrap_list().map(|elems| {
2279                elems
2280                    .clone()
2281                    .into_iter()
2282                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
2283                    .collect_vec()
2284            }),
2285            _ => None,
2286        }
2287    }
2288
2289    /// If the expression is a list, consumes it and returns its elements.
2290    ///
2291    /// Prefer this over [`Expression::unwrap_list`] when the expression is already owned: it moves
2292    /// expression elements out of the matrix rather than cloning the complete list.
2293    pub fn into_list(self) -> Option<Vec<Expression>> {
2294        match self {
2295            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2296                Moo::unwrap_or_clone(expr).into_list()
2297            }
2298            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
2299                matrix.into_list()
2300            }
2301            Expression::Atomic(
2302                _,
2303                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
2304            ) => matrix.into_list().map(|elems| {
2305                elems
2306                    .into_iter()
2307                    .map(|literal| Expression::Atomic(Metadata::new(), Atom::Literal(literal)))
2308                    .collect()
2309            }),
2310            _ => None,
2311        }
2312    }
2313
2314    /// If the expression is an expression-valued matrix, borrows its elements and index domain.
2315    ///
2316    /// As with [`Expression::unwrap_matrix_unchecked`], callers must preserve the relationship
2317    /// between the domain and element count. Literal-valued matrices are excluded because their
2318    /// elements cannot be borrowed as [`Expression`]s.
2319    pub fn unwrap_matrix_unchecked_ref(&self) -> Option<(&[Expression], &DomainPtr)> {
2320        match self {
2321            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2322                expr.unwrap_matrix_unchecked_ref()
2323            }
2324            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(elems, domain)) => {
2325                Some((elems, domain))
2326            }
2327            _ => None,
2328        }
2329    }
2330
2331    /// If the expression is a matrix, gets it elements and index domain.
2332    ///
2333    /// **Consider using the safer [`Expression::unwrap_list`] instead.**
2334    ///
2335    /// It is generally undefined to edit the length of a matrix unless it is a list (as defined by
2336    /// [`Expression::unwrap_list`]). Users of this function should ensure that, if the matrix is
2337    /// reconstructed, the index domain and the number of elements in the matrix remain the same.
2338    pub fn unwrap_matrix_unchecked(self) -> Option<(Vec<Expression>, DomainPtr)> {
2339        match self {
2340            Expression::TypeAnnotation(_, expr, _) | Expression::DomainAnnotation(_, expr, _) => {
2341                Moo::unwrap_or_clone(expr).unwrap_matrix_unchecked()
2342            }
2343            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(elems, domain)) => {
2344                Some((elems, domain))
2345            }
2346            Expression::Atomic(
2347                _,
2348                Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, domain))),
2349            ) => Some((
2350                elems
2351                    .into_iter()
2352                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
2353                    .collect_vec(),
2354                domain.into(),
2355            )),
2356
2357            _ => None,
2358        }
2359    }
2360
2361    /// For a Root expression, extends the inner vec with the given vec.
2362    ///
2363    /// # Panics
2364    /// Panics if the expression is not Root.
2365    pub fn extend_root(self, exprs: Vec<Expression>) -> Expression {
2366        match self {
2367            Expression::Root(meta, mut children) => {
2368                children.extend(exprs);
2369                Expression::Root(meta, children)
2370            }
2371            _ => panic!("extend_root called on a non-Root expression"),
2372        }
2373    }
2374
2375    /// Converts the expression to a literal, if possible.
2376    pub fn into_literal(self) -> Option<Literal> {
2377        match self {
2378            Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
2379            Expression::AbstractLiteral(_, abslit) => {
2380                Some(Literal::AbstractLiteral(abslit.into_literals()?))
2381            }
2382            Expression::Neg(_, e) => {
2383                let Literal::Int(i) = Moo::unwrap_or_clone(e).into_literal()? else {
2384                    bug!("negated literal should be an int");
2385                };
2386
2387                Some(Literal::Int(-i))
2388            }
2389
2390            _ => None,
2391        }
2392    }
2393
2394    /// If this expression is an associative-commutative operator, return its [ACOperatorKind].
2395    pub fn to_ac_operator_kind(&self) -> Option<ACOperatorKind> {
2396        TryFrom::try_from(self).ok()
2397    }
2398
2399    /// [`Typeable::return_type`], where the type can be worked out.
2400    ///
2401    /// `return_type` panics for a reference whose declaration has no domain yet -- a value letting
2402    /// whose body is not yet a constant, say. Parsers cannot rule those out while building the
2403    /// model, so they ask this instead and read `None` as "unknown".
2404    pub fn try_return_type(&self) -> Option<ReturnType> {
2405        let has_untyped_reference = self.any_expression(|expr| {
2406            matches!(expr, Expression::Atomic(_, Atom::Reference(reference))
2407                if reference.domain().is_none() && reference.resolve_constant().is_none())
2408        });
2409
2410        (!has_untyped_reference).then(|| self.return_type())
2411    }
2412
2413    /// Returns the categories of all sub-expressions of self.
2414    pub fn universe_categories(&self) -> HashSet<Category> {
2415        let mut categories = HashSet::new();
2416        self.for_each_expression(&mut |expr| {
2417            categories.insert(expr.category_of());
2418        });
2419        categories
2420    }
2421}
2422
2423/// True when `image(subject, argument)` can be undefined.
2424///
2425/// A function is defined only on its own domain -- `total` means defined for every value *in* that
2426/// domain, not defined everywhere -- so an argument that can fall outside it has no image there. A
2427/// partial function may also be undefined inside its domain, and a sequence is defined only on
2428/// `1..|s|`. When the length varies, every member of the domain still has the prefix `1..minSize`;
2429/// positions past that prefix are not known until solving.
2430///
2431/// Anything this cannot work out counts as undefinable. Safety has to be conservative: treating a
2432/// partial application as total lets its definedness be reasoned away, and the constraint it sits
2433/// in then silently holds where it should not.
2434pub fn image_can_be_undefined(subject: &Expression, argument: &Expression) -> bool {
2435    let Some(domain) = subject.domain_of().and_then(|domain| domain.resolve().ok()) else {
2436        return true;
2437    };
2438
2439    match domain.as_ref() {
2440        GroundDomain::Function(attr, from, _) => {
2441            !matches!(attr.partiality, PartialityAttr::Total) || !argument_always_in(argument, from)
2442        }
2443        // A permutation is total on its own domain, which is also where its image lands.
2444        GroundDomain::Permutation(_, inner) => !argument_always_in(argument, inner),
2445        GroundDomain::Sequence(attr, _) => {
2446            // Positions `1..=min_length` exist in every member of the domain. Anything past that
2447            // may sit in the allocated matrix and still be undefined if `|s|` is shorter.
2448            let min_length = attr.size.low().copied().unwrap_or(0);
2449            min_length <= 0
2450                || !argument_always_in(
2451                    argument,
2452                    &GroundDomain::Int(vec![Range::Bounded(1, min_length)]),
2453                )
2454        }
2455        _ => true,
2456    }
2457}
2458
2459/// Whether every value `argument` can take lies in `domain`.
2460///
2461/// Answers false whenever that cannot be established, including for domains that are equivalent
2462/// but not written the same way -- callers use this to decide whether they may reason about
2463/// definedness, where saying "no" only costs them the chance to simplify.
2464fn argument_always_in(argument: &Expression, domain: &GroundDomain) -> bool {
2465    let Some(argument_domain) = argument.domain_of().and_then(|d| d.resolve().ok()) else {
2466        return false;
2467    };
2468
2469    argument_domain
2470        .intersect(domain)
2471        .is_ok_and(|intersection| intersection == *argument_domain.as_ref())
2472}
2473
2474/// Also handles permutations: `image(perm, x)` returns a value from the permutation's own inner
2475/// domain (a permutation's "codomain" is its domain), so this doubles as the codomain lookup for
2476/// `Expression::Image`/`ImageSet` applied to either a function or a permutation.
2477pub fn get_function_codomain(function: &Moo<Expression>) -> Option<DomainPtr> {
2478    let function_domain = function.domain_of()?;
2479    match function_domain.resolve().as_ref() {
2480        Ok(d) => {
2481            match d.as_ref() {
2482                GroundDomain::Function(_, _, codomain) => Some(codomain.clone().into()),
2483                GroundDomain::Permutation(_, inner) => Some(inner.clone().into()),
2484                GroundDomain::Sequence(_, inner) => Some(inner.clone().into()),
2485                // Not defined for anything other than a function, permutation or sequence
2486                _ => None,
2487            }
2488        }
2489        Err(_) => {
2490            match function_domain.as_unresolved()? {
2491                UnresolvedDomain::Function(_, _, codomain) => Some(codomain.clone()),
2492                UnresolvedDomain::Permutation(_, inner) => Some(inner.clone()),
2493                UnresolvedDomain::Sequence(_, inner) => Some(inner.clone()),
2494                // Not defined for anything other than a function, permutation or sequence
2495                _ => None,
2496            }
2497        }
2498    }
2499}
2500
2501impl TryFrom<&Expression> for i32 {
2502    type Error = ();
2503
2504    fn try_from(value: &Expression) -> Result<Self, Self::Error> {
2505        let Expression::Atomic(_, atom) = value else {
2506            return Err(());
2507        };
2508
2509        let Atom::Literal(lit) = atom else {
2510            return Err(());
2511        };
2512
2513        let Literal::Int(i) = lit else {
2514            return Err(());
2515        };
2516
2517        Ok(*i)
2518    }
2519}
2520
2521impl TryFrom<Expression> for i32 {
2522    type Error = ();
2523
2524    fn try_from(value: Expression) -> Result<Self, Self::Error> {
2525        TryFrom::<&Expression>::try_from(&value)
2526    }
2527}
2528impl From<i32> for Expression {
2529    fn from(i: i32) -> Self {
2530        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(i)))
2531    }
2532}
2533
2534impl From<bool> for Expression {
2535    fn from(b: bool) -> Self {
2536        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(b)))
2537    }
2538}
2539
2540impl From<Atom> for Expression {
2541    fn from(value: Atom) -> Self {
2542        Expression::Atomic(Metadata::new(), value)
2543    }
2544}
2545
2546impl From<Literal> for Expression {
2547    fn from(value: Literal) -> Self {
2548        Expression::Atomic(Metadata::new(), value.into())
2549    }
2550}
2551
2552impl From<AbstractLiteral<Expression>> for Expression {
2553    fn from(value: AbstractLiteral<Expression>) -> Self {
2554        Expression::AbstractLiteral(Metadata::new(), value)
2555    }
2556}
2557
2558impl From<Moo<Expression>> for Expression {
2559    fn from(val: Moo<Expression>) -> Self {
2560        val.as_ref().clone()
2561    }
2562}
2563
2564impl CategoryOf for Expression {
2565    fn category_of(&self) -> Category {
2566        // take highest category of all the expressions children
2567        let category = self.cata(&move |x,children| {
2568
2569            if let Some(max_category) = children.iter().max() {
2570                // if this expression contains subexpressions, return the maximum category of the
2571                // subexpressions
2572                *max_category
2573            } else {
2574                // this expression has no children
2575                let mut max_category = Category::Bottom;
2576
2577                // calculate the category by looking at all atoms, submodels, comprehensions, and
2578                // declarationptrs inside this expression
2579
2580                // this should generically cover all leaf types we currently have in oxide.
2581
2582                // if x contains submodels (including comprehensions)
2583                if !Biplate::<Model>::universe_bi(&x).is_empty() {
2584                    // assume that the category is decision
2585                    return Category::Decision;
2586                }
2587
2588                // if x contains atoms
2589                if let Some(max_atom_category) = Biplate::<Atom>::universe_bi(&x).iter().map(|x| x.category_of()).max()
2590                // and those atoms have a higher category than we already know about
2591                && max_atom_category > max_category{
2592                    // update category
2593                    max_category = max_atom_category;
2594                }
2595
2596                // if x contains declarationPtrs
2597                if let Some(max_declaration_category) = Biplate::<DeclarationPtr>::universe_bi(&x).iter().map(|x| x.category_of()).max()
2598                // and those pointers have a higher category than we already know about
2599                && max_declaration_category > max_category{
2600                    // update category
2601                    max_category = max_declaration_category;
2602                }
2603                max_category
2604
2605            }
2606        });
2607
2608        if cfg!(debug_assertions) {
2609            trace!(
2610                category= %category,
2611                expression= %self,
2612                "Called Expression::category_of()"
2613            );
2614        };
2615        category
2616    }
2617}
2618
2619impl Display for Expression {
2620    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2621        match &self {
2622            Expression::Union(_, box1, box2) => {
2623                write!(f, "({} union {})", box1.clone(), box2.clone())
2624            }
2625            Expression::Difference(_, box1, box2) => {
2626                write!(f, "({} - {})", box1.clone(), box2.clone())
2627            }
2628            Expression::In(_, e1, e2) => {
2629                write!(f, "{e1} in {e2}")
2630            }
2631            Expression::Intersect(_, box1, box2) => {
2632                write!(f, "({} intersect {})", box1.clone(), box2.clone())
2633            }
2634            Expression::Supset(_, box1, box2) => {
2635                write!(f, "({} supset {})", box1.clone(), box2.clone())
2636            }
2637            Expression::SupsetEq(_, box1, box2) => {
2638                write!(f, "({} supsetEq {})", box1.clone(), box2.clone())
2639            }
2640            Expression::Subset(_, box1, box2) => {
2641                write!(f, "({} subset {})", box1.clone(), box2.clone())
2642            }
2643            Expression::SubsetEq(_, box1, box2) => {
2644                write!(f, "({} subsetEq {})", box1.clone(), box2.clone())
2645            }
2646
2647            Expression::AbstractLiteral(_, l) => l.fmt(f),
2648            Expression::Comprehension(_, c) => c.fmt(f),
2649            Expression::UnsafeIndex(_, e1, e2) => write!(f, "{e1}{}", pretty_vec(e2)),
2650            Expression::RecordField(_, r, fld) => {
2651                write!(f, "{r}[{fld}]")
2652            }
2653            Expression::SafeIndex(_, e1, e2) => write!(f, "SafeIndex({e1},{})", pretty_vec(e2)),
2654            Expression::UnsafeSlice(_, e1, es) => {
2655                let args = es
2656                    .iter()
2657                    .map(|x| match x {
2658                        Some(x) => format!("{x}"),
2659                        None => "..".into(),
2660                    })
2661                    .join(",");
2662
2663                write!(f, "{e1}[{args}]")
2664            }
2665            Expression::SafeSlice(_, e1, es) => {
2666                let args = es
2667                    .iter()
2668                    .map(|x| match x {
2669                        Some(x) => format!("{x}"),
2670                        None => "..".into(),
2671                    })
2672                    .join(",");
2673
2674                write!(f, "SafeSlice({e1},[{args}])")
2675            }
2676            Expression::InDomain(_, e, domain) => {
2677                write!(f, "__inDomain({e},{domain})")
2678            }
2679            Expression::Root(_, exprs) => {
2680                write!(f, "{}", pretty_expressions_as_top_level(exprs))
2681            }
2682            Expression::DominanceRelation(_, expr) => write!(f, "DominanceRelation({expr})"),
2683            Expression::FromSolution(_, expr) => write!(f, "FromSolution({expr})"),
2684            Expression::Metavar(_, name) => write!(f, "&{name}"),
2685            Expression::Atomic(_, atom) => atom.fmt(f),
2686            Expression::TypeAnnotation(_, expr, domain) => {
2687                write!(
2688                    f,
2689                    "{}",
2690                    pretty_expression_type_annotation(expr, domain.as_type_string())
2691                )
2692            }
2693            Expression::DomainAnnotation(_, expr, domain) => {
2694                write!(f, "{}", pretty_expression_domain_annotation(expr, domain))
2695            }
2696            Expression::Abs(_, a) | Expression::Card(_, a) => write!(f, "|{a}|"),
2697            Expression::Sum(_, e) => {
2698                write!(f, "sum({e})")
2699            }
2700            Expression::Product(_, e) => {
2701                write!(f, "product({e})")
2702            }
2703            Expression::Min(_, e) => {
2704                write!(f, "min({e})")
2705            }
2706            Expression::Max(_, e) => {
2707                write!(f, "max({e})")
2708            }
2709            Expression::Not(_, expr_box) => {
2710                write!(f, "!({})", expr_box.clone())
2711            }
2712            Expression::Or(_, e) => {
2713                write!(f, "or({e})")
2714            }
2715            Expression::And(_, e) => {
2716                write!(f, "and({e})")
2717            }
2718            Expression::Imply(_, box1, box2) => {
2719                write!(f, "({box1}) -> ({box2})")
2720            }
2721            Expression::Iff(_, box1, box2) => {
2722                write!(f, "({box1}) <-> ({box2})")
2723            }
2724            Expression::Eq(_, box1, box2) => {
2725                write!(f, "{box1} = {box2}")
2726            }
2727            Expression::Neq(_, box1, box2) => {
2728                write!(f, "{box1} != {box2}")
2729            }
2730            Expression::Geq(_, box1, box2) => {
2731                write!(f, "{box1} >= {box2}")
2732            }
2733            Expression::Leq(_, box1, box2) => {
2734                write!(f, "{box1} <= {box2}")
2735            }
2736            Expression::Gt(_, box1, box2) => {
2737                write!(f, "{box1} > {box2}")
2738            }
2739            Expression::Lt(_, box1, box2) => {
2740                write!(f, "{box1} < {box2}")
2741            }
2742            Expression::Apart(_, list, partition) => {
2743                write!(f, "apart({list}, {partition})")
2744            }
2745            Expression::Together(_, list, partition) => {
2746                write!(f, "together({list}, {partition})")
2747            }
2748            Expression::Participants(_, partition) => {
2749                write!(f, "participants({partition})")
2750            }
2751            Expression::Party(_, element, partition) => {
2752                write!(f, "party({element}, {partition})")
2753            }
2754            Expression::Parts(_, partition) => {
2755                write!(f, "parts({partition})")
2756            }
2757            Expression::FlatMinEq(_, vars, result) => {
2758                write!(f, "FlatMinEq({}, {})", pretty_vec(vars), result.clone())
2759            }
2760            Expression::FlatSumGeq(_, box1, box2) => {
2761                write!(f, "SumGeq({}, {})", pretty_vec(box1), box2.clone())
2762            }
2763            Expression::FlatSumLeq(_, box1, box2) => {
2764                write!(f, "SumLeq({}, {})", pretty_vec(box1), box2.clone())
2765            }
2766            Expression::FlatIneq(_, box1, box2, box3) => write!(
2767                f,
2768                "Ineq({}, {}, {})",
2769                box1.clone(),
2770                box2.clone(),
2771                box3.clone()
2772            ),
2773            Expression::Flatten(_, n, m) => {
2774                if let Some(n) = n {
2775                    write!(f, "flatten({n}, {m})")
2776                } else {
2777                    write!(f, "flatten({m})")
2778                }
2779            }
2780            Expression::AttributeAsConstraint(_, target, attr, val) => {
2781                if let Some(val) = val {
2782                    write!(f, "{attr}({target}, {val})")
2783                } else {
2784                    write!(f, "{attr}({target})")
2785                }
2786            }
2787            Expression::AllDiff(_, e) => {
2788                write!(f, "allDifferent({e})")
2789            }
2790            Expression::SmtDistinct(_, e) => {
2791                write!(f, "smtDistinct({e})")
2792            }
2793            Expression::AllDifferentExcept(_, matrix, except) => {
2794                write!(f, "allDifferentExcept({matrix}, {except})")
2795            }
2796            Expression::ElementId(_, matrix, value) => {
2797                write!(f, "elementId({matrix}, {value})")
2798            }
2799            Expression::Table(_, tuple_expr, rows_expr) => {
2800                write!(f, "table({tuple_expr}, {rows_expr})")
2801            }
2802            Expression::NegativeTable(_, tuple_expr, rows_expr) => {
2803                write!(f, "negativeTable({tuple_expr}, {rows_expr})")
2804            }
2805            Expression::AtLeast(_, vars, counts, values) => {
2806                write!(f, "atLeast({vars}, {counts}, {values})")
2807            }
2808            Expression::AtMost(_, vars, counts, values) => {
2809                write!(f, "atMost({vars}, {counts}, {values})")
2810            }
2811            Expression::Gcc(_, vars, values, counts) => {
2812                write!(f, "globalCardinality({vars}, {values}, {counts})")
2813            }
2814            Expression::GccWeak(_, vars, values, counts) => {
2815                write!(f, "gccweak({vars}, {values}, {counts})")
2816            }
2817            Expression::Bubble(_, box1, box2) => {
2818                write!(f, "{{{} @ {}}}", box1.clone(), box2.clone())
2819            }
2820            Expression::CatchUndef(_, box1, box2) => {
2821                write!(f, "catchUndef({box1}, {box2})")
2822            }
2823            Expression::SafeDiv(_, box1, box2) => {
2824                write!(f, "SafeDiv({}, {})", box1.clone(), box2.clone())
2825            }
2826            Expression::UnsafeDiv(_, box1, box2) => {
2827                write!(f, "({} / {})", box1.clone(), box2.clone())
2828            }
2829            Expression::UnsafePow(_, box1, box2) => {
2830                write!(f, "({} ** {})", box1.clone(), box2.clone())
2831            }
2832            Expression::SafePow(_, box1, box2) => {
2833                write!(f, "SafePow({}, {})", box1.clone(), box2.clone())
2834            }
2835            Expression::Subsequence(_, s, t) => {
2836                write!(f, "{} subsequence {}", s.clone(), t.clone())
2837            }
2838            Expression::Substring(_, s, t) => {
2839                write!(f, "{} substring {}", s.clone(), t.clone())
2840            }
2841            Expression::MinionDivEqUndefZero(_, box1, box2, box3) => {
2842                write!(
2843                    f,
2844                    "DivEq({}, {}, {})",
2845                    box1.clone(),
2846                    box2.clone(),
2847                    box3.clone()
2848                )
2849            }
2850            Expression::MinionModuloEqUndefZero(_, box1, box2, box3) => {
2851                write!(
2852                    f,
2853                    "ModEq({}, {}, {})",
2854                    box1.clone(),
2855                    box2.clone(),
2856                    box3.clone()
2857                )
2858            }
2859            Expression::FlatWatchedLiteral(_, x, l) => {
2860                write!(f, "WatchedLiteral({x},{l})")
2861            }
2862            Expression::MinionReify(_, box1, box2) => {
2863                write!(f, "Reify({}, {})", box1.clone(), box2.clone())
2864            }
2865            Expression::MinionReifyImply(_, box1, box2) => {
2866                write!(f, "ReifyImply({}, {})", box1.clone(), box2.clone())
2867            }
2868            Expression::MinionWInIntervalSet(_, atom, intervals) => {
2869                let intervals = intervals.iter().join(",");
2870                write!(f, "__minion_w_inintervalset({atom},[{intervals}])")
2871            }
2872            Expression::MinionWInSet(_, atom, values) => {
2873                let values = values.iter().join(",");
2874                write!(f, "__minion_w_inset({atom},[{values}])")
2875            }
2876            Expression::AuxDeclaration(_, reference, e) => {
2877                write!(f, "{} =aux {}", reference, e.clone())
2878            }
2879            Expression::UnsafeMod(_, a, b) => {
2880                write!(f, "{} % {}", a.clone(), b.clone())
2881            }
2882            Expression::SafeMod(_, a, b) => {
2883                write!(f, "SafeMod({},{})", a.clone(), b.clone())
2884            }
2885            Expression::Neg(_, a) => {
2886                write!(f, "-({})", a.clone())
2887            }
2888            Expression::Factorial(_, a) => {
2889                write!(f, "({})!", a.clone())
2890            }
2891            Expression::Minus(_, a, b) => {
2892                write!(f, "({} - {})", a.clone(), b.clone())
2893            }
2894            Expression::FlatAllDiff(_, es) => {
2895                write!(f, "__flat_alldiff({})", pretty_vec(es))
2896            }
2897            Expression::FlatAbsEq(_, a, b) => {
2898                write!(f, "AbsEq({},{})", a.clone(), b.clone())
2899            }
2900            Expression::FlatMinusEq(_, a, b) => {
2901                write!(f, "MinusEq({},{})", a.clone(), b.clone())
2902            }
2903            Expression::FlatProductEq(_, a, b, c) => {
2904                write!(
2905                    f,
2906                    "FlatProductEq({},{},{})",
2907                    a.clone(),
2908                    b.clone(),
2909                    c.clone()
2910                )
2911            }
2912            Expression::FlatWeightedSumLeq(_, cs, vs, total) => {
2913                write!(
2914                    f,
2915                    "FlatWeightedSumLeq({},{},{})",
2916                    pretty_vec(cs),
2917                    pretty_vec(vs),
2918                    total.clone()
2919                )
2920            }
2921            Expression::FlatWeightedSumGeq(_, cs, vs, total) => {
2922                write!(
2923                    f,
2924                    "FlatWeightedSumGeq({},{},{})",
2925                    pretty_vec(cs),
2926                    pretty_vec(vs),
2927                    total.clone()
2928                )
2929            }
2930            Expression::MinionPow(_, atom, atom1, atom2) => {
2931                write!(f, "MinionPow({atom},{atom1},{atom2})")
2932            }
2933            Expression::MinionElementOne(_, atoms, atom, atom1) => {
2934                let atoms = atoms.iter().join(",");
2935                write!(f, "__minion_element_one([{atoms}],{atom},{atom1})")
2936            }
2937
2938            Expression::ToInt(_, expr) => {
2939                write!(f, "toInt({expr})")
2940            }
2941
2942            Expression::SATInt(_, encoding, bits, (min, max)) => {
2943                write!(f, "SATInt({encoding:?}, {bits} [{min}, {max}])")
2944            }
2945
2946            Expression::PairwiseSum(_, a, b) => write!(f, "PairwiseSum({a}, {b})"),
2947            Expression::PairwiseProduct(_, a, b) => write!(f, "PairwiseProduct({a}, {b})"),
2948
2949            Expression::Defined(_, function) => write!(f, "defined({function})"),
2950            Expression::Range(_, function) => write!(f, "range({function})"),
2951            Expression::Image(_, function, elems) => write!(f, "image({function},{elems})"),
2952            Expression::ImageSet(_, function, elems) => write!(f, "imageSet({function},{elems})"),
2953            Expression::PreImage(_, function, elems) => write!(f, "preImage({function},{elems})"),
2954            Expression::Inverse(_, a, b) => write!(f, "inverse({a},{b})"),
2955            Expression::PermInverse(_, p) => write!(f, "permInverse({p})"),
2956            Expression::Compose(_, g, h) => write!(f, "compose({g},{h})"),
2957            Expression::Restrict(_, function, domain) => write!(f, "restrict({function},{domain})"),
2958
2959            Expression::LexLt(_, a, b) => write!(f, "({a} <lex {b})"),
2960            Expression::LexLeq(_, a, b) => write!(f, "({a} <=lex {b})"),
2961            Expression::LexGt(_, a, b) => write!(f, "({a} >lex {b})"),
2962            Expression::LexGeq(_, a, b) => write!(f, "({a} >=lex {b})"),
2963            Expression::FlatLexLt(_, a, b) => {
2964                write!(f, "FlatLexLt({}, {})", pretty_vec(a), pretty_vec(b))
2965            }
2966            Expression::FlatLexLeq(_, a, b) => {
2967                write!(f, "FlatLexLeq({}, {})", pretty_vec(a), pretty_vec(b))
2968            }
2969            Expression::Active(_, variant, field_name) => {
2970                write!(f, "active({variant}, {field_name})")
2971            }
2972            Expression::ToSet(_, other) => write!(f, "toSet({other})"),
2973            Expression::ToMSet(_, other) => write!(f, "toMSet({other})"),
2974            Expression::ToRelation(_, function) => write!(f, "toRelation({function})"),
2975            Expression::RelationProj(_, relation, projections) => {
2976                let projections_str = projections
2977                    .iter()
2978                    .map(|x| {
2979                        if let Some(x) = x {
2980                            x.to_string()
2981                        } else {
2982                            String::from("_")
2983                        }
2984                    })
2985                    .join(", ");
2986                write!(f, "{relation}({projections_str})")
2987            }
2988        }
2989    }
2990}
2991
2992fn minus_operand_return_type(expr: &Expression) -> ReturnType {
2993    match expr {
2994        Expression::Atomic(_, Atom::Reference(reference)) => {
2995            let decl_kind = reference.ptr.kind().clone();
2996            match decl_kind {
2997                DeclarationKind::Find(var) | DeclarationKind::FindAuxiliary(var) => {
2998                    var.return_type()
2999                }
3000                DeclarationKind::Given(domain)
3001                | DeclarationKind::DomainLetting(domain) => domain.return_type(),
3002                DeclarationKind::Quantified(inner) => inner.domain().return_type(),
3003                DeclarationKind::QuantifiedExpr(inner)
3004                | DeclarationKind::TemporaryValueLetting(inner)
3005                // not sure if i should ever be looking at the domain ptr but seems to work
3006                | DeclarationKind::ValueLetting(inner, _) => inner.return_type(),
3007            }
3008        }
3009        _ => expr.return_type(),
3010    }
3011}
3012
3013impl Typeable for Expression {
3014    fn return_type(&self) -> ReturnType {
3015        match self {
3016            Expression::Union(_, subject, _)
3017            | Expression::Intersect(_, subject, _)
3018            | Expression::Difference(_, subject, _) => subject.return_type(),
3019            Expression::In(_, _, _) => ReturnType::Bool,
3020            Expression::Supset(_, _, _) => ReturnType::Bool,
3021            Expression::SupsetEq(_, _, _) => ReturnType::Bool,
3022            Expression::Subset(_, _, _) => ReturnType::Bool,
3023            Expression::SubsetEq(_, _, _) => ReturnType::Bool,
3024            Expression::AbstractLiteral(_, lit) => lit.return_type(),
3025            Expression::RecordField(_, rec, field_name) => {
3026                if let ReturnType::Record(ents) = rec.return_type() {
3027                    for Field { name, value } in ents {
3028                        if name.eq(field_name) {
3029                            return value;
3030                        }
3031                    }
3032                }
3033                ReturnType::Unknown
3034            }
3035            Expression::UnsafeIndex(_, subject, idx) | Expression::SafeIndex(_, subject, idx) => {
3036                let mut indexed_ty = subject.return_type();
3037                for index in idx {
3038                    indexed_ty = match indexed_ty {
3039                        ReturnType::Tuple(field_types) => {
3040                            let Expression::Atomic(_, Atom::Literal(Literal::Int(index))) = index
3041                            else {
3042                                return ReturnType::Unknown;
3043                            };
3044                            let Some(zero_based_index) = index
3045                                .checked_sub(1)
3046                                .and_then(|index| usize::try_from(index).ok())
3047                            else {
3048                                return ReturnType::Unknown;
3049                            };
3050                            field_types
3051                                .get(zero_based_index)
3052                                .cloned()
3053                                .unwrap_or(ReturnType::Unknown)
3054                        }
3055                        // Declared matrix types are flat, whereas matrix literals can have one
3056                        // nested Matrix return type per dimension. Continue through nested
3057                        // matrices, but stop once the declared element type is reached.
3058                        ReturnType::Matrix(element_type) => {
3059                            let element_type = *element_type;
3060                            if !matches!(element_type, ReturnType::Matrix(_)) {
3061                                return element_type;
3062                            }
3063                            element_type
3064                        }
3065                        // Record and variant indices need their field-name context to determine a
3066                        // result type. Unknown can likewise be resolved by later typechecking.
3067                        ReturnType::Record(_) | ReturnType::Variant(_) | ReturnType::Unknown => {
3068                            return ReturnType::Unknown;
3069                        }
3070                        subject_ty => bug!(
3071                            "Invalid indexing operation: expected the operand to be a collection, got {self}: {subject_ty}"
3072                        ),
3073                    };
3074                }
3075                indexed_ty
3076            }
3077            Expression::UnsafeSlice(_, subject, _) | Expression::SafeSlice(_, subject, _) => {
3078                let mut element_type = subject.return_type();
3079                while let ReturnType::Matrix(inner) = element_type {
3080                    element_type = *inner;
3081                }
3082                ReturnType::Matrix(Box::new(element_type))
3083            }
3084            Expression::InDomain(_, _, _) => ReturnType::Bool,
3085            Expression::Comprehension(_, comp) => comp.return_type(),
3086            Expression::Root(_, _) => ReturnType::Bool,
3087            Expression::DominanceRelation(_, _) => ReturnType::Bool,
3088            Expression::FromSolution(_, expr) => expr.return_type(),
3089            Expression::Metavar(_, _) => ReturnType::Unknown,
3090            Expression::Atomic(_, atom) => atom.return_type(),
3091            Expression::TypeAnnotation(_, _, domain) => domain.return_type(),
3092            Expression::DomainAnnotation(_, _, domain) => domain.return_type(),
3093            Expression::Abs(_, _) => ReturnType::Int,
3094            Expression::Sum(_, _) => ReturnType::Int,
3095            Expression::Product(_, _) => ReturnType::Int,
3096            Expression::Min(_, _) => ReturnType::Int,
3097            Expression::Max(_, _) => ReturnType::Int,
3098            Expression::Not(_, _) => ReturnType::Bool,
3099            Expression::Or(_, _) => ReturnType::Bool,
3100            Expression::Imply(_, _, _) => ReturnType::Bool,
3101            Expression::Iff(_, _, _) => ReturnType::Bool,
3102            Expression::And(_, _) => ReturnType::Bool,
3103            Expression::Eq(_, _, _) => ReturnType::Bool,
3104            Expression::Neq(_, _, _) => ReturnType::Bool,
3105            Expression::Geq(_, _, _) => ReturnType::Bool,
3106            Expression::Leq(_, _, _) => ReturnType::Bool,
3107            Expression::Gt(_, _, _) => ReturnType::Bool,
3108            Expression::Lt(_, _, _) => ReturnType::Bool,
3109            Expression::Apart(_, _, _) => ReturnType::Bool,
3110            Expression::Together(_, _, _) => ReturnType::Bool,
3111            Expression::Party(_, _, subject) => ReturnType::Set(Box::new(subject.return_type())),
3112            Expression::Participants(_, subject) => {
3113                ReturnType::Set(Box::new(subject.return_type()))
3114            }
3115            Expression::Parts(_, subject) => {
3116                ReturnType::Set(Box::new(ReturnType::Set(Box::new(subject.return_type()))))
3117            }
3118            Expression::CatchUndef(_, _, _) => ReturnType::Int,
3119            Expression::SafeDiv(_, _, _) => ReturnType::Int,
3120            Expression::UnsafeDiv(_, _, _) => ReturnType::Int,
3121            Expression::FlatAllDiff(_, _) => ReturnType::Bool,
3122            Expression::FlatMinEq(_, _, _) => ReturnType::Bool,
3123            Expression::FlatSumGeq(_, _, _) => ReturnType::Bool,
3124            Expression::FlatSumLeq(_, _, _) => ReturnType::Bool,
3125            Expression::MinionDivEqUndefZero(_, _, _, _) => ReturnType::Bool,
3126            Expression::FlatIneq(_, _, _, _) => ReturnType::Bool,
3127            Expression::Flatten(_, _, matrix) => {
3128                let matrix_type = matrix.return_type();
3129                match matrix_type {
3130                    ReturnType::Matrix(_) => {
3131                        // unwrap until we get to innermost element
3132                        let mut elem_type = matrix_type;
3133                        while let ReturnType::Matrix(new_elem_type) = &elem_type {
3134                            elem_type = *new_elem_type.clone();
3135                        }
3136                        ReturnType::Matrix(Box::new(elem_type))
3137                    }
3138                    _ => bug!(
3139                        "Invalid indexing operation: expected the operand to be a collection, got {self}: {matrix_type}"
3140                    ),
3141                }
3142            }
3143            Expression::AllDiff(_, _) => ReturnType::Bool,
3144            Expression::SmtDistinct(_, _) => ReturnType::Bool,
3145            Expression::AllDifferentExcept(_, _, _) => ReturnType::Bool,
3146            Expression::ElementId(_, _, _) => ReturnType::Int,
3147            Expression::Table(_, _, _) => ReturnType::Bool,
3148            Expression::NegativeTable(_, _, _) => ReturnType::Bool,
3149            Expression::AtLeast(_, _, _, _) => ReturnType::Bool,
3150            Expression::AtMost(_, _, _, _) => ReturnType::Bool,
3151            Expression::Gcc(_, _, _, _) | Expression::GccWeak(_, _, _, _) => ReturnType::Bool,
3152            Expression::Bubble(_, inner, _) => inner.return_type(),
3153            Expression::FlatWatchedLiteral(_, _, _) => ReturnType::Bool,
3154            Expression::MinionReify(_, _, _) => ReturnType::Bool,
3155            Expression::MinionReifyImply(_, _, _) => ReturnType::Bool,
3156            Expression::MinionWInIntervalSet(_, _, _) => ReturnType::Bool,
3157            Expression::MinionWInSet(_, _, _) => ReturnType::Bool,
3158            Expression::MinionElementOne(_, _, _, _) => ReturnType::Bool,
3159            Expression::AuxDeclaration(_, _, _) => ReturnType::Bool,
3160            Expression::UnsafeMod(_, _, _) => ReturnType::Int,
3161            Expression::SafeMod(_, _, _) => ReturnType::Int,
3162            Expression::MinionModuloEqUndefZero(_, _, _, _) => ReturnType::Bool,
3163            Expression::Neg(_, _) => ReturnType::Int,
3164            Expression::Factorial(_, _) => ReturnType::Int,
3165            Expression::UnsafePow(_, _, _) => ReturnType::Int,
3166            Expression::SafePow(_, _, _) => ReturnType::Int,
3167            Expression::Minus(_, a, b) => {
3168                // rather than calling .return_type on a and b which sometimes errors on references that don't have domains
3169                // use custom function that extracts return type from atomic references based on each declaration variant
3170                let a_type = minus_operand_return_type(a);
3171                let b_type = minus_operand_return_type(b);
3172
3173                if a_type == ReturnType::Int && b_type == ReturnType::Int {
3174                    ReturnType::Int
3175                } else if let ReturnType::Set(a_inner) = a_type
3176                    && let ReturnType::Set(b_inner) = b_type
3177                    && a_inner == b_inner
3178                {
3179                    ReturnType::Set(a_inner)
3180                } else {
3181                    bug!(
3182                        "Invalid minus operation: operands are of different or invalid types for this operation"
3183                    )
3184                }
3185            }
3186            Expression::FlatAbsEq(_, _, _) => ReturnType::Bool,
3187            Expression::FlatMinusEq(_, _, _) => ReturnType::Bool,
3188            Expression::FlatProductEq(_, _, _, _) => ReturnType::Bool,
3189            Expression::FlatWeightedSumLeq(_, _, _, _) => ReturnType::Bool,
3190            Expression::FlatWeightedSumGeq(_, _, _, _) => ReturnType::Bool,
3191            Expression::MinionPow(_, _, _, _) => ReturnType::Bool,
3192            Expression::ToInt(_, _) => ReturnType::Int,
3193            Expression::SATInt(..) => ReturnType::Int,
3194            Expression::PairwiseSum(_, _, _) => ReturnType::Int,
3195            Expression::PairwiseProduct(_, _, _) => ReturnType::Int,
3196            Expression::Defined(_, function) => {
3197                let subject = function.return_type();
3198                match subject {
3199                    ReturnType::Function(domain, _) => ReturnType::Set(Box::new(*domain)),
3200                    _ => bug!(
3201                        "Invalid defined operation: expected the operand to be a function, got {self}: {subject}"
3202                    ),
3203                }
3204            }
3205            Expression::Range(_, function) => {
3206                let subject = function.return_type();
3207                match subject {
3208                    ReturnType::Function(_, codomain) => ReturnType::Set(Box::new(*codomain)),
3209                    _ => bug!(
3210                        "Invalid range operation: expected the operand to be a function, got {self}: {subject}"
3211                    ),
3212                }
3213            }
3214            Expression::Image(_, function, _) => {
3215                let subject = function.return_type();
3216                match subject {
3217                    ReturnType::Function(_, codomain) => *codomain,
3218                    ReturnType::Permutation(inner) => *inner,
3219                    // A sequence is a function from int, so applying it is an image too.
3220                    ReturnType::Sequence(inner) => *inner,
3221                    _ => bug!(
3222                        "Invalid image operation: expected the operand to be a function or permutation, got {self}: {subject}"
3223                    ),
3224                }
3225            }
3226            Expression::ImageSet(_, function, _) => {
3227                let subject = function.return_type();
3228                match subject {
3229                    ReturnType::Function(_, codomain) => ReturnType::Set(Box::new(*codomain)),
3230                    ReturnType::Permutation(inner) => ReturnType::Set(inner),
3231                    _ => bug!(
3232                        "Invalid imageSet operation: expected the operand to be a function or permutation, got {self}: {subject}"
3233                    ),
3234                }
3235            }
3236            Expression::PreImage(_, function, _) => {
3237                let subject = function.return_type();
3238                match subject {
3239                    ReturnType::Function(domain, _) => ReturnType::Set(Box::new(*domain)),
3240                    _ => bug!(
3241                        "Invalid preImage operation: expected the operand to be a function, got {self}: {subject}"
3242                    ),
3243                }
3244            }
3245            Expression::Restrict(_, function, new_domain) => {
3246                let subject = function.return_type();
3247                match subject {
3248                    ReturnType::Function(_, codomain) => {
3249                        ReturnType::Function(Box::new(new_domain.return_type()), codomain)
3250                    }
3251                    _ => bug!(
3252                        "Invalid preImage operation: expected the operand to be a function, got {self}: {subject}"
3253                    ),
3254                }
3255            }
3256            Expression::Inverse(..) => ReturnType::Bool,
3257            Expression::PermInverse(_, p) => p.return_type(),
3258            Expression::Compose(_, g, _h) => g.return_type(),
3259            Expression::LexLt(..) => ReturnType::Bool,
3260            Expression::LexGt(..) => ReturnType::Bool,
3261            Expression::LexLeq(..) => ReturnType::Bool,
3262            Expression::LexGeq(..) => ReturnType::Bool,
3263            Expression::FlatLexLt(..) => ReturnType::Bool,
3264            Expression::FlatLexLeq(..) => ReturnType::Bool,
3265            Expression::Active(..) => ReturnType::Bool,
3266            Expression::ToSet(_, other) => {
3267                let subject = other.return_type();
3268                match subject {
3269                    ReturnType::Matrix(domain) => ReturnType::Set(Box::new(*domain)),
3270                    ReturnType::MSet(domain) => ReturnType::Set(Box::new(*domain)),
3271                    ReturnType::Function(domain, codomain) => {
3272                        ReturnType::Set(Box::new(ReturnType::Tuple(vec![*domain, *codomain])))
3273                    }
3274                    ReturnType::Relation(domains) => {
3275                        ReturnType::Set(Box::new(ReturnType::Tuple(domains)))
3276                    }
3277                    _ => bug!(
3278                        "Invalid toSet operation: expected the operand to be a mset, matrix, relation, or function, got {self}: {subject}"
3279                    ),
3280                }
3281            }
3282            Expression::ToMSet(_, other) => {
3283                let subject = other.return_type();
3284                match subject {
3285                    ReturnType::Set(domain) => ReturnType::MSet(Box::new(*domain)),
3286                    ReturnType::Function(domain, codomain) => {
3287                        ReturnType::MSet(Box::new(ReturnType::Tuple(vec![*domain, *codomain])))
3288                    }
3289                    ReturnType::Relation(domains) => {
3290                        ReturnType::MSet(Box::new(ReturnType::Tuple(domains)))
3291                    }
3292                    _ => bug!(
3293                        "Invalid toMSet operation: expected the operand to be a set, relation, or function, got {self}: {subject}"
3294                    ),
3295                }
3296            }
3297            Expression::ToRelation(_, function) => {
3298                let subject = function.return_type();
3299                match subject {
3300                    ReturnType::Function(domain, codomain) => {
3301                        ReturnType::Relation(vec![*domain, *codomain])
3302                    }
3303                    _ => bug!(
3304                        "Invalid toRelation operation: expected the operand to be a function, got {self}: {subject}"
3305                    ),
3306                }
3307            }
3308            Expression::RelationProj(_, relation, projections) => {
3309                let subject = relation.return_type();
3310                match subject {
3311                    ReturnType::Relation(domains) => {
3312                        let new_doms = domains
3313                            .iter()
3314                            .zip(projections.iter())
3315                            .filter_map(|(domain, included)| {
3316                                if included.is_none() {
3317                                    // The domains corresponding to projections which are None remain in the relation
3318                                    Some(domain.clone())
3319                                } else {
3320                                    None
3321                                }
3322                            })
3323                            .collect();
3324                        ReturnType::Relation(new_doms)
3325                    }
3326                    _ => bug!(
3327                        "Invalid RelationProj operation: expected the operand to be a relation, got {self}: {subject}"
3328                    ),
3329                }
3330            }
3331            Expression::Card(..) => ReturnType::Int,
3332            Expression::Subsequence(_, _, _) => ReturnType::Bool,
3333            Expression::Substring(_, _, _) => ReturnType::Bool,
3334            Expression::AttributeAsConstraint(_, _, _, _) => ReturnType::Bool,
3335        }
3336    }
3337}
3338
3339impl Expression {
3340    /// Visit each direct `Expression` child by reference, without cloning.
3341    pub fn for_each_expr_child<'a>(&'a self, f: &mut impl FnMut(&'a Expression)) {
3342        match self {
3343            // Special Case
3344            Expression::AbstractLiteral(_, alit) => match alit {
3345                AbstractLiteral::Set(v) | AbstractLiteral::MSet(v) | AbstractLiteral::Tuple(v) => {
3346                    for expr in v {
3347                        f(expr);
3348                    }
3349                }
3350                AbstractLiteral::Partition(two_d_v) => {
3351                    for part in two_d_v {
3352                        for expr in part {
3353                            f(expr);
3354                        }
3355                    }
3356                }
3357                AbstractLiteral::Matrix(v, _domain) => {
3358                    for expr in v {
3359                        f(expr);
3360                    }
3361                }
3362                AbstractLiteral::Record(rs) => {
3363                    for r in rs {
3364                        f(&r.value);
3365                    }
3366                }
3367                AbstractLiteral::Sequence(v) => {
3368                    for expr in v {
3369                        f(expr);
3370                    }
3371                }
3372                AbstractLiteral::Function(vs) => {
3373                    for (a, b) in vs {
3374                        f(a);
3375                        f(b);
3376                    }
3377                }
3378                AbstractLiteral::Variant(v) => {
3379                    f(&v.value);
3380                }
3381                AbstractLiteral::Relation(vs) => {
3382                    for exprs in vs {
3383                        for expr in exprs {
3384                            f(expr);
3385                        }
3386                    }
3387                }
3388                AbstractLiteral::Permutation(cycles) => {
3389                    for cycle in cycles {
3390                        for expr in cycle {
3391                            f(expr);
3392                        }
3393                    }
3394                }
3395            },
3396            Expression::Root(_, vs) => {
3397                for expr in vs {
3398                    f(expr);
3399                }
3400            }
3401
3402            // Moo<Expression>
3403            Expression::DominanceRelation(_, m1)
3404            | Expression::TypeAnnotation(_, m1, _)
3405            | Expression::DomainAnnotation(_, m1, _)
3406            | Expression::ToInt(_, m1)
3407            | Expression::Abs(_, m1)
3408            | Expression::Sum(_, m1)
3409            | Expression::Product(_, m1)
3410            | Expression::Min(_, m1)
3411            | Expression::Max(_, m1)
3412            | Expression::Not(_, m1)
3413            | Expression::Or(_, m1)
3414            | Expression::And(_, m1)
3415            | Expression::Neg(_, m1)
3416            | Expression::PermInverse(_, m1)
3417            | Expression::Defined(_, m1)
3418            | Expression::AllDiff(_, m1)
3419            | Expression::SmtDistinct(_, m1)
3420            | Expression::Factorial(_, m1)
3421            | Expression::Range(_, m1)
3422            | Expression::Participants(_, m1)
3423            | Expression::Parts(_, m1)
3424            | Expression::ToSet(_, m1)
3425            | Expression::ToMSet(_, m1)
3426            | Expression::ToRelation(_, m1)
3427            | Expression::Card(_, m1)
3428            | Expression::RecordField(_, m1, _)
3429            | Expression::Active(_, m1, _) => {
3430                f(m1);
3431            }
3432
3433            // Moo<Expression> + Moo<Expression>
3434            Expression::Table(_, m1, m2)
3435            | Expression::NegativeTable(_, m1, m2)
3436            | Expression::Bubble(_, m1, m2)
3437            | Expression::Imply(_, m1, m2)
3438            | Expression::Iff(_, m1, m2)
3439            | Expression::Difference(_, m1, m2)
3440            | Expression::Union(_, m1, m2)
3441            | Expression::In(_, m1, m2)
3442            | Expression::Intersect(_, m1, m2)
3443            | Expression::Supset(_, m1, m2)
3444            | Expression::SupsetEq(_, m1, m2)
3445            | Expression::Subset(_, m1, m2)
3446            | Expression::SubsetEq(_, m1, m2)
3447            | Expression::Eq(_, m1, m2)
3448            | Expression::Neq(_, m1, m2)
3449            | Expression::Geq(_, m1, m2)
3450            | Expression::Leq(_, m1, m2)
3451            | Expression::Gt(_, m1, m2)
3452            | Expression::Lt(_, m1, m2)
3453            | Expression::CatchUndef(_, m1, m2)
3454            | Expression::SafeDiv(_, m1, m2)
3455            | Expression::UnsafeDiv(_, m1, m2)
3456            | Expression::SafeMod(_, m1, m2)
3457            | Expression::UnsafeMod(_, m1, m2)
3458            | Expression::UnsafePow(_, m1, m2)
3459            | Expression::SafePow(_, m1, m2)
3460            | Expression::Minus(_, m1, m2)
3461            | Expression::PairwiseSum(_, m1, m2)
3462            | Expression::PairwiseProduct(_, m1, m2)
3463            | Expression::Image(_, m1, m2)
3464            | Expression::ImageSet(_, m1, m2)
3465            | Expression::PreImage(_, m1, m2)
3466            | Expression::Inverse(_, m1, m2)
3467            | Expression::Compose(_, m1, m2)
3468            | Expression::Restrict(_, m1, m2)
3469            | Expression::Apart(_, m1, m2)
3470            | Expression::Together(_, m1, m2)
3471            | Expression::Party(_, m1, m2)
3472            | Expression::LexLt(_, m1, m2)
3473            | Expression::LexLeq(_, m1, m2)
3474            | Expression::LexGt(_, m1, m2)
3475            | Expression::LexGeq(_, m1, m2)
3476            | Expression::Subsequence(_, m1, m2)
3477            | Expression::Substring(_, m1, m2) => {
3478                f(m1);
3479                f(m2);
3480            }
3481
3482            // Moo<Expression> + Vec<Expression>
3483            Expression::UnsafeIndex(_, m, vs) | Expression::SafeIndex(_, m, vs) => {
3484                f(m);
3485                for v in vs {
3486                    f(v);
3487                }
3488            }
3489            // Moo<Expression> + Vec<Option<Expression>>
3490            Expression::UnsafeSlice(_, m, vs)
3491            | Expression::SafeSlice(_, m, vs)
3492            | Expression::RelationProj(_, m, vs) => {
3493                f(m);
3494                for e in vs.iter().flatten() {
3495                    f(e);
3496                }
3497            }
3498
3499            // Moo<Expression> + Moo<Expression> + Moo<Expression>
3500            Expression::AtLeast(_, m1, m2, m3)
3501            | Expression::AtMost(_, m1, m2, m3)
3502            | Expression::Gcc(_, m1, m2, m3)
3503            | Expression::GccWeak(_, m1, m2, m3) => {
3504                f(m1);
3505                f(m2);
3506                f(m3);
3507            }
3508
3509            // Moo<Expression> + Moo<Expression> (two-arg globals)
3510            Expression::AllDifferentExcept(_, m1, m2) | Expression::ElementId(_, m1, m2) => {
3511                f(m1);
3512                f(m2);
3513            }
3514
3515            // Moo<Expression> + DomainPtr
3516            Expression::InDomain(_, m, _) => {
3517                f(m);
3518            }
3519
3520            // Option<Moo<Expression>> + Moo<Expression>
3521            Expression::Flatten(_, opt, m) => {
3522                if let Some(e) = opt {
3523                    f(e);
3524                }
3525                f(m);
3526            }
3527
3528            // Moo<Expression> + AttrName + Option<Moo<Expression>>
3529            Expression::AttributeAsConstraint(_, target, _, val) => {
3530                f(target);
3531                if let Some(v) = val {
3532                    f(v);
3533                }
3534            }
3535
3536            // Moo<Expression> + Atom
3537            Expression::MinionReify(_, m, _) | Expression::MinionReifyImply(_, m, _) => {
3538                f(m);
3539            }
3540
3541            // Reference + Moo<Expression>
3542            Expression::AuxDeclaration(_, _, m) => {
3543                f(m);
3544            }
3545
3546            // SATIntEncoding + Moo<Expression> + (i32, i32)
3547            Expression::SATInt(_, _, m, _) => {
3548                f(m);
3549            }
3550
3551            // No Expression children
3552            Expression::Comprehension(_, _)
3553            | Expression::Atomic(_, _)
3554            | Expression::FromSolution(_, _)
3555            | Expression::Metavar(_, _)
3556            | Expression::FlatAbsEq(_, _, _)
3557            | Expression::FlatMinusEq(_, _, _)
3558            | Expression::FlatProductEq(_, _, _, _)
3559            | Expression::MinionDivEqUndefZero(_, _, _, _)
3560            | Expression::MinionModuloEqUndefZero(_, _, _, _)
3561            | Expression::MinionPow(_, _, _, _)
3562            | Expression::FlatAllDiff(_, _)
3563            | Expression::FlatMinEq(_, _, _)
3564            | Expression::FlatSumGeq(_, _, _)
3565            | Expression::FlatSumLeq(_, _, _)
3566            | Expression::FlatIneq(_, _, _, _)
3567            | Expression::FlatWatchedLiteral(_, _, _)
3568            | Expression::FlatWeightedSumLeq(_, _, _, _)
3569            | Expression::FlatWeightedSumGeq(_, _, _, _)
3570            | Expression::MinionWInIntervalSet(_, _, _)
3571            | Expression::MinionWInSet(_, _, _)
3572            | Expression::MinionElementOne(_, _, _, _)
3573            | Expression::FlatLexLt(_, _, _)
3574            | Expression::FlatLexLeq(_, _, _) => {}
3575        }
3576    }
3577
3578    /// Visits this expression and all of its expression descendants by reference.
3579    pub fn for_each_expression<'a>(&'a self, f: &mut impl FnMut(&'a Expression)) {
3580        f(self);
3581        self.for_each_expr_child(&mut |child| child.for_each_expression(f));
3582    }
3583
3584    /// Returns whether this expression or any expression below it satisfies `predicate`.
3585    ///
3586    /// Unlike [`Uniplate::universe`], this does not construct an owned copy of the traversed tree.
3587    pub fn any_expression(&self, mut predicate: impl FnMut(&Expression) -> bool) -> bool {
3588        fn visit(expr: &Expression, predicate: &mut impl FnMut(&Expression) -> bool) -> bool {
3589            if predicate(expr) {
3590                return true;
3591            }
3592
3593            let mut found = false;
3594            expr.for_each_expr_child(&mut |child| {
3595                if !found {
3596                    found = visit(child, predicate);
3597                }
3598            });
3599            found
3600        }
3601
3602        visit(self, &mut predicate)
3603    }
3604
3605    /// Returns whether any expression strictly below this one satisfies `predicate`.
3606    pub fn any_expression_descendant(
3607        &self,
3608        mut predicate: impl FnMut(&Expression) -> bool,
3609    ) -> bool {
3610        let mut found = false;
3611        self.for_each_expr_child(&mut |child| {
3612            if !found {
3613                found = child.any_expression(&mut predicate);
3614            }
3615        });
3616        found
3617    }
3618}
3619
3620impl Expression {
3621    /// Invalidates the cached content hash on this expression only.
3622    pub(crate) fn invalidate_cached_content_hash(&self) {
3623        let metadata = self.meta_ref();
3624        metadata
3625            .cached_content_hash
3626            .store(NO_HASH, Ordering::Relaxed);
3627    }
3628
3629    /// Returns the cached expression content hash, computing and storing it when absent.
3630    pub(crate) fn cached_content_hash(&self) -> u64 {
3631        let stored = self.meta_ref().cached_content_hash.load(Ordering::Relaxed);
3632        if stored != NO_HASH {
3633            HASH_HITS.fetch_add(1, Ordering::Relaxed);
3634            return stored;
3635        }
3636        HASH_MISSES.fetch_add(1, Ordering::Relaxed);
3637        self.calculate_content_hash()
3638    }
3639
3640    /// Computes an expression content hash from precomputed child node hashes.
3641    ///
3642    /// Child hashes must be supplied in the same order as [`Uniplate::children`] for this
3643    /// expression.
3644    #[allow(unused_variables)]
3645    pub(crate) fn content_hash_from_child_hashes(
3646        &self,
3647        child_hashes: &mut impl Iterator<Item = u64>,
3648    ) -> u64 {
3649        fn child_hash(child_hashes: &mut impl Iterator<Item = u64>) -> u64 {
3650            child_hashes
3651                .next()
3652                .expect("expression content hash missing child hash")
3653        }
3654
3655        let mut hasher = DefaultHasher::new();
3656        std::mem::discriminant(self).hash(&mut hasher);
3657        match self {
3658            // Special Case
3659            Expression::AbstractLiteral(_, alit) => match alit {
3660                AbstractLiteral::Set(v)
3661                | AbstractLiteral::MSet(v)
3662                | AbstractLiteral::Tuple(v)
3663                | AbstractLiteral::Sequence(v) => {
3664                    for expr in v {
3665                        child_hash(child_hashes).hash(&mut hasher);
3666                    }
3667                }
3668                AbstractLiteral::Matrix(v, domain) => {
3669                    domain.hash(&mut hasher);
3670                    for expr in v {
3671                        child_hash(child_hashes).hash(&mut hasher);
3672                    }
3673                }
3674                AbstractLiteral::Record(rs) => {
3675                    for r in rs {
3676                        r.name.hash(&mut hasher);
3677                        child_hash(child_hashes).hash(&mut hasher);
3678                    }
3679                }
3680                AbstractLiteral::Function(vs) => {
3681                    for (a, b) in vs {
3682                        child_hash(child_hashes).hash(&mut hasher);
3683                        child_hash(child_hashes).hash(&mut hasher);
3684                    }
3685                }
3686                AbstractLiteral::Variant(v) => {
3687                    v.name.hash(&mut hasher);
3688                    child_hash(child_hashes).hash(&mut hasher);
3689                }
3690                AbstractLiteral::Relation(v) => {
3691                    for exprs in v {
3692                        for expr in exprs {
3693                            child_hash(child_hashes).hash(&mut hasher);
3694                        }
3695                    }
3696                }
3697                AbstractLiteral::Partition(v) => {
3698                    for exprs in v {
3699                        for expr in exprs {
3700                            child_hash(child_hashes).hash(&mut hasher);
3701                        }
3702                    }
3703                }
3704                AbstractLiteral::Permutation(v) => {
3705                    for exprs in v {
3706                        for expr in exprs {
3707                            child_hash(child_hashes).hash(&mut hasher);
3708                        }
3709                    }
3710                }
3711            },
3712            Expression::Root(_, vs) => {
3713                for expr in vs {
3714                    child_hash(child_hashes).hash(&mut hasher);
3715                }
3716            }
3717
3718            // Moo<Expression>
3719            Expression::DominanceRelation(_, m1)
3720            | Expression::ToInt(_, m1)
3721            | Expression::Abs(_, m1)
3722            | Expression::Sum(_, m1)
3723            | Expression::Product(_, m1)
3724            | Expression::Min(_, m1)
3725            | Expression::Max(_, m1)
3726            | Expression::Not(_, m1)
3727            | Expression::Or(_, m1)
3728            | Expression::And(_, m1)
3729            | Expression::Neg(_, m1)
3730            | Expression::PermInverse(_, m1)
3731            | Expression::Defined(_, m1)
3732            | Expression::AllDiff(_, m1)
3733            | Expression::SmtDistinct(_, m1)
3734            | Expression::Factorial(_, m1)
3735            | Expression::Participants(_, m1)
3736            | Expression::Parts(_, m1)
3737            | Expression::Range(_, m1)
3738            | Expression::ToSet(_, m1)
3739            | Expression::ToMSet(_, m1)
3740            | Expression::ToRelation(_, m1)
3741            | Expression::Card(_, m1) => {
3742                child_hash(child_hashes).hash(&mut hasher);
3743            }
3744            Expression::TypeAnnotation(_, m1, domain) => {
3745                child_hash(child_hashes).hash(&mut hasher);
3746                domain.hash(&mut hasher);
3747            }
3748            Expression::DomainAnnotation(_, m1, domain) => {
3749                child_hash(child_hashes).hash(&mut hasher);
3750                domain.hash(&mut hasher);
3751            }
3752
3753            // Moo<Expression> + Moo<Expression>
3754            Expression::Table(_, m1, m2)
3755            | Expression::NegativeTable(_, m1, m2)
3756            | Expression::Bubble(_, m1, m2)
3757            | Expression::Imply(_, m1, m2)
3758            | Expression::Iff(_, m1, m2)
3759            | Expression::Difference(_, m1, m2)
3760            | Expression::Union(_, m1, m2)
3761            | Expression::In(_, m1, m2)
3762            | Expression::Intersect(_, m1, m2)
3763            | Expression::Supset(_, m1, m2)
3764            | Expression::SupsetEq(_, m1, m2)
3765            | Expression::Subset(_, m1, m2)
3766            | Expression::SubsetEq(_, m1, m2)
3767            | Expression::Eq(_, m1, m2)
3768            | Expression::Neq(_, m1, m2)
3769            | Expression::Geq(_, m1, m2)
3770            | Expression::Leq(_, m1, m2)
3771            | Expression::Gt(_, m1, m2)
3772            | Expression::Lt(_, m1, m2)
3773            | Expression::Apart(_, m1, m2)
3774            | Expression::Together(_, m1, m2)
3775            | Expression::Party(_, m1, m2)
3776            | Expression::CatchUndef(_, m1, m2)
3777            | Expression::SafeDiv(_, m1, m2)
3778            | Expression::UnsafeDiv(_, m1, m2)
3779            | Expression::SafeMod(_, m1, m2)
3780            | Expression::UnsafeMod(_, m1, m2)
3781            | Expression::UnsafePow(_, m1, m2)
3782            | Expression::SafePow(_, m1, m2)
3783            | Expression::Minus(_, m1, m2)
3784            | Expression::PairwiseSum(_, m1, m2)
3785            | Expression::PairwiseProduct(_, m1, m2)
3786            | Expression::Image(_, m1, m2)
3787            | Expression::ImageSet(_, m1, m2)
3788            | Expression::PreImage(_, m1, m2)
3789            | Expression::Inverse(_, m1, m2)
3790            | Expression::Compose(_, m1, m2)
3791            | Expression::Restrict(_, m1, m2)
3792            | Expression::LexLt(_, m1, m2)
3793            | Expression::LexLeq(_, m1, m2)
3794            | Expression::LexGt(_, m1, m2)
3795            | Expression::LexGeq(_, m1, m2)
3796            | Expression::Subsequence(_, m1, m2)
3797            | Expression::Substring(_, m1, m2) => {
3798                child_hash(child_hashes).hash(&mut hasher);
3799                child_hash(child_hashes).hash(&mut hasher);
3800            }
3801            // Moo<Expression> + Vec<Expression>
3802            Expression::UnsafeIndex(_, m, vs) | Expression::SafeIndex(_, m, vs) => {
3803                child_hash(child_hashes).hash(&mut hasher);
3804                for v in vs {
3805                    child_hash(child_hashes).hash(&mut hasher);
3806                }
3807            }
3808
3809            // Moo<Expression> + Vec<Option<Expression>>
3810            Expression::UnsafeSlice(_, m, vs)
3811            | Expression::SafeSlice(_, m, vs)
3812            | Expression::RelationProj(_, m, vs) => {
3813                child_hash(child_hashes).hash(&mut hasher);
3814                for v in vs {
3815                    match v {
3816                        Some(e) => child_hash(child_hashes).hash(&mut hasher),
3817                        None => 0u64.hash(&mut hasher),
3818                    }
3819                }
3820            }
3821
3822            // Moo<Expression> + Moo<Expression> + Moo<Expression>
3823            Expression::AtLeast(_, m1, m2, m3)
3824            | Expression::AtMost(_, m1, m2, m3)
3825            | Expression::Gcc(_, m1, m2, m3)
3826            | Expression::GccWeak(_, m1, m2, m3) => {
3827                child_hash(child_hashes).hash(&mut hasher);
3828                child_hash(child_hashes).hash(&mut hasher);
3829                child_hash(child_hashes).hash(&mut hasher);
3830            }
3831
3832            // Moo<Expression> + Moo<Expression> (two-arg globals)
3833            Expression::AllDifferentExcept(_, m1, m2) | Expression::ElementId(_, m1, m2) => {
3834                child_hash(child_hashes).hash(&mut hasher);
3835                child_hash(child_hashes).hash(&mut hasher);
3836            }
3837
3838            // Moo<Expression> + Name
3839            Expression::RecordField(_, m, n) | Expression::Active(_, m, n) => {
3840                child_hash(child_hashes).hash(&mut hasher);
3841                n.hash(&mut hasher);
3842            }
3843
3844            // Moo<Expression> + DomainPtr
3845            Expression::InDomain(_, m, d) => {
3846                child_hash(child_hashes).hash(&mut hasher);
3847                d.hash(&mut hasher);
3848            }
3849
3850            // Option<Moo<Expression>> + Moo<Expression>
3851            Expression::Flatten(_, opt, m) => {
3852                if let Some(e) = opt {
3853                    child_hash(child_hashes).hash(&mut hasher);
3854                }
3855                child_hash(child_hashes).hash(&mut hasher);
3856            }
3857
3858            // Moo<Expression> + AttrName + Option<Moo<Expression>>
3859            Expression::AttributeAsConstraint(_, target, attr, val) => {
3860                child_hash(child_hashes).hash(&mut hasher);
3861                attr.hash(&mut hasher);
3862                if let Some(v) = val {
3863                    child_hash(child_hashes).hash(&mut hasher);
3864                }
3865            }
3866
3867            // Moo<Expression> + Atom
3868            Expression::MinionReify(_, m, a) | Expression::MinionReifyImply(_, m, a) => {
3869                child_hash(child_hashes).hash(&mut hasher);
3870                a.hash(&mut hasher);
3871            }
3872
3873            // Reference + Moo<Expression>
3874            Expression::AuxDeclaration(_, r, m) => {
3875                r.hash(&mut hasher);
3876                child_hash(child_hashes).hash(&mut hasher);
3877            }
3878
3879            // SATIntEncoding + Moo<Expression> + (i32, i32)
3880            Expression::SATInt(_, enc, m, bounds) => {
3881                enc.hash(&mut hasher);
3882                child_hash(child_hashes).hash(&mut hasher);
3883                bounds.hash(&mut hasher);
3884            }
3885
3886            // Non-Expression Moo types - hash normally
3887            Expression::Comprehension(_, c) => c.hash(&mut hasher),
3888
3889            // Leaf types - no Expression children
3890            Expression::Atomic(_, a) => a.hash(&mut hasher),
3891            Expression::FromSolution(_, a) => a.hash(&mut hasher),
3892            Expression::Metavar(_, u) => u.hash(&mut hasher),
3893
3894            // Two Moo<Atom>
3895            Expression::FlatAbsEq(_, a1, a2) | Expression::FlatMinusEq(_, a1, a2) => {
3896                a1.hash(&mut hasher);
3897                a2.hash(&mut hasher);
3898            }
3899
3900            // Three Moo<Atom>
3901            Expression::FlatProductEq(_, a1, a2, a3)
3902            | Expression::MinionDivEqUndefZero(_, a1, a2, a3)
3903            | Expression::MinionModuloEqUndefZero(_, a1, a2, a3)
3904            | Expression::MinionPow(_, a1, a2, a3) => {
3905                a1.hash(&mut hasher);
3906                a2.hash(&mut hasher);
3907                a3.hash(&mut hasher);
3908            }
3909
3910            // Vec<Atom>
3911            Expression::FlatAllDiff(_, vs) => {
3912                for v in vs {
3913                    v.hash(&mut hasher);
3914                }
3915            }
3916
3917            // Vec<Atom> + Atom
3918            Expression::FlatMinEq(_, vs, a)
3919            | Expression::FlatSumGeq(_, vs, a)
3920            | Expression::FlatSumLeq(_, vs, a) => {
3921                for v in vs {
3922                    v.hash(&mut hasher);
3923                }
3924                a.hash(&mut hasher);
3925            }
3926
3927            // Moo<Atom> + Moo<Atom> + Box<Literal>
3928            Expression::FlatIneq(_, a1, a2, lit) => {
3929                a1.hash(&mut hasher);
3930                a2.hash(&mut hasher);
3931                lit.hash(&mut hasher);
3932            }
3933
3934            // Reference + Literal
3935            Expression::FlatWatchedLiteral(_, r, l) => {
3936                r.hash(&mut hasher);
3937                l.hash(&mut hasher);
3938            }
3939
3940            // Vec<Literal> + Vec<Atom> + Moo<Atom>
3941            Expression::FlatWeightedSumLeq(_, lits, atoms, a)
3942            | Expression::FlatWeightedSumGeq(_, lits, atoms, a) => {
3943                for l in lits {
3944                    l.hash(&mut hasher);
3945                }
3946                for at in atoms {
3947                    at.hash(&mut hasher);
3948                }
3949                a.hash(&mut hasher);
3950            }
3951
3952            // Atom + Vec<i32>
3953            Expression::MinionWInIntervalSet(_, a, vs) | Expression::MinionWInSet(_, a, vs) => {
3954                a.hash(&mut hasher);
3955                for v in vs {
3956                    v.hash(&mut hasher);
3957                }
3958            }
3959
3960            // Vec<Atom> + Moo<Atom> + Moo<Atom>
3961            Expression::MinionElementOne(_, vs, a1, a2) => {
3962                for v in vs {
3963                    v.hash(&mut hasher);
3964                }
3965                a1.hash(&mut hasher);
3966                a2.hash(&mut hasher);
3967            }
3968
3969            // Vec<Atom> + Vec<Atom>
3970            Expression::FlatLexLt(_, v1, v2) | Expression::FlatLexLeq(_, v1, v2) => {
3971                for v in v1 {
3972                    v.hash(&mut hasher);
3973                }
3974                for v in v2 {
3975                    v.hash(&mut hasher);
3976                }
3977            }
3978        };
3979
3980        hasher.finish()
3981    }
3982
3983    /// Computes an expression content hash that ignores metadata except for child content hashes.
3984    pub(crate) fn calculate_content_hash(&self) -> u64 {
3985        let mut hashes = Vec::new();
3986        self.for_each_expr_child(&mut |child| hashes.push(child.cached_content_hash()));
3987        let mut child_hashes = hashes.into_iter();
3988        let result = self.content_hash_from_child_hashes(&mut child_hashes);
3989        self.meta_ref()
3990            .cached_content_hash
3991            .store(result, Ordering::Relaxed);
3992        result
3993    }
3994}
3995
3996#[cfg(test)]
3997mod tests {
3998    use crate::matrix_expr;
3999
4000    use super::*;
4001
4002    #[test]
4003    fn test_domain_of_constant_sum() {
4004        let c1 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1)));
4005        let c2 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2)));
4006        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![c1, c2]));
4007        assert_eq!(sum.domain_of(), Some(Domain::int(vec![Range::Single(3)])));
4008    }
4009
4010    #[test]
4011    fn test_domain_of_constant_invalid_type() {
4012        let c1 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1)));
4013        let c2 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)));
4014        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![c1, c2]));
4015        assert_eq!(sum.domain_of(), None);
4016    }
4017
4018    #[test]
4019    fn test_domain_of_empty_sum() {
4020        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![]));
4021        assert_eq!(sum.domain_of(), None);
4022    }
4023
4024    /// Product domain inference must not panic when bound corner-products overflow i32.
4025    #[test]
4026    fn test_domain_of_product_overflow_returns_none() {
4027        let mk_var = |name: &str| {
4028            Expression::Atomic(
4029                Metadata::new(),
4030                Atom::Reference(Reference::new(DeclarationPtr::new_find(
4031                    Name::User(name.into()),
4032                    Domain::int(vec![Range::Bounded(0, 711)]),
4033                ))),
4034            )
4035        };
4036        let product = Expression::Product(
4037            Metadata::new(),
4038            Moo::new(matrix_expr![
4039                mk_var("item1"),
4040                mk_var("item2"),
4041                mk_var("item3"),
4042                mk_var("item4")
4043            ]),
4044        );
4045        assert_eq!(product.domain_of(), None);
4046    }
4047
4048    #[test]
4049    fn mset_union_domain_adds_finite_operand_bounds() {
4050        let lhs = DeclarationPtr::new_find(
4051            Name::user("xs"),
4052            Domain::mset(
4053                MSetAttr::new_max_size(6).with_representation("counts"),
4054                Domain::int(vec![Range::Bounded(1, 999)]),
4055            ),
4056        );
4057        let rhs = Expression::AbstractLiteral(
4058            Metadata::new(),
4059            AbstractLiteral::MSet(vec![1.into(), 2.into()]),
4060        );
4061        let union = Expression::Union(
4062            Metadata::new(),
4063            Moo::new(Expression::from(Reference::new(lhs))),
4064            Moo::new(rhs),
4065        );
4066
4067        let domain = union.domain_of().expect("multiset union has a domain");
4068        let (attrs, inner) = domain.as_mset_ground().expect("ground multiset domain");
4069        assert_eq!(attrs.size, Range::Bounded(2, 8));
4070        assert_eq!(attrs.occurrence, Range::Bounded(1, 8));
4071        assert_eq!(attrs.representation.as_deref(), Some("counts"));
4072        assert_eq!(
4073            inner.as_ref(),
4074            &GroundDomain::Int(vec![Range::Bounded(1, 999)])
4075        );
4076    }
4077
4078    #[test]
4079    fn list_inspection_borrows_expression_elements_without_cloning_the_list() {
4080        let list = matrix_expr![
4081            Expression::from(Literal::Int(1)),
4082            Expression::from(Literal::Int(2))
4083        ];
4084
4085        assert_eq!(list.list_len(), Some(2));
4086        assert!(list.is_list());
4087        assert!(matches!(list.unwrap_list_cow(), Some(Cow::Borrowed(_))));
4088    }
4089
4090    #[test]
4091    fn list_inspection_materialises_literal_elements_only_when_they_are_requested() {
4092        let list = Expression::Atomic(
4093            Metadata::new(),
4094            Atom::Literal(Literal::AbstractLiteral(
4095                AbstractLiteral::matrix_implied_indices(vec![Literal::Int(1), Literal::Int(2)]),
4096            )),
4097        );
4098
4099        assert_eq!(list.list_len(), Some(2));
4100        assert!(list.is_list());
4101        assert!(matches!(list.unwrap_list_cow(), Some(Cow::Owned(_))));
4102        assert_eq!(
4103            list.unwrap_list_cow().map(Cow::into_owned),
4104            list.unwrap_list()
4105        );
4106    }
4107
4108    #[test]
4109    fn list_length_looks_through_annotations() {
4110        let list = Expression::DomainAnnotation(
4111            Metadata::new(),
4112            Moo::new(matrix_expr![Expression::from(Literal::Int(1))]),
4113            Domain::int(vec![Range::Bounded(0, 1)]),
4114        );
4115
4116        assert_eq!(list.list_len(), Some(1));
4117        assert!(matches!(list.unwrap_list_cow(), Some(Cow::Borrowed(_))));
4118    }
4119}