Skip to main content

conjure_cp_core/ast/
expressions.rs

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