1
use std::collections::{HashSet, VecDeque};
2
use std::fmt::{Display, Formatter};
3
use std::hash::{DefaultHasher, Hash, Hasher};
4
use std::sync::atomic::{AtomicU64, Ordering};
5

            
6
static HASH_HITS: AtomicU64 = AtomicU64::new(0);
7
static HASH_MISSES: AtomicU64 = AtomicU64::new(0);
8

            
9
pub fn print_hash_stats() {
10
    println!(
11
        "Expression hash stats: hits={}, misses={}",
12
        HASH_HITS.load(Ordering::Relaxed),
13
        HASH_MISSES.load(Ordering::Relaxed)
14
    );
15
}
16
use tracing::trace;
17

            
18
use conjure_cp_enum_compatibility_macro::{document_compatibility, generate_discriminants};
19
use itertools::Itertools;
20
use serde::{Deserialize, Serialize};
21
use tree_morph::cache::CacheHashable;
22
use ustr::Ustr;
23

            
24
use polyquine::Quine;
25
use uniplate::{Biplate, Uniplate};
26

            
27
use crate::ast::FuncAttr;
28
use crate::ast::metadata::NO_HASH;
29
use crate::bug;
30

            
31
use super::abstract_comprehension::AbstractComprehension;
32
use super::ac_operators::ACOperatorKind;
33
use super::categories::{Category, CategoryOf};
34
use super::comprehension::Comprehension;
35
use super::declaration::DeclarationKind;
36
use super::domains::HasDomain as _;
37
use super::pretty::{pretty_expressions_as_top_level, pretty_vec};
38
use super::records::Field;
39
use super::sat_encoding::SATIntEncoding;
40
use super::{
41
    AbstractLiteral, Atom, DeclarationPtr, Domain, DomainPtr, GroundDomain, IntVal, JectivityAttr,
42
    Literal, MSetAttr, Metadata, Model, Moo, Name, PartialityAttr, Range, Reference, RelAttr,
43
    ReturnType, SetAttr, SymbolTable, SymbolTablePtr, Typeable, UnresolvedDomain, matrix,
44
};
45

            
46
// Ensure that this type doesn't get too big
47
//
48
// If you triggered this assertion, you either made a variant of this enum that is too big, or you
49
// made Name,Literal,AbstractLiteral,Atom bigger, which made this bigger! To fix this, put some
50
// stuff in boxes.
51
//
52
// Enums take the size of their largest variant, so an enum with mostly small variants and a few
53
// large ones wastes memory... A larger Expression type also slows down Oxide.
54
//
55
// For more information, and more details on type sizes and how to measure them, see the commit
56
// message for 6012de809 (perf: reduce size of AST types, 2025-06-18).
57
//
58
// You can also see type sizes in the rustdoc documentation, generated by ./tools/gen_docs.sh
59
//
60
// https://github.com/conjure-cp/conjure-oxide/commit/6012de8096ca491ded91ecec61352fdf4e994f2e
61

            
62
// TODO: box all usages of Metadata to bring this down a bit more - I have added variants to
63
// ReturnType, and Metadata contains ReturnType, so Metadata has got bigger. Metadata will get a
64
// lot bigger still when we start using it for memoisation, so it should really be
65
// boxed ~niklasdewally
66

            
67
// expect size of Expression to be 112 bytes
68
static_assertions::assert_eq_size!([u8; 112], Expression);
69

            
70
/// Represents different types of expressions used to define rules and constraints in the model.
71
///
72
/// The `Expression` enum includes operations, constants, and variable references
73
/// used to build rules and conditions for the model.
74
#[generate_discriminants]
75
#[document_compatibility]
76
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Uniplate, Quine)]
77
#[biplate(to=AbstractComprehension)]
78
#[biplate(to=AbstractLiteral<Expression>)]
79
#[biplate(to=AbstractLiteral<Literal>)]
80
#[biplate(to=Atom)]
81
#[biplate(to=Comprehension)]
82
#[biplate(to=DeclarationPtr)]
83
#[biplate(to=DomainPtr)]
84
#[biplate(to=Literal)]
85
#[biplate(to=Metadata)]
86
#[biplate(to=Name)]
87
#[biplate(to=Option<Expression>)]
88
#[biplate(to=Field<Expression>)]
89
#[biplate(to=Field<Literal>)]
90
#[biplate(to=Reference)]
91
#[biplate(to=Model)]
92
#[biplate(to=SymbolTable)]
93
#[biplate(to=SymbolTablePtr)]
94
#[biplate(to=Vec<Expression>)]
95
#[path_prefix(conjure_cp::ast)]
96
pub enum Expression {
97
    AbstractLiteral(Metadata, AbstractLiteral<Expression>),
98
    /// The top of the model
99
    Root(Metadata, Vec<Expression>),
100

            
101
    /// An expression representing "A is valid as long as B is true"
102
    /// Turns into a conjunction when it reaches a boolean context
103
    Bubble(Metadata, Moo<Expression>, Moo<Expression>),
104

            
105
    /// A comprehension.
106
    ///
107
    /// The inside of the comprehension opens a new scope.
108
    // todo (gskorokhod): Comprehension contains a symbol table which contains a bunch of pointers.
109
    // This makes implementing Quine tricky (it doesnt support Rc, by design). Skip it for now.
110
    #[polyquine_skip]
111
    Comprehension(Metadata, Moo<Comprehension>),
112

            
113
    /// Higher-level abstract comprehension
114
    #[polyquine_skip] // no idea what this is lol but it stops rustc screaming at me
115
    AbstractComprehension(Metadata, Moo<AbstractComprehension>),
116

            
117
    /// Defines dominance ("Solution A is preferred over Solution B")
118
    DominanceRelation(Metadata, Moo<Expression>),
119
    /// `fromSolution(name)` - Used in dominance relation definitions
120
    FromSolution(Metadata, Moo<Atom>),
121

            
122
    #[polyquine_with(arm = (_, name) => {
123
        let ident = proc_macro2::Ident::new(name.as_str(), proc_macro2::Span::call_site());
124
        quote::quote! { #ident.clone().into() }
125
    })]
126
    Metavar(Metadata, Ustr),
127

            
128
    Atomic(Metadata, Atom),
129

            
130
    /// Asserts that the given variant of a variant expression is in use.
131
    /// See also: [GroundDomain::Variant]
132
    #[compatible(JsonInput)]
133
    Active(Metadata, Moo<Expression>, Name),
134

            
135
    /// Indexing into a record expression, e.g `{foo = 1, bar = true}[foo]`
136
    /// See also: [GroundDomain::Record]
137
    #[compatible(JsonInput)]
138
    RecordField(Metadata, Moo<Expression>, Name),
139

            
140
    /// A matrix index.
141
    ///
142
    /// Defined iff the indices are within their respective index domains.
143
    #[compatible(JsonInput)]
144
    UnsafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
145

            
146
    /// A safe matrix index.
147
    ///
148
    /// See [`Expression::UnsafeIndex`]
149
    #[compatible(SMT)]
150
    SafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
151

            
152
    /// A matrix slice: `a[indices]`.
153
    ///
154
    /// One of the indicies may be `None`, representing the dimension of the matrix we want to take
155
    /// a slice of. For example, for some 3d matrix a, `a[1,..,2]` has the indices
156
    /// `Some(1),None,Some(2)`.
157
    ///
158
    /// It is assumed that the slice only has one "wild-card" dimension and thus is 1 dimensional.
159
    ///
160
    /// Defined iff the defined indices are within their respective index domains.
161
    #[compatible(JsonInput)]
162
    UnsafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
163

            
164
    /// A safe matrix slice: `a[indices]`.
165
    ///
166
    /// See [`Expression::UnsafeSlice`].
167
    SafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
168

            
169
    /// `inDomain(x,domain)` iff `x` is in the domain `domain`.
170
    ///
171
    /// This cannot be constructed from Essence input, nor passed to a solver: this expression is
172
    /// mainly used during the conversion of `UnsafeIndex` and `UnsafeSlice` to `SafeIndex` and
173
    /// `SafeSlice` respectively.
174
    InDomain(Metadata, Moo<Expression>, DomainPtr),
175

            
176
    /// `toInt(b)` casts boolean expression b to an integer.
177
    ///
178
    /// - If b is false, then `toInt(b) == 0`
179
    ///
180
    /// - If b is true, then `toInt(b) == 1`
181
    #[compatible(SMT)]
182
    ToInt(Metadata, Moo<Expression>),
183

            
184
    /// `|x|` - absolute value of `x`
185
    #[compatible(JsonInput, SMT)]
186
    Abs(Metadata, Moo<Expression>),
187

            
188
    /// `sum(<vec_expr>)`
189
    #[compatible(JsonInput, SMT)]
190
    Sum(Metadata, Moo<Expression>),
191

            
192
    /// `a * b * c * ...`
193
    #[compatible(JsonInput, SMT)]
194
    Product(Metadata, Moo<Expression>),
195

            
196
    /// `min(<vec_expr>)`
197
    #[compatible(JsonInput, SMT)]
198
    Min(Metadata, Moo<Expression>),
199

            
200
    /// `max(<vec_expr>)`
201
    #[compatible(JsonInput, SMT)]
202
    Max(Metadata, Moo<Expression>),
203

            
204
    /// `not(a)`
205
    #[compatible(JsonInput, SAT, SMT)]
206
    Not(Metadata, Moo<Expression>),
207

            
208
    /// `or(<vec_expr>)`
209
    #[compatible(JsonInput, SAT, SMT)]
210
    Or(Metadata, Moo<Expression>),
211

            
212
    /// `and(<vec_expr>)`
213
    #[compatible(JsonInput, SAT, SMT)]
214
    And(Metadata, Moo<Expression>),
215

            
216
    /// Ensures that `a->b` (material implication).
217
    #[compatible(JsonInput, SMT)]
218
    Imply(Metadata, Moo<Expression>, Moo<Expression>),
219

            
220
    /// `iff(a, b)` a <-> b
221
    #[compatible(JsonInput, SMT)]
222
    Iff(Metadata, Moo<Expression>, Moo<Expression>),
223

            
224
    #[compatible(JsonInput)]
225
    Union(Metadata, Moo<Expression>, Moo<Expression>),
226

            
227
    #[compatible(JsonInput)]
228
    In(Metadata, Moo<Expression>, Moo<Expression>),
229

            
230
    #[compatible(JsonInput)]
231
    Intersect(Metadata, Moo<Expression>, Moo<Expression>),
232

            
233
    #[compatible(JsonInput)]
234
    Supset(Metadata, Moo<Expression>, Moo<Expression>),
235

            
236
    #[compatible(JsonInput)]
237
    SupsetEq(Metadata, Moo<Expression>, Moo<Expression>),
238

            
239
    #[compatible(JsonInput)]
240
    Subset(Metadata, Moo<Expression>, Moo<Expression>),
241

            
242
    #[compatible(JsonInput)]
243
    SubsetEq(Metadata, Moo<Expression>, Moo<Expression>),
244

            
245
    #[compatible(JsonInput, SMT)]
246
    Eq(Metadata, Moo<Expression>, Moo<Expression>),
247

            
248
    #[compatible(JsonInput, SMT)]
249
    Neq(Metadata, Moo<Expression>, Moo<Expression>),
250

            
251
    #[compatible(JsonInput, SMT)]
252
    Geq(Metadata, Moo<Expression>, Moo<Expression>),
253

            
254
    #[compatible(JsonInput, SMT)]
255
    Leq(Metadata, Moo<Expression>, Moo<Expression>),
256

            
257
    #[compatible(JsonInput, SMT)]
258
    Gt(Metadata, Moo<Expression>, Moo<Expression>),
259

            
260
    #[compatible(JsonInput, SMT)]
261
    Lt(Metadata, Moo<Expression>, Moo<Expression>),
262

            
263
    /// `s subsequence t` tests whether the list of values taken by s occurs in the same order
264
    /// in the list of values taken by t
265
    #[compatible(JsonInput)]
266
    Subsequence(Metadata, Moo<Expression>, Moo<Expression>),
267

            
268
    /// `s substring t` tests whether the list of values taken by s occurs in the same order
269
    /// and contiguously in the list of values taken by t
270
    #[compatible(JsonInput)]
271
    Substring(Metadata, Moo<Expression>, Moo<Expression>),
272

            
273
    /// Division after preventing division by zero, usually with a bubble
274
    #[compatible(SMT)]
275
    SafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
276

            
277
    /// Division with a possibly undefined value (division by 0)
278
    #[compatible(JsonInput)]
279
    UnsafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
280

            
281
    /// Modulo after preventing mod 0, usually with a bubble
282
    #[compatible(SMT)]
283
    SafeMod(Metadata, Moo<Expression>, Moo<Expression>),
284

            
285
    /// Modulo with a possibly undefined value (mod 0)
286
    #[compatible(JsonInput)]
287
    UnsafeMod(Metadata, Moo<Expression>, Moo<Expression>),
288

            
289
    /// Negation: `-x`
290
    #[compatible(JsonInput, SMT)]
291
    Neg(Metadata, Moo<Expression>),
292

            
293
    /// Factorial: `x!` or 'factorial(x)`
294
    #[compatible(JsonInput)]
295
    Factorial(Metadata, Moo<Expression>),
296

            
297
    /// Set of domain values function is defined for
298
    #[compatible(JsonInput)]
299
    Defined(Metadata, Moo<Expression>),
300

            
301
    /// Set of codomain values function is defined for
302
    #[compatible(JsonInput)]
303
    Range(Metadata, Moo<Expression>),
304

            
305
    #[compatible(JsonInput)]
306
    ToSet(Metadata, Moo<Expression>),
307

            
308
    #[compatible(JsonInput)]
309
    ToMSet(Metadata, Moo<Expression>),
310

            
311
    #[compatible(JsonInput)]
312
    ToRelation(Metadata, Moo<Expression>),
313

            
314
    /// Unsafe power`x**y` (possibly undefined)
315
    ///
316
    /// Defined when (X!=0 \\/ Y!=0) /\ Y>=0
317
    #[compatible(JsonInput)]
318
    UnsafePow(Metadata, Moo<Expression>, Moo<Expression>),
319

            
320
    /// `UnsafePow` after preventing undefinedness
321
    SafePow(Metadata, Moo<Expression>, Moo<Expression>),
322

            
323
    /// Flatten matrix operator
324
    /// `flatten(M)` or `flatten(n, M)`
325
    /// where M is a matrix and n is an optional integer argument indicating depth of flattening
326
    Flatten(Metadata, Option<Moo<Expression>>, Moo<Expression>),
327

            
328
    /// `allDiff(<vec_expr>)`
329
    #[compatible(JsonInput)]
330
    AllDiff(Metadata, Moo<Expression>),
331

            
332
    /// `table([x1, x2, ...], [[r11, r12, ...], [r21, r22, ...], ...])`
333
    ///
334
    /// Represents a positive table constraint: the tuple `[x1, x2, ...]` must match one of the
335
    /// allowed rows.
336
    #[compatible(JsonInput)]
337
    Table(Metadata, Moo<Expression>, Moo<Expression>),
338

            
339
    /// `negativeTable([x1, x2, ...], [[r11, r12, ...], [r21, r22, ...], ...])`
340
    ///
341
    /// Represents a negative table constraint: the tuple `[x1, x2, ...]` must NOT match any of the
342
    /// forbidden rows.
343
    #[compatible(JsonInput)]
344
    NegativeTable(Metadata, Moo<Expression>, Moo<Expression>),
345
    /// Binary subtraction operator
346
    ///
347
    /// This is a parser-level construct, and is immediately normalised to `Sum([a,-b])`.
348
    /// TODO: make this compatible with Set Difference calculations - need to change return type and domain for this expression and write a set comprehension rule.
349
    /// have already edited minus_to_sum to prevent this from applying to sets
350
    #[compatible(JsonInput)]
351
    Minus(Metadata, Moo<Expression>, Moo<Expression>),
352

            
353
    /// Partition Operator: test if a list of elements are not all contained in one part of the partition
354
    /// First Expr Arg is a list of elements
355
    /// Second Expr Arg is the partition
356
    #[compatible(JsonInput)]
357
    Apart(Metadata, Moo<Expression>, Moo<Expression>),
358

            
359
    /// Partition Operator: union of all parts of a partition
360
    /// Expr Arg is a partition
361
    #[compatible(JsonInput)]
362
    Participants(Metadata, Moo<Expression>),
363

            
364
    /// Partition Operator: part of partition that contains specified element
365
    /// First Expr Arg is an element that should be contained
366
    /// Second Expr Arg is the partition that should contain that element
367
    #[compatible(JsonInput)]
368
    Party(Metadata, Moo<Expression>, Moo<Expression>),
369

            
370
    /// Partition Operator: partition to its set of parts
371
    /// Expr Arg is the partition from which the parts come from
372
    #[compatible(JsonInput)]
373
    Parts(Metadata, Moo<Expression>),
374

            
375
    /// Partition Operator: test if a list of elements are all in the same part of the partition
376
    /// First Expr Arg is the list of elements to test with
377
    /// Second Expr Arg is the partition to test on
378
    #[compatible(JsonInput)]
379
    Together(Metadata, Moo<Expression>, Moo<Expression>),
380

            
381
    /// Ensures that x=|y| i.e. x is the absolute value of y.
382
    ///
383
    /// Low-level Minion constraint.
384
    ///
385
    /// # See also
386
    ///
387
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#abs)
388
    #[compatible(Minion)]
389
    FlatAbsEq(Metadata, Moo<Atom>, Moo<Atom>),
390

            
391
    /// Ensures that `alldiff([a,b,...])`.
392
    ///
393
    /// Low-level Minion constraint.
394
    ///
395
    /// # See also
396
    ///
397
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#alldiff)
398
    #[compatible(Minion)]
399
    FlatAllDiff(Metadata, Vec<Atom>),
400

            
401
    /// Ensures that sum(vec) >= x.
402
    ///
403
    /// Low-level Minion constraint.
404
    ///
405
    /// # See also
406
    ///
407
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#sumgeq)
408
    #[compatible(Minion)]
409
    FlatSumGeq(Metadata, Vec<Atom>, Atom),
410

            
411
    /// Ensures that sum(vec) <= x.
412
    ///
413
    /// Low-level Minion constraint.
414
    ///
415
    /// # See also
416
    ///
417
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#sumleq)
418
    #[compatible(Minion)]
419
    FlatSumLeq(Metadata, Vec<Atom>, Atom),
420

            
421
    /// `ineq(x,y,k)` ensures that x <= y + k.
422
    ///
423
    /// Low-level Minion constraint.
424
    ///
425
    /// # See also
426
    ///
427
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#ineq)
428
    #[compatible(Minion)]
429
    FlatIneq(Metadata, Moo<Atom>, Moo<Atom>, Box<Literal>),
430

            
431
    /// `w-literal(x,k)` ensures that x == k, where x is a variable and k a constant.
432
    ///
433
    /// Low-level Minion constraint.
434
    ///
435
    /// This is a low-level Minion constraint and you should probably use Eq instead. The main use
436
    /// of w-literal is to convert boolean variables to constraints so that they can be used inside
437
    /// watched-and and watched-or.
438
    ///
439
    /// # See also
440
    ///
441
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#minuseq)
442
    /// + `rules::minion::boolean_literal_to_wliteral`.
443
    #[compatible(Minion)]
444
    #[polyquine_skip]
445
    FlatWatchedLiteral(Metadata, Reference, Literal),
446

            
447
    /// `weightedsumleq(cs,xs,total)` ensures that cs.xs <= total, where cs.xs is the scalar dot
448
    /// product of cs and xs.
449
    ///
450
    /// Low-level Minion constraint.
451
    ///
452
    /// Represents a weighted sum of the form `ax + by + cz + ...`
453
    ///
454
    /// # See also
455
    ///
456
    /// + [Minion
457
    /// documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#weightedsumleq)
458
    FlatWeightedSumLeq(Metadata, Vec<Literal>, Vec<Atom>, Moo<Atom>),
459

            
460
    /// `weightedsumgeq(cs,xs,total)` ensures that cs.xs >= total, where cs.xs is the scalar dot
461
    /// product of cs and xs.
462
    ///
463
    /// Low-level Minion constraint.
464
    ///
465
    /// Represents a weighted sum of the form `ax + by + cz + ...`
466
    ///
467
    /// # See also
468
    ///
469
    /// + [Minion
470
    /// documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#weightedsumleq)
471
    FlatWeightedSumGeq(Metadata, Vec<Literal>, Vec<Atom>, Moo<Atom>),
472

            
473
    /// Ensures that x =-y, where x and y are atoms.
474
    ///
475
    /// Low-level Minion constraint.
476
    ///
477
    /// # See also
478
    ///
479
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#minuseq)
480
    #[compatible(Minion)]
481
    FlatMinusEq(Metadata, Moo<Atom>, Moo<Atom>),
482

            
483
    /// Ensures that x*y=z.
484
    ///
485
    /// Low-level Minion constraint.
486
    ///
487
    /// # See also
488
    ///
489
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#product)
490
    #[compatible(Minion)]
491
    FlatProductEq(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
492

            
493
    /// Ensures that floor(x/y)=z. Always true when y=0.
494
    ///
495
    /// Low-level Minion constraint.
496
    ///
497
    /// # See also
498
    ///
499
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#div_undefzero)
500
    #[compatible(Minion)]
501
    MinionDivEqUndefZero(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
502

            
503
    /// Ensures that x%y=z. Always true when y=0.
504
    ///
505
    /// Low-level Minion constraint.
506
    ///
507
    /// # See also
508
    ///
509
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#mod_undefzero)
510
    #[compatible(Minion)]
511
    MinionModuloEqUndefZero(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
512

            
513
    /// Ensures that `x**y = z`.
514
    ///
515
    /// Low-level Minion constraint.
516
    ///
517
    /// This constraint is false when `y<0` except for `1**y=1` and `(-1)**y=z` (where z is 1 if y
518
    /// is odd and z is -1 if y is even).
519
    ///
520
    /// # See also
521
    ///
522
    /// + [Github comment about `pow` semantics](https://github.com/minion/minion/issues/40#issuecomment-2595914891)
523
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#pow)
524
    MinionPow(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
525

            
526
    /// `reify(constraint,r)` ensures that r=1 iff `constraint` is satisfied, where r is a 0/1
527
    /// variable.
528
    ///
529
    /// Low-level Minion constraint.
530
    ///
531
    /// # See also
532
    ///
533
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#reify)
534
    #[compatible(Minion)]
535
    MinionReify(Metadata, Moo<Expression>, Atom),
536

            
537
    /// `reifyimply(constraint,r)` ensures that `r->constraint`, where r is a 0/1 variable.
538
    /// variable.
539
    ///
540
    /// Low-level Minion constraint.
541
    ///
542
    /// # See also
543
    ///
544
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#reifyimply)
545
    #[compatible(Minion)]
546
    MinionReifyImply(Metadata, Moo<Expression>, Atom),
547

            
548
    /// `w-inintervalset(x, [a1,a2, b1,b2, … ])` ensures that the value of x belongs to one of the
549
    /// intervals {a1,…,a2}, {b1,…,b2} etc.
550
    ///
551
    /// The list of intervals must be given in numerical order.
552
    ///
553
    /// Low-level Minion constraint.
554
    ///
555
    /// # See also
556
    ///>
557
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inintervalset)
558
    #[compatible(Minion)]
559
    MinionWInIntervalSet(Metadata, Atom, Vec<i32>),
560

            
561
    /// `w-inset(x, [v1, v2, … ])` ensures that the value of `x` is one of the explicitly given values `v1`, `v2`, etc.
562
    ///
563
    /// This constraint enforces membership in a specific set of discrete values rather than intervals.
564
    ///
565
    /// The list of values must be given in numerical order.
566
    ///
567
    /// Low-level Minion constraint.
568
    ///
569
    /// # See also
570
    ///
571
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inset)
572
    #[compatible(Minion)]
573
    MinionWInSet(Metadata, Atom, Vec<i32>),
574

            
575
    /// `element_one(vec, i, e)` specifies that `vec[i] = e`. This implies that i is
576
    /// in the range `[1..len(vec)]`.
577
    ///
578
    /// Low-level Minion constraint.
579
    ///
580
    /// # See also
581
    ///
582
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#element_one)
583
    #[compatible(Minion)]
584
    MinionElementOne(Metadata, Vec<Atom>, Moo<Atom>, Moo<Atom>),
585

            
586
    /// Declaration of an auxiliary variable.
587
    ///
588
    /// As with Savile Row, we semantically distinguish this from `Eq`.
589
    #[compatible(Minion)]
590
    #[polyquine_skip]
591
    AuxDeclaration(Metadata, Reference, Moo<Expression>),
592

            
593
    /// 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.
594
    #[compatible(SAT)]
595
    SATInt(Metadata, SATIntEncoding, Moo<Expression>, (i32, i32)),
596

            
597
    /// Addition over a pair of expressions (i.e. a + b) rather than a vec-expr like Expression::Sum.
598
    /// This is for compatibility with backends that do not support addition over vectors.
599
    #[compatible(SMT)]
600
    PairwiseSum(Metadata, Moo<Expression>, Moo<Expression>),
601

            
602
    /// Multiplication over a pair of expressions (i.e. a * b) rather than a vec-expr like Expression::Product.
603
    /// This is for compatibility with backends that do not support multiplication over vectors.
604
    #[compatible(SMT)]
605
    PairwiseProduct(Metadata, Moo<Expression>, Moo<Expression>),
606

            
607
    #[compatible(JsonInput)]
608
    Image(Metadata, Moo<Expression>, Moo<Expression>),
609

            
610
    #[compatible(JsonInput)]
611
    ImageSet(Metadata, Moo<Expression>, Moo<Expression>),
612

            
613
    #[compatible(JsonInput)]
614
    PreImage(Metadata, Moo<Expression>, Moo<Expression>),
615

            
616
    #[compatible(JsonInput)]
617
    Inverse(Metadata, Moo<Expression>, Moo<Expression>),
618

            
619
    #[compatible(JsonInput)]
620
    Restrict(Metadata, Moo<Expression>, Moo<Expression>),
621

            
622
    /// Lexicographical < between two matrices.
623
    ///
624
    /// A <lex B iff: A[i] < B[i] for some i /\ (A[j] > B[j] for some j -> i < j)
625
    /// I.e. A must be less than B at some index i, and if it is greater than B at another index j,
626
    /// then j comes after i.
627
    /// I.e. A must be greater than B at the first index where they differ.
628
    ///
629
    /// E.g. [1, 1] <lex [2, 1] and [1, 1] <lex [1, 2]
630
    LexLt(Metadata, Moo<Expression>, Moo<Expression>),
631

            
632
    /// Lexicographical <= between two matrices
633
    LexLeq(Metadata, Moo<Expression>, Moo<Expression>),
634

            
635
    /// Lexicographical > between two matrices
636
    /// This is a parser-level construct, and is immediately normalised to LexLt(b, a)
637
    LexGt(Metadata, Moo<Expression>, Moo<Expression>),
638

            
639
    /// Lexicographical >= between two matrices
640
    /// This is a parser-level construct, and is immediately normalised to LexLeq(b, a)
641
    LexGeq(Metadata, Moo<Expression>, Moo<Expression>),
642

            
643
    /// Low-level minion constraint. See Expression::LexLt
644
    FlatLexLt(Metadata, Vec<Atom>, Vec<Atom>),
645

            
646
    /// Low-level minion constraint. See Expression::LexLeq
647
    FlatLexLeq(Metadata, Vec<Atom>, Vec<Atom>),
648

            
649
    /// Alters the shape of relations by projection
650
    #[compatible(JsonInput)]
651
    RelationProj(Metadata, Moo<Expression>, Vec<Option<Expression>>),
652

            
653
    /// Cardinality of a collection type
654
    #[compatible(JsonInput)]
655
    Card(Metadata, Moo<Expression>),
656
}
657

            
658
// for the given matrix literal, return a bounded domain from the min to max of applying op to each
659
// child expression.
660
//
661
// Op must be monotonic.
662
//
663
// Returns none if unbounded
664
1832227
fn bounded_i32_domain_for_matrix_literal_monotonic(
665
1832227
    e: &Expression,
666
1832227
    op: fn(i32, i32) -> Option<i32>,
667
1832227
) -> Option<DomainPtr> {
668
    // only care about the elements, not the indices
669
1832227
    let (mut exprs, _) = e.clone().unwrap_matrix_unchecked()?;
670

            
671
    // fold each element's domain into one using op.
672
    //
673
    // here, I assume that op is monotone. This means that the bounds of op([a1,a2],[b1,b2])  for
674
    // the ranges [a1,a2], [b1,b2] will be
675
    // [min(op(a1,b1),op(a2,b1),op(a1,b2),op(a2,b2)),max(op(a1,b1),op(a2,b1),op(a1,b2),op(a2,b2))].
676
    //
677
    // We used to not assume this, and work out the bounds by applying op on the Cartesian product
678
    // of A and B; however, this caused a combinatorial explosion and my computer to run out of
679
    // memory (on the hakank_eprime_xkcd test)...
680
    //Int
681
    // For example, to find the bounds of the intervals [1,4], [1,5] combined using op, we used to do
682
    //  [min(op(1,1), op(1,2),op(1,3),op(1,4),op(1,5),op(2,1)..
683
    //
684
    // +,-,/,* are all monotone, so this assumption should be fine for now...
685

            
686
1810123
    let expr = exprs.pop()?;
687
1810122
    let dom = expr.domain_of()?;
688
1807362
    let resolved = dom.resolve().ok()?;
689
1807002
    let GroundDomain::Int(ranges) = resolved.as_ref() else {
690
25
        return None;
691
    };
692

            
693
1806977
    let (mut current_min, mut current_max) = range_vec_bounds_i32(ranges)?;
694

            
695
2056109
    for expr in exprs {
696
2056109
        let dom = expr.domain_of()?;
697
2055749
        let resolved = dom.resolve().ok()?;
698
2055629
        let GroundDomain::Int(ranges) = resolved.as_ref() else {
699
            return None;
700
        };
701

            
702
2055629
        let (min, max) = range_vec_bounds_i32(ranges)?;
703

            
704
        // all the possible new values for current_min / current_max
705
2055629
        let minmax = op(min, current_max)?;
706
2055629
        let minmin = op(min, current_min)?;
707
2055629
        let maxmin = op(max, current_min)?;
708
2055629
        let maxmax = op(max, current_max)?;
709
2055629
        let vals = [minmax, minmin, maxmin, maxmax];
710

            
711
2055629
        current_min = *vals
712
2055629
            .iter()
713
2055629
            .min()
714
2055629
            .expect("vals iterator should not be empty, and should have a minimum.");
715
2055629
        current_max = *vals
716
2055629
            .iter()
717
2055629
            .max()
718
2055629
            .expect("vals iterator should not be empty, and should have a maximum.");
719
    }
720

            
721
1806497
    if current_min == current_max {
722
1061
        Some(Domain::int(vec![Range::Single(current_min)]))
723
    } else {
724
1805436
        Some(Domain::int(vec![Range::Bounded(current_min, current_max)]))
725
    }
726
1832227
}
727

            
728
20
fn matrix_element_domain(e: &Expression) -> Option<DomainPtr> {
729
20
    let (elem_domain, _) = e.domain_of()?.as_matrix()?;
730
20
    elem_domain.as_ref().as_int()?;
731
20
    Some(elem_domain)
732
20
}
733

            
734
// Returns none if unbounded
735
3862606
fn range_vec_bounds_i32(ranges: &Vec<Range<i32>>) -> Option<(i32, i32)> {
736
3862606
    let mut min = i32::MAX;
737
3862606
    let mut max = i32::MIN;
738
3881822
    for r in ranges {
739
3881822
        match r {
740
1387650
            Range::Single(i) => {
741
1387650
                if *i < min {
742
1368434
                    min = *i;
743
1368434
                }
744
1387650
                if *i > max {
745
1387650
                    max = *i;
746
1387650
                }
747
            }
748
2494172
            Range::Bounded(i, j) => {
749
2494172
                if *i < min {
750
2494172
                    min = *i;
751
2494172
                }
752
2494172
                if *j > max {
753
2494172
                    max = *j;
754
2494172
                }
755
            }
756
            Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => return None,
757
        }
758
    }
759
3862606
    Some((min, max))
760
3862606
}
761

            
762
impl Expression {
763
    /// Returns the possible values of the expression, recursing to leaf expressions
764
9402452
    pub fn domain_of(&self) -> Option<DomainPtr> {
765
9402452
        match self {
766
112
            Expression::Union(_, a, b) => Some(Domain::set(
767
112
                SetAttr::<IntVal>::default(),
768
112
                a.domain_of()?.union(&b.domain_of()?).ok()?,
769
            )),
770
100
            Expression::Intersect(_, a, b) => Some(Domain::set(
771
100
                SetAttr::<IntVal>::default(),
772
100
                a.domain_of()?.intersect(&b.domain_of()?).ok()?,
773
            )),
774
            Expression::In(_, _, _) => Some(Domain::bool()),
775
            Expression::Supset(_, _, _) => Some(Domain::bool()),
776
            Expression::SupsetEq(_, _, _) => Some(Domain::bool()),
777
            Expression::Subset(_, _, _) => Some(Domain::bool()),
778
12
            Expression::SubsetEq(_, _, _) => Some(Domain::bool()),
779
242420
            Expression::AbstractLiteral(_, abslit) => abslit.domain_of(),
780
            Expression::DominanceRelation(_, _) => Some(Domain::bool()),
781
            Expression::FromSolution(_, expr) => Some(expr.domain_of()),
782
144
            Expression::Metavar(_, _) => None,
783
52
            Expression::Comprehension(_, comprehension) => comprehension.domain_of(),
784
            Expression::AbstractComprehension(_, comprehension) => comprehension.domain_of(),
785
5760
            Expression::RecordField(_, rec, field_name) => {
786
5760
                let rec_ents = rec.domain_of()?.as_record()?;
787
                for ent in rec_ents {
788
                    if ent.name.eq(field_name) {
789
                        return Some(ent.value);
790
                    }
791
                }
792
                None
793
            }
794
731348
            Expression::UnsafeIndex(_, matrix, index) | Expression::SafeIndex(_, matrix, index) => {
795
752664
                let dom = matrix.domain_of()?;
796
752664
                if let Some((elem_domain, _)) = dom.as_matrix() {
797
752664
                    return Some(elem_domain);
798
                }
799

            
800
                // may actually use the value in the future
801
                #[allow(clippy::redundant_pattern_matching)]
802
                if let Some(_) = dom.as_tuple() {
803
                    // TODO: We can implement proper indexing for tuples
804
                    return None;
805
                }
806

            
807
                if let Some(doms) = dom.as_variant().or(dom.as_record()) {
808
                    let index_expr = index.first()?;
809
                    return match index_expr {
810
                        Expression::Atomic(_, atom) => {
811
                            let decl = atom.clone().into_declaration();
812
                            for inner_dom in doms {
813
                                if *decl.name() == inner_dom.name {
814
                                    return Some(inner_dom.value);
815
                                }
816
                            }
817
                            None
818
                        }
819
                        _ => None,
820
                    };
821
                }
822

            
823
                bug!(
824
                    "subject of an index operation should support indexing, but got {matrix}: {dom}"
825
                )
826
            }
827
            Expression::UnsafeSlice(_, matrix, indices)
828
480
            | Expression::SafeSlice(_, matrix, indices) => {
829
480
                let sliced_dimension = indices.iter().position(Option::is_none);
830

            
831
480
                let dom = matrix.domain_of()?;
832
480
                let Some((elem_domain, index_domains)) = dom.as_matrix() else {
833
                    bug!("subject of an index operation should be a matrix");
834
                };
835

            
836
480
                match sliced_dimension {
837
480
                    Some(dimension) => Some(Domain::matrix(
838
480
                        elem_domain,
839
480
                        vec![index_domains[dimension].clone()],
840
480
                    )),
841

            
842
                    // same as index
843
                    None => Some(elem_domain),
844
                }
845
            }
846
            Expression::InDomain(_, _, _) => Some(Domain::bool()),
847
6067797
            Expression::Atomic(_, atom) => Some(atom.domain_of()),
848
1233279
            Expression::Sum(_, e) => {
849
5859972
                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| Some(x + y))
850
            }
851
591488
            Expression::Product(_, e) => {
852
2321264
                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| Some(x * y))
853
            }
854
24960
            Expression::Min(_, e) => bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
855
24960
                Some(if x < y { x } else { y })
856
24960
            })
857
4240
            .or_else(|| matrix_element_domain(e)),
858
16340
            Expression::Max(_, e) => bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
859
16320
                Some(if x > y { x } else { y })
860
16320
            })
861
3220
            .or_else(|| matrix_element_domain(e)),
862
2120
            Expression::UnsafeDiv(_, a, b) => a
863
2120
                .domain_of()?
864
2120
                .resolve()
865
2120
                .ok()?
866
2120
                .apply_i32(
867
                    // rust integer division is truncating; however, we want to always round down,
868
                    // including for negative numbers.
869
56800
                    |x, y| {
870
56800
                        if y != 0 {
871
49720
                            Some((x as f32 / y as f32).floor() as i32)
872
                        } else {
873
7080
                            None
874
                        }
875
56800
                    },
876
2120
                    b.domain_of()?.resolve().ok()?.as_ref(),
877
                )
878
2120
                .map(DomainPtr::from)
879
2120
                .ok(),
880
21720
            Expression::SafeDiv(_, a, b) => {
881
                // rust integer division is truncating; however, we want to always round down
882
                // including for negative numbers.
883
21720
                let domain = a
884
21720
                    .domain_of()?
885
21720
                    .resolve()
886
21720
                    .ok()?
887
21720
                    .apply_i32(
888
897680
                        |x, y| {
889
897680
                            if y != 0 {
890
780040
                                Some((x as f32 / y as f32).floor() as i32)
891
                            } else {
892
117640
                                None
893
                            }
894
897680
                        },
895
21720
                        b.domain_of()?.resolve().ok()?.as_ref(),
896
                    )
897
                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
898

            
899
21720
                if let GroundDomain::Int(ranges) = domain {
900
21720
                    let mut ranges = ranges;
901
21720
                    ranges.push(Range::Single(0));
902
21720
                    Some(Domain::int(ranges))
903
                } else {
904
                    bug!("Domain of {self} was not integer")
905
                }
906
            }
907
800
            Expression::UnsafeMod(_, a, b) => a
908
800
                .domain_of()?
909
800
                .resolve()
910
800
                .ok()?
911
800
                .apply_i32(
912
12040
                    |x, y| if y != 0 { Some(x % y) } else { None },
913
800
                    b.domain_of()?.resolve().ok()?.as_ref(),
914
                )
915
800
                .map(DomainPtr::from)
916
800
                .ok(),
917
6960
            Expression::SafeMod(_, a, b) => {
918
6960
                let domain = a
919
6960
                    .domain_of()?
920
6960
                    .resolve()
921
6960
                    .ok()?
922
6960
                    .apply_i32(
923
250320
                        |x, y| if y != 0 { Some(x % y) } else { None },
924
6960
                        b.domain_of()?.resolve().ok()?.as_ref(),
925
                    )
926
                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
927

            
928
6960
                if let GroundDomain::Int(ranges) = domain {
929
6960
                    let mut ranges = ranges;
930
6960
                    ranges.push(Range::Single(0));
931
6960
                    Some(Domain::int(ranges))
932
                } else {
933
                    bug!("Domain of {self} was not integer")
934
                }
935
            }
936
5380
            Expression::SafePow(_, a, b) | Expression::UnsafePow(_, a, b) => a
937
5380
                .domain_of()?
938
5380
                .resolve()
939
5380
                .ok()?
940
5380
                .apply_i32(
941
162920
                    |x, y| {
942
162920
                        if (x != 0 || y != 0) && y >= 0 {
943
147720
                            Some(x.pow(y as u32))
944
                        } else {
945
15200
                            None
946
                        }
947
162920
                    },
948
5380
                    b.domain_of()?.resolve().ok()?.as_ref(),
949
                )
950
5380
                .map(DomainPtr::from)
951
5380
                .ok(),
952
            Expression::Root(_, _) => None,
953
320
            Expression::Bubble(_, inner, _) => inner.domain_of(),
954
            Expression::AuxDeclaration(_, _, _) => Some(Domain::bool()),
955
31432
            Expression::And(_, _) => Some(Domain::bool()),
956
400
            Expression::Not(_, _) => Some(Domain::bool()),
957
320
            Expression::Or(_, _) => Some(Domain::bool()),
958
4416
            Expression::Imply(_, _, _) => Some(Domain::bool()),
959
            Expression::Iff(_, _, _) => Some(Domain::bool()),
960
16236
            Expression::Eq(_, _, _) => Some(Domain::bool()),
961
            Expression::Neq(_, _, _) => Some(Domain::bool()),
962
            Expression::Geq(_, _, _) => Some(Domain::bool()),
963
1720
            Expression::Leq(_, _, _) => Some(Domain::bool()),
964
84
            Expression::Gt(_, _, _) => Some(Domain::bool()),
965
4
            Expression::Lt(_, _, _) => Some(Domain::bool()),
966
            Expression::Factorial(_, _) => None, // not implemented
967
            Expression::FlatAbsEq(_, _, _) => Some(Domain::bool()),
968
80
            Expression::FlatSumGeq(_, _, _) => Some(Domain::bool()),
969
            Expression::FlatSumLeq(_, _, _) => Some(Domain::bool()),
970
            Expression::MinionDivEqUndefZero(_, _, _, _) => Some(Domain::bool()),
971
            Expression::MinionModuloEqUndefZero(_, _, _, _) => Some(Domain::bool()),
972
360
            Expression::FlatIneq(_, _, _, _) => Some(Domain::bool()),
973
808
            Expression::Flatten(_, n, m) => {
974
808
                if let Some(expr) = n {
975
                    if expr.return_type() == ReturnType::Int {
976
                        // TODO: handle flatten with depth argument
977
                        return None;
978
                    }
979
                } else {
980
                    // TODO: currently only works for matrices
981
808
                    let dom = m.domain_of()?.resolve().ok()?;
982
800
                    let (val_dom, idx_doms) = match dom.as_ref() {
983
800
                        GroundDomain::Matrix(val, idx) => (val, idx),
984
                        _ => return None,
985
                    };
986
800
                    let num_elems = matrix::num_elements(idx_doms).ok()? as i32;
987

            
988
800
                    let new_index_domain = Domain::int(vec![Range::Bounded(1, num_elems)]);
989
800
                    return Some(Domain::matrix(
990
800
                        val_dom.clone().into(),
991
800
                        vec![new_index_domain],
992
800
                    ));
993
                }
994
                None
995
            }
996
            Expression::AllDiff(_, _) => Some(Domain::bool()),
997
            Expression::Table(_, _, _) => Some(Domain::bool()),
998
            Expression::NegativeTable(_, _, _) => Some(Domain::bool()),
999
            Expression::FlatWatchedLiteral(_, _, _) => Some(Domain::bool()),
            Expression::MinionReify(_, _, _) => Some(Domain::bool()),
4820
            Expression::MinionReifyImply(_, _, _) => Some(Domain::bool()),
            Expression::MinionWInIntervalSet(_, _, _) => Some(Domain::bool()),
            Expression::MinionWInSet(_, _, _) => Some(Domain::bool()),
            Expression::MinionElementOne(_, _, _, _) => Some(Domain::bool()),
7532
            Expression::Neg(_, x) => {
7532
                let dom = x.domain_of()?;
7532
                let mut ranges = dom.as_int()?;
4772
                ranges = ranges
4772
                    .into_iter()
4772
                    .map(|r| match r {
840
                        Range::Single(x) => Range::Single(-x),
3932
                        Range::Bounded(x, y) => Range::Bounded(-y, -x),
                        Range::UnboundedR(i) => Range::UnboundedL(-i),
                        Range::UnboundedL(i) => Range::UnboundedR(-i),
                        Range::Unbounded => Range::Unbounded,
4772
                    })
4772
                    .collect();
4772
                Some(Domain::int(ranges))
            }
376348
            Expression::Minus(_, a, b) => {
376348
                let a_resolved = a.domain_of()?.resolve().ok()?;
376348
                let b_resolved = b.domain_of()?.resolve().ok()?;
376344
                if matches!(a_resolved.as_ref(), GroundDomain::Int(_))
376344
                    && matches!(b_resolved.as_ref(), GroundDomain::Int(_))
                {
376344
                    a_resolved
1206220
                        .apply_i32(|x, y| Some(x - y), b_resolved.as_ref())
376344
                        .map(DomainPtr::from)
376344
                        .ok()
                } else if matches!(a_resolved.as_ref(), GroundDomain::Set(_, _))
                    && matches!(b_resolved.as_ref(), GroundDomain::Set(_, _))
                {
                    Some(DomainPtr::from(a_resolved))
                } else {
                    None
                }
            }
            Expression::FlatAllDiff(_, _) => Some(Domain::bool()),
            Expression::FlatMinusEq(_, _, _) => Some(Domain::bool()),
            Expression::FlatProductEq(_, _, _, _) => Some(Domain::bool()),
            Expression::FlatWeightedSumLeq(_, _, _, _) => Some(Domain::bool()),
            Expression::FlatWeightedSumGeq(_, _, _, _) => Some(Domain::bool()),
5640
            Expression::Abs(_, a) => a
5640
                .domain_of()?
5640
                .resolve()
5640
                .ok()?
5640
                .apply_i32(
953360
                    |a, _| Some(a.abs()),
5640
                    a.domain_of()?.resolve().ok()?.as_ref(),
                )
5640
                .map(DomainPtr::from)
5640
                .ok(),
            Expression::MinionPow(_, _, _, _) => Some(Domain::bool()),
6088
            Expression::ToInt(_, _) => Some(Domain::int(vec![Range::Bounded(0, 1)])),
3840
            Expression::SATInt(_, _, _, (low, high)) => {
3840
                Some(Domain::int_ground(vec![Range::Bounded(*low, *high)]))
            }
            Expression::PairwiseSum(_, a, b) => a
                .domain_of()?
                .resolve()
                .ok()?
                .apply_i32(|a, b| Some(a + b), b.domain_of()?.resolve().ok()?.as_ref())
                .map(DomainPtr::from)
                .ok(),
            Expression::PairwiseProduct(_, a, b) => a
                .domain_of()?
                .resolve()
                .ok()?
                .apply_i32(|a, b| Some(a * b), b.domain_of()?.resolve().ok()?.as_ref())
                .map(DomainPtr::from)
                .ok(),
32
            Expression::Defined(_, function) => {
32
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
32
                let size = Self::function_elements_size(attrs, &domain, &codomain);
32
                if let Some(size) = size {
28
                    Some(Domain::set(SetAttr::new(size), domain))
                } else {
4
                    Some(Domain::empty(ReturnType::Set(Box::new(
4
                        domain.return_type(),
4
                    ))))
                }
            }
44
            Expression::Range(_, function) => {
44
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
44
                let jectivity = attrs.resolve().ok()?.jectivity;
44
                let size_size = attrs.resolve().ok()?.size;
44
                let size_size = match size_size {
20
                    Range::Unbounded => Range::UnboundedR(0),
                    // If lower bound we can guarantee one mapping (unless size = 0)
8
                    Range::Single(x) => match jectivity {
4
                        JectivityAttr::Injective | JectivityAttr::Surjective => Range::Single(x),
4
                        _ => Range::Bounded(Ord::min(1, x), x),
                    },
                    // Upper bound guarantees the same upper bound
4
                    Range::UnboundedL(x) => Range::Bounded(0, x),
                    // If not bounded by 0 can guarantee min 1
8
                    Range::UnboundedR(x) => match jectivity {
                        JectivityAttr::Injective | JectivityAttr::Surjective => {
4
                            Range::UnboundedR(x)
                        }
4
                        _ => Range::UnboundedR(Ord::min(1, x)),
                    },
4
                    Range::Bounded(x, y) => Range::Bounded(Ord::min(1, x), y),
                };
                // Gets the size imposed by the partiality and jectivity attributes
44
                let partiality = attrs.resolve().ok()?.partiality;
44
                let codomain_length = codomain.length_signed();
44
                let attr_size = match jectivity {
                    // Bijective and surjective functions must have every element in the codomain mapped to
12
                    JectivityAttr::Bijective | JectivityAttr::Surjective => match codomain_length {
12
                        Ok(co_len) => Some(Range::Single(co_len)),
                        Err(_) => None,
                    },
                    JectivityAttr::Injective => {
16
                        let domain_length = domain.length_signed();
16
                        match domain_length {
16
                            Ok(len) => match codomain_length {
16
                                Ok(co_len) => match partiality {
                                    // When its injective we can guarantee 1 to 1, so the maximum domain length is a single bound
                                    PartialityAttr::Total => {
4
                                        Some(Range::Single(Ord::min(len, co_len)))
                                    }
                                    PartialityAttr::Partial => {
12
                                        Some(Range::Bounded(0, Ord::min(len, co_len)))
                                    }
                                },
                                Err(_) => None,
                            },
                            Err(_) => None,
                        }
                    }
                    JectivityAttr::None => {
16
                        let domain_length = domain.length_signed();
16
                        match domain_length {
                            // This is the general case, where we know there cannot be more codomain elements mapped to that domain elements
16
                            Ok(len) => Some(Range::Bounded(0, len)),
                            Err(_) => None,
                        }
                    }
                };
44
                let size = match attr_size {
44
                    Some(attr_size) => {
44
                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
44
                        match unsafe_range {
44
                            Ok(range) => range,
                            Err(_) => {
                                return Some(Domain::empty(ReturnType::Set(Box::new(
                                    domain.return_type(),
                                ))));
                            }
                        }
                    }
                    None => size_size,
                };
44
                Some(Domain::set(SetAttr::new(size), codomain))
            }
            Expression::Image(_, function, _) => get_function_codomain(function),
            Expression::ImageSet(_, function, _) => {
                let codomain = get_function_codomain(function);
                // An imageSet is the converted to a set, and can be empty
                codomain.map(|inner_dom| Domain::set(SetAttr::new(Range::Bounded(0, 1)), inner_dom))
            }
44
            Expression::PreImage(_, function, _) => {
44
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
44
                let size_size = attrs.resolve().ok()?.size;
44
                let size_size = match size_size {
                    // Our only guarantee is an upper bound is the same
20
                    Range::Unbounded => Range::UnboundedR(0),
8
                    Range::Single(x) => Range::Bounded(0, x),
4
                    Range::UnboundedL(x) => Range::Bounded(0, x),
8
                    Range::UnboundedR(_) => Range::UnboundedR(0),
4
                    Range::Bounded(_, y) => Range::Bounded(0, y),
                };
44
                let jectivity = attrs.resolve().ok()?.jectivity;
44
                let codomain_length = codomain.length_signed();
44
                let attr_size = match jectivity {
                    // When there is 1-to-1 mapping we can guarantee no more than 1 occurrence
4
                    JectivityAttr::Bijective => Some(Range::Single(1)),
16
                    JectivityAttr::Injective => match size_size {
4
                        Range::Single(x) | Range::UnboundedL(x) | Range::Bounded(x, _) => {
4
                            match codomain_length {
4
                                Ok(co_len) => {
4
                                    if x >= co_len {
                                        Some(Range::Single(1))
                                    } else {
4
                                        Some(Range::Bounded(0, 1))
                                    }
                                }
                                Err(_) => Some(Range::Bounded(0, 1)),
                            }
                        }
12
                        _ => Some(Range::Bounded(0, 1)),
                    },
                    JectivityAttr::Surjective => {
8
                        let domain_length = domain.length_signed();
8
                        match domain_length {
8
                            Ok(len) => match codomain_length {
                                // We know the element is mapped but not how many times
                                // Every element must be mapped so it cannot be every element of domain
8
                                Ok(co_len) => match size_size {
4
                                    Range::Bounded(_, x)
                                    | Range::UnboundedL(x)
4
                                    | Range::Single(x) => Some(Range::Bounded(
4
                                        1,
4
                                        Ord::max(Ord::min(len, x) - co_len + 1, 0),
4
                                    )),
4
                                    _ => Some(Range::Bounded(1, Ord::max(len - co_len + 1, 0))),
                                },
                                Err(_) => Some(Range::UnboundedR(1)),
                            },
                            Err(_) => Some(Range::UnboundedR(1)),
                        }
                    }
                    JectivityAttr::None => {
16
                        let domain_length = domain.length_signed();
16
                        match domain_length {
16
                            Ok(len) => Some(Range::Bounded(0, len)),
                            Err(_) => Some(Range::UnboundedR(0)),
                        }
                    }
                };
44
                let size = match attr_size {
44
                    Some(attr_size) => {
44
                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
44
                        match unsafe_range {
44
                            Ok(range) => range,
                            Err(_) => {
                                return Some(Domain::empty(ReturnType::Set(Box::new(
                                    domain.return_type(),
                                ))));
                            }
                        }
                    }
                    None => size_size,
                };
44
                Some(Domain::set(SetAttr::new(size), domain))
            }
16
            Expression::Restrict(_, function, new_domain) => {
16
                let mut domain = function.domain_of()?;
16
                let (attrs_mut, dom, codom_mut) = domain.as_function_mut()?;
                // Stops other references being mutable
16
                let attrs: &FuncAttr<IntVal> = attrs_mut;
16
                let codom: &Moo<Domain> = codom_mut;
                // Gets the minimal range between the old domain and new domain
16
                let mut new_dom = new_domain.domain_of()?;
                // If domains cannot be resolved we just stick to the restricted one
16
                if let Some(new_rng) = new_dom.as_int_ground_mut()
16
                    && let Some(old_rng) = dom.as_int_ground_mut()
                {
16
                    new_rng.append(old_rng);
16
                    if let Ok(rng) = Range::minimal(new_rng) {
16
                        let ranges = vec![rng];
16
                        new_dom = Domain::int(ranges);
16
                    }
                }
16
                let attr_size = attrs.resolve().ok()?.size;
16
                let new_size = match new_dom.length_signed() {
                    // Combines current size attributes with length of new domain
16
                    Ok(len) => match Range::minimal(&[attr_size, Range::Bounded(0, len)]) {
12
                        Ok(size) => size,
                        Err(_) => {
                            // Means the restriction is impossible
4
                            return Some(Domain::empty(ReturnType::Function(
4
                                Box::new(new_dom.return_type()),
4
                                Box::new(codom.return_type()),
4
                            )));
                        }
                    },
                    Err(_) => attr_size,
                };
12
                let jectivity = attrs.jectivity.clone();
12
                let partiality = attrs.partiality.clone();
12
                let new_attrs = FuncAttr {
12
                    size: new_size,
12
                    jectivity,
12
                    partiality,
12
                };
12
                Some(Domain::function(new_attrs, new_dom, codom.clone()))
            }
            Expression::Subsequence(_, _, _) => Some(Domain::bool()),
            Expression::Substring(_, _, _) => Some(Domain::bool()),
            Expression::Inverse(..) => Some(Domain::bool()),
            Expression::LexLt(..) => Some(Domain::bool()),
3080
            Expression::LexLeq(..) => Some(Domain::bool()),
            Expression::LexGt(..) => Some(Domain::bool()),
            Expression::LexGeq(..) => Some(Domain::bool()),
            Expression::FlatLexLt(..) => Some(Domain::bool()),
            Expression::FlatLexLeq(..) => Some(Domain::bool()),
            Expression::Active(..) => Some(Domain::bool()),
            Expression::ToSet(_, other) => {
                if let Some((attrs, dom, codom)) = other.domain_of()?.as_function() {
                    let set_attrs = SetAttr { size: attrs.size };
                    Some(Domain::set(set_attrs, Domain::tuple(vec![dom, codom])))
                } else if let Some((attrs, doms)) = other.domain_of()?.as_relation() {
                    let set_attrs = SetAttr { size: attrs.size };
                    Some(Domain::set(set_attrs, Domain::tuple(doms)))
                } else if let Some((attrs, dom)) = other.domain_of()?.as_mset() {
                    let set_attrs = SetAttr { size: attrs.size };
                    Some(Domain::set(set_attrs, dom))
                } else if let Some((dom, dimensions)) = other.domain_of()?.as_matrix() {
                    // We combine all matrix domains into a tuple
                    let mut doms = vec![];
                    for _ in dimensions {
                        doms.push(dom.clone());
                    }
                    let doms_sizes: Result<Vec<i32>, _> =
                        doms.iter().map(|x| x.length_signed()).collect();
                    let attr = match doms_sizes {
                        Ok(vals) => {
                            if let Some(&size) = vals.iter().min() {
                                SetAttr::new(Range::Single(size))
                            } else {
                                SetAttr::<i32>::default()
                            }
                        }
                        // We do not know the ground dimensions yet so default is chosen
                        Err(_) => SetAttr::<i32>::default(),
                    };
                    Some(Domain::set(attr, Domain::tuple(doms)))
                } else {
                    bug!(
                        "Domain of {self} needed to be a function, relation, mset, or matrix for ToSet"
                    )
                }
            }
            Expression::ToMSet(_, other) => {
                if let Some((attrs, dom, codom)) = other.domain_of()?.as_function() {
                    let set_attrs = MSetAttr {
                        size: attrs.size,
                        occurrence: Range::Single(IntVal::Const(1)),
                    };
                    Some(Domain::mset(set_attrs, Domain::tuple(vec![dom, codom])))
                } else if let Some((attrs, doms)) = other.domain_of()?.as_relation() {
                    let set_attrs = MSetAttr {
                        size: attrs.size,
                        occurrence: Range::Single(IntVal::Const(1)),
                    };
                    Some(Domain::mset(set_attrs, Domain::tuple(doms)))
                } else if let Some((attrs, dom)) = other.domain_of()?.as_set() {
                    let set_attrs = MSetAttr {
                        size: attrs.size,
                        occurrence: Range::Single(IntVal::Const(1)),
                    };
                    Some(Domain::mset(set_attrs, dom))
                } else {
                    bug!("Domain of {self} needed to be a function, relation, or set for ToMSet")
                }
            }
            Expression::ToRelation(_, function) => {
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
                // Function attributes apply to the relation
                let rel_attrs = RelAttr {
                    size: attrs.size,
                    binary: vec![],
                };
                Some(Domain::relation(rel_attrs, vec![domain, codomain]))
            }
4
            Expression::RelationProj(_, relation, projections) => {
4
                let (_, domains) = relation.domain_of()?.as_relation()?;
4
                let new_doms = domains
4
                    .iter()
4
                    .zip(projections.iter())
12
                    .filter_map(|(domain, included)| {
12
                        if included.is_none() {
                            // The domains corresponding to projections which are None remain in the relation
8
                            Some(domain.clone())
                        } else {
4
                            None
                        }
12
                    })
4
                    .collect();
4
                Some(Domain::relation(RelAttr::<IntVal>::default(), new_doms))
            }
            Expression::Apart(_, _, _) => Some(Domain::bool()),
            Expression::Together(_, _, _) => Some(Domain::bool()),
8
            Expression::Participants(_, p) => {
                // Every single element of the domain _must_ be in the set, so fixed size on that.
8
                let (attr, inner) = p.domain_of()?.as_partition()?;
8
                let len = inner.length_signed().ok()?;
8
                let p_parts = attr.resolve().ok()?.num_parts;
8
                let p_card = attr.resolve().ok()?.part_len;
                // if
8
                match (p_parts.low(), p_parts.high(), p_card.low(), p_card.high()) {
4
                    (Some(p), Some(q), Some(r), Some(s)) => {
4
                        let lo = p * r;
4
                        let hi = q * s;
4
                        if len < lo || len > hi {
4
                            return Some(Domain::empty(ReturnType::Set(Box::new(
4
                                inner.return_type(),
4
                            ))));
                        }
                    }
                    (None, Some(q), None, Some(s)) => {
                        let hi = q * s;
                        if len > hi {
                            return Some(Domain::empty(ReturnType::Set(Box::new(
                                inner.return_type(),
                            ))));
                        }
                    }
                    (Some(p), None, Some(r), None) => {
                        let lo = p * r;
                        if len < lo {
                            return Some(Domain::empty(ReturnType::Set(Box::new(
                                inner.return_type(),
                            ))));
                        }
                    }
4
                    _ => {}
                }
4
                Some(Domain::set(
4
                    SetAttr::new_size(len),
4
                    Domain::int(inner.as_int()?),
                ))
            }
4
            Expression::Party(_, _, p) => {
                // Will pick a part, so set will share same attrs
4
                let (attr, inner) = p.domain_of()?.as_partition()?;
4
                Some(Domain::set(SetAttr::new(attr.part_len), inner))
            }
8
            Expression::Parts(_, p) => {
8
                let (attr, inner) = p.domain_of()?.as_partition()?;
8
                Some(Domain::set(
8
                    SetAttr::new(attr.num_parts.clone()),
8
                    Domain::set(SetAttr::new(attr.part_len), inner),
8
                ))
            }
16
            Expression::Card(_, collection) => {
16
                let domain = collection.domain_of()?;
16
                if let Some((_, dimensions)) = domain.as_matrix() {
                    let doms_ground: Result<Vec<i32>, _> =
                        dimensions.iter().map(|x| x.length_signed()).collect();
                    if let Ok(doms_ground) = doms_ground {
                        let size: Range<i32> = Range::Single(doms_ground.iter().product());
                        Some(Domain::int(vec![size]))
                    } else {
                        Some(Domain::int(vec![Range::<i32>::Unbounded]))
                    }
16
                } else if let Some((attr, dom)) = domain.as_set() {
4
                    let attr_size = attr.resolve().ok()?.size;
4
                    if let Ok(length) = dom.length_signed() {
4
                        let unsafe_range = Range::minimal(&[attr_size, Range::Bounded(0, length)]);
4
                        return match unsafe_range {
4
                            Ok(range) => Some(Domain::int(vec![range])),
                            Err(_) => None,
                        };
                    }
                    // If the domain is not known we just need to go off of attributes
                    Some(Domain::int(vec![attr_size]))
12
                } else if let Some((attrs, dom)) = domain.as_mset() {
4
                    let attrs_gd = attrs.resolve().ok()?;
                    // Gets maximum value of the occurrence
4
                    let attr_occ = match attrs_gd.occurrence {
                        Range::Single(x) => Some(x),
                        Range::Unbounded | Range::UnboundedR(_) => None,
                        Range::Bounded(_, x) => Some(x),
4
                        Range::UnboundedL(x) => Some(x),
                    };
4
                    if let Some(occ) = attr_occ {
4
                        if let Ok(length) = dom.length_signed() {
4
                            let unsafe_range =
4
                                Range::minimal(&[attrs_gd.size, Range::Bounded(0, length * occ)]);
4
                            match unsafe_range {
4
                                Ok(range) => Some(Domain::int(vec![range])),
                                Err(_) => None,
                            }
                        } else {
                            // If the domain is not known we just need to go off of attributes
                            Some(Domain::int(vec![attrs_gd.size]))
                        }
                    } else {
                        // If no occurrence is provided then it must have bounded size
                        Some(Domain::int(vec![attrs_gd.size]))
                    }
8
                } else if let Some((attrs, doms)) = domain.as_relation() {
                    // TODO: Further inference may be possible using the binary attributes
4
                    let attrs_gd = attrs.resolve().ok()?;
                    // See if all domains are ground
4
                    let doms_sizes: Result<Vec<i32>, _> =
12
                        doms.iter().map(|x| x.length_signed()).collect();
4
                    if let Ok(doms_sizes) = doms_sizes {
4
                        let length = Range::Bounded(0, doms_sizes.iter().product());
                        // Combine the attributes and the domain possibilities
4
                        let unsafe_range = Range::minimal(&[attrs_gd.size, length]);
4
                        return match unsafe_range {
4
                            Ok(range) => Some(Domain::int(vec![range])),
                            Err(_) => None,
                        };
                    }
                    // If the domain is not known we just need to go off of attributes
                    Some(Domain::int(vec![attrs_gd.size]))
4
                } else if let Some((attrs, dom, codom)) = domain.as_function() {
4
                    let size = Self::function_elements_size(attrs, &dom, &codom);
4
                    size.map(|size| Domain::int(vec![size]))
                } else {
                    bug!(
                        "Domain of {self} needed to be a matrix, set, mset, relation, or function for cardinality"
                    )
                }
            }
        }
9402452
    }
    // Gets the number of domain elements mapped in a function. This is the cardinality and also the defined
36
    fn function_elements_size(
36
        attrs: FuncAttr<IntVal>,
36
        domain: &DomainPtr,
36
        codomain: &DomainPtr,
36
    ) -> Option<Range> {
36
        let attrs_gd = attrs.resolve().ok()?;
36
        let domain_length = domain.length_signed();
        // We can only infer if the domain is ground and the length is known
36
        let attr_size = match domain_length {
36
            Ok(len) => match attrs_gd.partiality {
4
                PartialityAttr::Total => Some(Range::Single(len)),
                PartialityAttr::Partial => {
                    // When partial we also need the codomain to be ground and known
32
                    let codomain_length = codomain.length_signed();
32
                    match codomain_length {
32
                        Ok(co_len) => match attrs_gd.jectivity {
8
                            JectivityAttr::Bijective => Some(Range::Single(co_len)),
8
                            JectivityAttr::Surjective => Some(Range::Bounded(co_len, len)),
                            JectivityAttr::Injective => {
12
                                Some(Range::Bounded(0, Ord::min(len, co_len)))
                            }
4
                            JectivityAttr::None => Some(Range::Bounded(0, len)),
                        },
                        Err(_) => None,
                    }
                }
            },
            Err(_) => None,
        };
        // We combine the sizes:
        // attrs_gd.size relates to size constraints imposed by the size attributes of the function
        // attr_size relates to size constraints imposed by the jectivity and partiality attributes.
        //       This uses inference from the domain and codomain lengths.
        // If the attributes clash the function is unsolveable, and an empty domain is returned
36
        match attr_size {
36
            Some(attr_size) => {
36
                let unsafe_range = Range::minimal(&[attrs_gd.size, attr_size]);
36
                unsafe_range.ok()
            }
            None => Some(attrs_gd.size),
        }
36
    }
    /// Returns a reference to this expression's metadata without cloning.
    pub fn meta_ref(&self) -> &Metadata {
        macro_rules! match_meta_ref {
            ($($variant:ident),* $(,)?) => {
                match self {
                    $(Expression::$variant(meta, ..) => meta,)*
                }
            };
        }
        match_meta_ref!(
            AbstractLiteral,
            Root,
            Bubble,
            Comprehension,
            AbstractComprehension,
            DominanceRelation,
            FromSolution,
            Metavar,
            Atomic,
            RecordField,
            UnsafeIndex,
            SafeIndex,
            UnsafeSlice,
            SafeSlice,
            InDomain,
            ToInt,
            Abs,
            Sum,
            Product,
            Min,
            Max,
            Not,
            Or,
            And,
            Imply,
            Iff,
            Union,
            In,
            Intersect,
            Supset,
            SupsetEq,
            Subset,
            SubsetEq,
            Eq,
            Neq,
            Geq,
            Leq,
            Gt,
            Lt,
            SafeDiv,
            UnsafeDiv,
            SafeMod,
            UnsafeMod,
            Apart,
            Together,
            Participants,
            Party,
            Parts,
            Neg,
            Defined,
            Range,
            UnsafePow,
            SafePow,
            Flatten,
            AllDiff,
            Minus,
            Factorial,
            FlatAbsEq,
            FlatAllDiff,
            FlatSumGeq,
            FlatSumLeq,
            FlatIneq,
            FlatWatchedLiteral,
            FlatWeightedSumLeq,
            FlatWeightedSumGeq,
            FlatMinusEq,
            FlatProductEq,
            MinionDivEqUndefZero,
            MinionModuloEqUndefZero,
            MinionPow,
            MinionReify,
            MinionReifyImply,
            MinionWInIntervalSet,
            MinionWInSet,
            MinionElementOne,
            AuxDeclaration,
            SATInt,
            PairwiseSum,
            PairwiseProduct,
            Image,
            ImageSet,
            PreImage,
            Inverse,
            Restrict,
            LexLt,
            LexLeq,
            LexGt,
            LexGeq,
            FlatLexLt,
            FlatLexLeq,
            NegativeTable,
            Table,
            Active,
            ToSet,
            ToMSet,
            ToRelation,
            RelationProj,
            Card,
            Subsequence,
            Substring,
        )
    }
    pub fn get_meta(&self) -> Metadata {
        let metas: VecDeque<Metadata> = self.children_bi();
        metas[0].clone()
    }
    pub fn set_meta(&self, meta: Metadata) {
        self.transform_bi(&|_| meta.clone());
    }
    /// Checks whether this expression is safe.
    ///
    /// An expression is unsafe if can be undefined, or if any of its children can be undefined.
    ///
    /// Unsafe expressions are (typically) prefixed with Unsafe in our AST, and can be made
    /// safe through the use of bubble rules.
5369768
    pub fn is_safe(&self) -> bool {
        // TODO: memoise in Metadata
43226432
        for expr in self.universe() {
43226432
            match expr {
                Expression::UnsafeDiv(_, _, _)
                | Expression::UnsafeMod(_, _, _)
                | Expression::UnsafePow(_, _, _)
                | Expression::UnsafeIndex(_, _, _)
                | Expression::Bubble(_, _, _)
                | Expression::UnsafeSlice(_, _, _) => {
833008
                    return false;
                }
42393424
                _ => {}
            }
        }
4536760
        true
5369768
    }
    /// True if the expression is an associative and commutative operator
14902372
    pub fn is_associative_commutative_operator(&self) -> bool {
14902372
        TryInto::<ACOperatorKind>::try_into(self).is_ok()
14902372
    }
    /// True if the expression is a matrix literal.
    ///
    /// This is true for both forms of matrix literals: those with elements of type [`Literal`] and
    /// [`Expression`].
8280
    pub fn is_matrix_literal(&self) -> bool {
        matches!(
8280
            self,
            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(_, _))
                | Expression::Atomic(
                    _,
                    Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _))),
                )
        )
8280
    }
    /// True iff self and other are both atomic and identical.
    ///
    /// This method is useful to cheaply check equivalence. Assuming CSE is enabled, any unifiable
    /// expressions will be rewritten to a common variable. This is much cheaper than checking the
    /// entire subtrees of `self` and `other`.
2595632
    pub fn identical_atom_to(&self, other: &Expression) -> bool {
2595632
        let atom1: Result<&Atom, _> = self.try_into();
2595632
        let atom2: Result<&Atom, _> = other.try_into();
2595632
        if let (Ok(atom1), Ok(atom2)) = (atom1, atom2) {
572992
            atom2 == atom1
        } else {
2022640
            false
        }
2595632
    }
    /// If the expression is a list, returns a *copied* vector of the inner expressions.
    ///
    /// A list is any a matrix with the domain `int(1..)`. This includes matrix literals without
    /// any explicitly specified domain.
9074624
    pub fn unwrap_list(&self) -> Option<Vec<Expression>> {
7716296
        match self {
7716296
            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
7716296
                matrix.unwrap_list().cloned()
            }
            Expression::Atomic(
                _,
27748
                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
27748
            ) => matrix.unwrap_list().map(|elems| {
23568
                elems
23568
                    .clone()
23568
                    .into_iter()
64376
                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
23568
                    .collect_vec()
23568
            }),
1330580
            _ => None,
        }
9074624
    }
    /// If the expression is a matrix, gets it elements and index domain.
    ///
    /// **Consider using the safer [`Expression::unwrap_list`] instead.**
    ///
    /// It is generally undefined to edit the length of a matrix unless it is a list (as defined by
    /// [`Expression::unwrap_list`]). Users of this function should ensure that, if the matrix is
    /// reconstructed, the index domain and the number of elements in the matrix remain the same.
12329495
    pub fn unwrap_matrix_unchecked(self) -> Option<(Vec<Expression>, DomainPtr)> {
8413239
        match self {
8413239
            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(elems, domain)) => {
8413239
                Some((elems, domain))
            }
            Expression::Atomic(
                _,
457664
                Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, domain))),
            ) => Some((
457664
                elems
457664
                    .into_iter()
1092024
                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
457664
                    .collect_vec(),
457664
                domain.into(),
            )),
3458592
            _ => None,
        }
12329495
    }
    /// For a Root expression, extends the inner vec with the given vec.
    ///
    /// # Panics
    /// Panics if the expression is not Root.
27940
    pub fn extend_root(self, exprs: Vec<Expression>) -> Expression {
27940
        match self {
27940
            Expression::Root(meta, mut children) => {
27940
                children.extend(exprs);
27940
                Expression::Root(meta, children)
            }
            _ => panic!("extend_root called on a non-Root expression"),
        }
27940
    }
    /// Converts the expression to a literal, if possible.
161060
    pub fn into_literal(self) -> Option<Literal> {
151500
        match self {
141172
            Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
            Expression::AbstractLiteral(_, abslit) => {
                Some(Literal::AbstractLiteral(abslit.into_literals()?))
            }
6104
            Expression::Neg(_, e) => {
6104
                let Literal::Int(i) = Moo::unwrap_or_clone(e).into_literal()? else {
                    bug!("negated literal should be an int");
                };
6100
                Some(Literal::Int(-i))
            }
13784
            _ => None,
        }
161060
    }
    /// If this expression is an associative-commutative operator, return its [ACOperatorKind].
16143252
    pub fn to_ac_operator_kind(&self) -> Option<ACOperatorKind> {
16143252
        TryFrom::try_from(self).ok()
16143252
    }
    /// Returns the categories of all sub-expressions of self.
92212
    pub fn universe_categories(&self) -> HashSet<Category> {
92212
        self.universe()
92212
            .into_iter()
1095664
            .map(|x| x.category_of())
92212
            .collect()
92212
    }
}
pub fn get_function_codomain(function: &Moo<Expression>) -> Option<DomainPtr> {
    let function_domain = function.domain_of()?;
    match function_domain.resolve().as_ref() {
        Ok(d) => {
            match d.as_ref() {
                GroundDomain::Function(_, _, codomain) => Some(codomain.clone().into()),
                // Not defined for anything other than a function
                _ => None,
            }
        }
        Err(_) => {
            match function_domain.as_unresolved()? {
                UnresolvedDomain::Function(_, _, codomain) => Some(codomain.clone()),
                // Not defined for anything other than a function
                _ => None,
            }
        }
    }
}
impl TryFrom<&Expression> for i32 {
    type Error = ();
989260
    fn try_from(value: &Expression) -> Result<Self, Self::Error> {
989260
        let Expression::Atomic(_, atom) = value else {
742756
            return Err(());
        };
246504
        let Atom::Literal(lit) = atom else {
245184
            return Err(());
        };
1320
        let Literal::Int(i) = lit else {
            return Err(());
        };
1320
        Ok(*i)
989260
    }
}
impl TryFrom<Expression> for i32 {
    type Error = ();
    fn try_from(value: Expression) -> Result<Self, Self::Error> {
        TryFrom::<&Expression>::try_from(&value)
    }
}
impl From<i32> for Expression {
70944
    fn from(i: i32) -> Self {
70944
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(i)))
70944
    }
}
impl From<bool> for Expression {
38484
    fn from(b: bool) -> Self {
38484
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(b)))
38484
    }
}
impl From<Atom> for Expression {
5548
    fn from(value: Atom) -> Self {
5548
        Expression::Atomic(Metadata::new(), value)
5548
    }
}
impl From<Literal> for Expression {
7040
    fn from(value: Literal) -> Self {
7040
        Expression::Atomic(Metadata::new(), value.into())
7040
    }
}
impl From<AbstractLiteral<Expression>> for Expression {
    fn from(value: AbstractLiteral<Expression>) -> Self {
        Expression::AbstractLiteral(Metadata::new(), value)
    }
}
impl From<Moo<Expression>> for Expression {
87788
    fn from(val: Moo<Expression>) -> Self {
87788
        val.as_ref().clone()
87788
    }
}
impl CategoryOf for Expression {
1391196
    fn category_of(&self) -> Category {
        // take highest category of all the expressions children
5389436
        let category = self.cata(&move |x,children| {
5389436
            if let Some(max_category) = children.iter().max() {
                // if this expression contains subexpressions, return the maximum category of the
                // subexpressions
1664536
                *max_category
            } else {
                // this expression has no children
3724900
                let mut max_category = Category::Bottom;
                // calculate the category by looking at all atoms, submodels, comprehensions, and
                // declarationptrs inside this expression
                // this should generically cover all leaf types we currently have in oxide.
                // if x contains submodels (including comprehensions)
3724900
                if !Biplate::<Model>::universe_bi(&x).is_empty() {
                    // assume that the category is decision
                    return Category::Decision;
3724900
                }
                // if x contains atoms
3779580
                if let Some(max_atom_category) = Biplate::<Atom>::universe_bi(&x).iter().map(|x| x.category_of()).max()
                // and those atoms have a higher category than we already know about
3724860
                && max_atom_category > max_category{
                    // update category
3724860
                    max_category = max_atom_category;
3724860
                }
                // if x contains declarationPtrs
3724900
                if let Some(max_declaration_category) = Biplate::<DeclarationPtr>::universe_bi(&x).iter().map(|x| x.category_of()).max()
                // and those pointers have a higher category than we already know about
2835292
                && max_declaration_category > max_category{
                    // update category
1228
                    max_category = max_declaration_category;
3723672
                }
3724900
                max_category
            }
5389436
        });
1391196
        if cfg!(debug_assertions) {
1391196
            trace!(
                category= %category,
                expression= %self,
                "Called Expression::category_of()"
            );
        };
1391196
        category
1391196
    }
}
impl Display for Expression {
81866092
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81866092
        match &self {
448
            Expression::Union(_, box1, box2) => {
448
                write!(f, "({} union {})", box1.clone(), box2.clone())
            }
3120
            Expression::In(_, e1, e2) => {
3120
                write!(f, "{e1} in {e2}")
            }
400
            Expression::Intersect(_, box1, box2) => {
400
                write!(f, "({} intersect {})", box1.clone(), box2.clone())
            }
480
            Expression::Supset(_, box1, box2) => {
480
                write!(f, "({} supset {})", box1.clone(), box2.clone())
            }
480
            Expression::SupsetEq(_, box1, box2) => {
480
                write!(f, "({} supsetEq {})", box1.clone(), box2.clone())
            }
600
            Expression::Subset(_, box1, box2) => {
600
                write!(f, "({} subset {})", box1.clone(), box2.clone())
            }
540
            Expression::SubsetEq(_, box1, box2) => {
540
                write!(f, "({} subsetEq {})", box1.clone(), box2.clone())
            }
3079520
            Expression::AbstractLiteral(_, l) => l.fmt(f),
31928
            Expression::Comprehension(_, c) => c.fmt(f),
            Expression::AbstractComprehension(_, c) => c.fmt(f),
409468
            Expression::UnsafeIndex(_, e1, e2) => write!(f, "{e1}{}", pretty_vec(e2)),
1800
            Expression::RecordField(_, r, fld) => {
1800
                write!(f, "{r}[{fld}]")
            }
992960
            Expression::SafeIndex(_, e1, e2) => write!(f, "SafeIndex({e1},{})", pretty_vec(e2)),
20320
            Expression::UnsafeSlice(_, e1, es) => {
20320
                let args = es
20320
                    .iter()
40400
                    .map(|x| match x {
20080
                        Some(x) => format!("{x}"),
20320
                        None => "..".into(),
40400
                    })
20320
                    .join(",");
20320
                write!(f, "{e1}[{args}]")
            }
13760
            Expression::SafeSlice(_, e1, es) => {
13760
                let args = es
13760
                    .iter()
26960
                    .map(|x| match x {
13200
                        Some(x) => format!("{x}"),
13760
                        None => "..".into(),
26960
                    })
13760
                    .join(",");
13760
                write!(f, "SafeSlice({e1},[{args}])")
            }
3480
            Expression::InDomain(_, e, domain) => {
3480
                write!(f, "__inDomain({e},{domain})")
            }
229920
            Expression::Root(_, exprs) => {
229920
                write!(f, "{}", pretty_expressions_as_top_level(exprs))
            }
            Expression::DominanceRelation(_, expr) => write!(f, "DominanceRelation({expr})"),
            Expression::FromSolution(_, expr) => write!(f, "FromSolution({expr})"),
            Expression::Metavar(_, name) => write!(f, "&{name}"),
70505120
            Expression::Atomic(_, atom) => atom.fmt(f),
8392
            Expression::Abs(_, a) | Expression::Card(_, a) => write!(f, "|{a}|"),
953644
            Expression::Sum(_, e) => {
953644
                write!(f, "sum({e})")
            }
112976
            Expression::Product(_, e) => {
112976
                write!(f, "product({e})")
            }
14840
            Expression::Min(_, e) => {
14840
                write!(f, "min({e})")
            }
14968
            Expression::Max(_, e) => {
14968
                write!(f, "max({e})")
            }
44780
            Expression::Not(_, expr_box) => {
44780
                write!(f, "!({})", expr_box.clone())
            }
330640
            Expression::Or(_, e) => {
330640
                write!(f, "or({e})")
            }
312316
            Expression::And(_, e) => {
312316
                write!(f, "and({e})")
            }
61392
            Expression::Imply(_, box1, box2) => {
61392
                write!(f, "({box1}) -> ({box2})")
            }
1680
            Expression::Iff(_, box1, box2) => {
1680
                write!(f, "({box1}) <-> ({box2})")
            }
437264
            Expression::Eq(_, box1, box2) => {
437264
                write!(f, "({} = {})", box1.clone(), box2.clone())
            }
832072
            Expression::Neq(_, box1, box2) => {
832072
                write!(f, "({} != {})", box1.clone(), box2.clone())
            }
141348
            Expression::Geq(_, box1, box2) => {
141348
                write!(f, "({} >= {})", box1.clone(), box2.clone())
            }
454992
            Expression::Leq(_, box1, box2) => {
454992
                write!(f, "({} <= {})", box1.clone(), box2.clone())
            }
7568
            Expression::Gt(_, box1, box2) => {
7568
                write!(f, "({} > {})", box1.clone(), box2.clone())
            }
45568
            Expression::Lt(_, box1, box2) => {
45568
                write!(f, "({} < {})", box1.clone(), box2.clone())
            }
40
            Expression::Apart(_, list, partition) => {
40
                write!(f, "apart({list}, {partition})")
            }
40
            Expression::Together(_, list, partition) => {
40
                write!(f, "together({list}, {partition})")
            }
56
            Expression::Participants(_, partition) => {
56
                write!(f, "participants({partition})")
            }
48
            Expression::Party(_, element, partition) => {
48
                write!(f, "party({element}, {partition})")
            }
56
            Expression::Parts(_, partition) => {
56
                write!(f, "parts({partition})")
            }
246260
            Expression::FlatSumGeq(_, box1, box2) => {
246260
                write!(f, "SumGeq({}, {})", pretty_vec(box1), box2.clone())
            }
241140
            Expression::FlatSumLeq(_, box1, box2) => {
241140
                write!(f, "SumLeq({}, {})", pretty_vec(box1), box2.clone())
            }
84864
            Expression::FlatIneq(_, box1, box2, box3) => write!(
84864
                f,
                "Ineq({}, {}, {})",
84864
                box1.clone(),
84864
                box2.clone(),
84864
                box3.clone()
            ),
2028
            Expression::Flatten(_, n, m) => {
2028
                if let Some(n) = n {
                    write!(f, "flatten({n}, {m})")
                } else {
2028
                    write!(f, "flatten({m})")
                }
            }
14912
            Expression::AllDiff(_, e) => {
14912
                write!(f, "allDiff({e})")
            }
1200
            Expression::Table(_, tuple_expr, rows_expr) => {
1200
                write!(f, "table({tuple_expr}, {rows_expr})")
            }
200
            Expression::NegativeTable(_, tuple_expr, rows_expr) => {
200
                write!(f, "negativeTable({tuple_expr}, {rows_expr})")
            }
13648
            Expression::Bubble(_, box1, box2) => {
13648
                write!(f, "{{{} @ {}}}", box1.clone(), box2.clone())
            }
19960
            Expression::SafeDiv(_, box1, box2) => {
19960
                write!(f, "SafeDiv({}, {})", box1.clone(), box2.clone())
            }
21560
            Expression::UnsafeDiv(_, box1, box2) => {
21560
                write!(f, "({} / {})", box1.clone(), box2.clone())
            }
46992
            Expression::UnsafePow(_, box1, box2) => {
46992
                write!(f, "({} ** {})", box1.clone(), box2.clone())
            }
555884
            Expression::SafePow(_, box1, box2) => {
555884
                write!(f, "SafePow({}, {})", box1.clone(), box2.clone())
            }
            Expression::Subsequence(_, s, t) => {
                write!(f, "{} subsequence {}", s.clone(), t.clone())
            }
80
            Expression::Substring(_, s, t) => {
80
                write!(f, "{} substring {}", s.clone(), t.clone())
            }
4220
            Expression::MinionDivEqUndefZero(_, box1, box2, box3) => {
4220
                write!(
4220
                    f,
                    "DivEq({}, {}, {})",
4220
                    box1.clone(),
4220
                    box2.clone(),
4220
                    box3.clone()
                )
            }
1320
            Expression::MinionModuloEqUndefZero(_, box1, box2, box3) => {
1320
                write!(
1320
                    f,
                    "ModEq({}, {}, {})",
1320
                    box1.clone(),
1320
                    box2.clone(),
1320
                    box3.clone()
                )
            }
4348
            Expression::FlatWatchedLiteral(_, x, l) => {
4348
                write!(f, "WatchedLiteral({x},{l})")
            }
109692
            Expression::MinionReify(_, box1, box2) => {
109692
                write!(f, "Reify({}, {})", box1.clone(), box2.clone())
            }
71556
            Expression::MinionReifyImply(_, box1, box2) => {
71556
                write!(f, "ReifyImply({}, {})", box1.clone(), box2.clone())
            }
760
            Expression::MinionWInIntervalSet(_, atom, intervals) => {
760
                let intervals = intervals.iter().join(",");
760
                write!(f, "__minion_w_inintervalset({atom},[{intervals}])")
            }
480
            Expression::MinionWInSet(_, atom, values) => {
480
                let values = values.iter().join(",");
480
                write!(f, "__minion_w_inset({atom},[{values}])")
            }
105044
            Expression::AuxDeclaration(_, reference, e) => {
105044
                write!(f, "{} =aux {}", reference, e.clone())
            }
3880
            Expression::UnsafeMod(_, a, b) => {
3880
                write!(f, "{} % {}", a.clone(), b.clone())
            }
6720
            Expression::SafeMod(_, a, b) => {
6720
                write!(f, "SafeMod({},{})", a.clone(), b.clone())
            }
61820
            Expression::Neg(_, a) => {
61820
                write!(f, "-({})", a.clone())
            }
            Expression::Factorial(_, a) => {
                write!(f, "({})!", a.clone())
            }
266424
            Expression::Minus(_, a, b) => {
266424
                write!(f, "({} - {})", a.clone(), b.clone())
            }
10488
            Expression::FlatAllDiff(_, es) => {
10488
                write!(f, "__flat_alldiff({})", pretty_vec(es))
            }
1600
            Expression::FlatAbsEq(_, a, b) => {
1600
                write!(f, "AbsEq({},{})", a.clone(), b.clone())
            }
720
            Expression::FlatMinusEq(_, a, b) => {
720
                write!(f, "MinusEq({},{})", a.clone(), b.clone())
            }
2300
            Expression::FlatProductEq(_, a, b, c) => {
2300
                write!(
2300
                    f,
                    "FlatProductEq({},{},{})",
2300
                    a.clone(),
2300
                    b.clone(),
2300
                    c.clone()
                )
            }
18700
            Expression::FlatWeightedSumLeq(_, cs, vs, total) => {
18700
                write!(
18700
                    f,
                    "FlatWeightedSumLeq({},{},{})",
18700
                    pretty_vec(cs),
18700
                    pretty_vec(vs),
18700
                    total.clone()
                )
            }
18900
            Expression::FlatWeightedSumGeq(_, cs, vs, total) => {
18900
                write!(
18900
                    f,
                    "FlatWeightedSumGeq({},{},{})",
18900
                    pretty_vec(cs),
18900
                    pretty_vec(vs),
18900
                    total.clone()
                )
            }
17240
            Expression::MinionPow(_, atom, atom1, atom2) => {
17240
                write!(f, "MinionPow({atom},{atom1},{atom2})")
            }
169216
            Expression::MinionElementOne(_, atoms, atom, atom1) => {
169216
                let atoms = atoms.iter().join(",");
169216
                write!(f, "__minion_element_one([{atoms}],{atom},{atom1})")
            }
1192
            Expression::ToInt(_, expr) => {
1192
                write!(f, "toInt({expr})")
            }
655720
            Expression::SATInt(_, encoding, bits, (min, max)) => {
655720
                write!(f, "SATInt({encoding:?}, {bits} [{min}, {max}])")
            }
            Expression::PairwiseSum(_, a, b) => write!(f, "PairwiseSum({a}, {b})"),
            Expression::PairwiseProduct(_, a, b) => write!(f, "PairwiseProduct({a}, {b})"),
104
            Expression::Defined(_, function) => write!(f, "defined({function})"),
128
            Expression::Range(_, function) => write!(f, "range({function})"),
40
            Expression::Image(_, function, elems) => write!(f, "image({function},{elems})"),
40
            Expression::ImageSet(_, function, elems) => write!(f, "imageSet({function},{elems})"),
128
            Expression::PreImage(_, function, elems) => write!(f, "preImage({function},{elems})"),
40
            Expression::Inverse(_, a, b) => write!(f, "inverse({a},{b})"),
72
            Expression::Restrict(_, function, domain) => write!(f, "restrict({function},{domain})"),
1360
            Expression::LexLt(_, a, b) => write!(f, "({a} <lex {b})"),
12820
            Expression::LexLeq(_, a, b) => write!(f, "({a} <=lex {b})"),
120
            Expression::LexGt(_, a, b) => write!(f, "({a} >lex {b})"),
180
            Expression::LexGeq(_, a, b) => write!(f, "({a} >=lex {b})"),
240
            Expression::FlatLexLt(_, a, b) => {
240
                write!(f, "FlatLexLt({}, {})", pretty_vec(a), pretty_vec(b))
            }
400
            Expression::FlatLexLeq(_, a, b) => {
400
                write!(f, "FlatLexLeq({}, {})", pretty_vec(a), pretty_vec(b))
            }
40
            Expression::Active(_, variant, field_name) => {
40
                write!(f, "active({variant}, {field_name})")
            }
120
            Expression::ToSet(_, other) => write!(f, "toSet({other})"),
80
            Expression::ToMSet(_, other) => write!(f, "toMSet({other})"),
40
            Expression::ToRelation(_, function) => write!(f, "toRelation({function})"),
48
            Expression::RelationProj(_, relation, projections) => {
48
                let projections_str = projections
48
                    .iter()
144
                    .map(|x| {
144
                        if let Some(x) = x {
48
                            x.to_string()
                        } else {
96
                            String::from("_")
                        }
144
                    })
48
                    .join(", ");
48
                write!(f, "{relation}({projections_str})")
            }
        }
81866092
    }
}
3280
fn minus_operand_return_type(expr: &Expression) -> ReturnType {
1988
    match expr {
1148
        Expression::Atomic(_, Atom::Reference(reference)) => {
1148
            let decl_kind = reference.ptr.kind().clone();
1148
            match decl_kind {
444
                DeclarationKind::Find(var) => var.return_type(),
                DeclarationKind::Given(domain)
                | DeclarationKind::DomainLetting(domain) => domain.return_type(),
280
                DeclarationKind::Quantified(inner) => inner.domain().return_type(),
                DeclarationKind::QuantifiedExpr(inner)
                | DeclarationKind::TemporaryValueLetting(inner)
                // not sure if i should ever be looking at the domain ptr but seems to work
424
                | DeclarationKind::ValueLetting(inner, _) => inner.return_type(),
            }
        }
2132
        _ => expr.return_type(),
    }
3280
}
impl Typeable for Expression {
2292197
    fn return_type(&self) -> ReturnType {
2292197
        match self {
            Expression::Union(_, subject, _) => ReturnType::Set(Box::new(subject.return_type())),
            Expression::Intersect(_, subject, _) => {
                ReturnType::Set(Box::new(subject.return_type()))
            }
600
            Expression::In(_, _, _) => ReturnType::Bool,
            Expression::Supset(_, _, _) => ReturnType::Bool,
            Expression::SupsetEq(_, _, _) => ReturnType::Bool,
            Expression::Subset(_, _, _) => ReturnType::Bool,
            Expression::SubsetEq(_, _, _) => ReturnType::Bool,
135440
            Expression::AbstractLiteral(_, lit) => lit.return_type(),
2480
            Expression::RecordField(_, rec, field_name) => {
2480
                if let ReturnType::Record(ents) = rec.return_type() {
4080
                    for Field { name, value } in ents {
4080
                        if name.eq(field_name) {
2480
                            return value;
1600
                        }
                    }
                }
                ReturnType::Unknown
            }
233504
            Expression::UnsafeIndex(_, subject, idx) | Expression::SafeIndex(_, subject, idx) => {
311144
                let subject_ty = subject.return_type();
311144
                match subject_ty {
                    ReturnType::Matrix(_) => {
                        // For n-dimensional matrices, unwrap the element type until
                        // we either get to the innermost element type or the last index
305384
                        let mut elem_typ = subject_ty;
305384
                        let mut idx_len = idx.len();
611236
                        while idx_len > 0
362120
                            && let ReturnType::Matrix(new_elem_typ) = &elem_typ
305852
                        {
305852
                            elem_typ = *new_elem_typ.clone();
305852
                            idx_len -= 1;
305852
                        }
305384
                        elem_typ
                    }
                    // TODO: We can implement indexing for these eventually
                    ReturnType::Record(_) | ReturnType::Tuple(_) | ReturnType::Variant(_) => {
5760
                        ReturnType::Unknown
                    }
                    _ => bug!(
                        "Invalid indexing operation: expected the operand to be a collection, got {self}: {subject_ty}"
                    ),
                }
            }
480
            Expression::UnsafeSlice(_, subject, _) | Expression::SafeSlice(_, subject, _) => {
640
                ReturnType::Matrix(Box::new(subject.return_type()))
            }
            Expression::InDomain(_, _, _) => ReturnType::Bool,
8
            Expression::Comprehension(_, comp) => comp.return_type(),
            Expression::AbstractComprehension(_, comp) => comp.return_type(),
            Expression::Root(_, _) => ReturnType::Bool,
            Expression::DominanceRelation(_, _) => ReturnType::Bool,
            Expression::FromSolution(_, expr) => expr.return_type(),
            Expression::Metavar(_, _) => ReturnType::Unknown,
1625717
            Expression::Atomic(_, atom) => atom.return_type(),
1120
            Expression::Abs(_, _) => ReturnType::Int,
106008
            Expression::Sum(_, _) => ReturnType::Int,
10924
            Expression::Product(_, _) => ReturnType::Int,
1920
            Expression::Min(_, _) => ReturnType::Int,
2020
            Expression::Max(_, _) => ReturnType::Int,
160
            Expression::Not(_, _) => ReturnType::Bool,
428
            Expression::Or(_, _) => ReturnType::Bool,
1304
            Expression::Imply(_, _, _) => ReturnType::Bool,
            Expression::Iff(_, _, _) => ReturnType::Bool,
8276
            Expression::And(_, _) => ReturnType::Bool,
8704
            Expression::Eq(_, _, _) => ReturnType::Bool,
1520
            Expression::Neq(_, _, _) => ReturnType::Bool,
            Expression::Geq(_, _, _) => ReturnType::Bool,
2200
            Expression::Leq(_, _, _) => ReturnType::Bool,
            Expression::Gt(_, _, _) => ReturnType::Bool,
            Expression::Lt(_, _, _) => ReturnType::Bool,
            Expression::Apart(_, _, _) => ReturnType::Bool,
            Expression::Together(_, _, _) => ReturnType::Bool,
40
            Expression::Party(_, _, subject) => ReturnType::Set(Box::new(subject.return_type())),
40
            Expression::Participants(_, subject) => {
40
                ReturnType::Set(Box::new(subject.return_type()))
            }
40
            Expression::Parts(_, subject) => {
40
                ReturnType::Set(Box::new(ReturnType::Set(Box::new(subject.return_type()))))
            }
13200
            Expression::SafeDiv(_, _, _) => ReturnType::Int,
11400
            Expression::UnsafeDiv(_, _, _) => ReturnType::Int,
            Expression::FlatAllDiff(_, _) => ReturnType::Bool,
80
            Expression::FlatSumGeq(_, _, _) => ReturnType::Bool,
            Expression::FlatSumLeq(_, _, _) => ReturnType::Bool,
            Expression::MinionDivEqUndefZero(_, _, _, _) => ReturnType::Bool,
680
            Expression::FlatIneq(_, _, _, _) => ReturnType::Bool,
2560
            Expression::Flatten(_, _, matrix) => {
2560
                let matrix_type = matrix.return_type();
2560
                match matrix_type {
                    ReturnType::Matrix(_) => {
                        // unwrap until we get to innermost element
2560
                        let mut elem_type = matrix_type;
5120
                        while let ReturnType::Matrix(new_elem_type) = &elem_type {
2560
                            elem_type = *new_elem_type.clone();
2560
                        }
2560
                        ReturnType::Matrix(Box::new(elem_type))
                    }
                    _ => bug!(
                        "Invalid indexing operation: expected the operand to be a collection, got {self}: {matrix_type}"
                    ),
                }
            }
160
            Expression::AllDiff(_, _) => ReturnType::Bool,
            Expression::Table(_, _, _) => ReturnType::Bool,
            Expression::NegativeTable(_, _, _) => ReturnType::Bool,
3120
            Expression::Bubble(_, inner, _) => inner.return_type(),
            Expression::FlatWatchedLiteral(_, _, _) => ReturnType::Bool,
            Expression::MinionReify(_, _, _) => ReturnType::Bool,
240
            Expression::MinionReifyImply(_, _, _) => ReturnType::Bool,
            Expression::MinionWInIntervalSet(_, _, _) => ReturnType::Bool,
            Expression::MinionWInSet(_, _, _) => ReturnType::Bool,
            Expression::MinionElementOne(_, _, _, _) => ReturnType::Bool,
            Expression::AuxDeclaration(_, _, _) => ReturnType::Bool,
1520
            Expression::UnsafeMod(_, _, _) => ReturnType::Int,
4320
            Expression::SafeMod(_, _, _) => ReturnType::Int,
            Expression::MinionModuloEqUndefZero(_, _, _, _) => ReturnType::Bool,
2884
            Expression::Neg(_, _) => ReturnType::Int,
            Expression::Factorial(_, _) => ReturnType::Int,
1012
            Expression::UnsafePow(_, _, _) => ReturnType::Int,
2808
            Expression::SafePow(_, _, _) => ReturnType::Int,
1640
            Expression::Minus(_, a, b) => {
                // rather than calling .return_type on a and b which sometimes errors on references that don't have domains
                // use custom function that extracts return type from atomic references based on each declaration variant
1640
                let a_type = minus_operand_return_type(a);
1640
                let b_type = minus_operand_return_type(b);
1640
                if a_type == ReturnType::Int && b_type == ReturnType::Int {
1640
                    ReturnType::Int
                } else if let ReturnType::Set(a_inner) = a_type
                    && let ReturnType::Set(b_inner) = b_type
                    && a_inner == b_inner
                {
                    ReturnType::Set(a_inner)
                } else {
                    bug!(
                        "Invalid minus operation: operands are of different or invalid types for this operation"
                    )
                }
            }
            Expression::FlatAbsEq(_, _, _) => ReturnType::Bool,
            Expression::FlatMinusEq(_, _, _) => ReturnType::Bool,
            Expression::FlatProductEq(_, _, _, _) => ReturnType::Bool,
            Expression::FlatWeightedSumLeq(_, _, _, _) => ReturnType::Bool,
160
            Expression::FlatWeightedSumGeq(_, _, _, _) => ReturnType::Bool,
            Expression::MinionPow(_, _, _, _) => ReturnType::Bool,
2360
            Expression::ToInt(_, _) => ReturnType::Int,
21960
            Expression::SATInt(..) => ReturnType::Int,
            Expression::PairwiseSum(_, _, _) => ReturnType::Int,
            Expression::PairwiseProduct(_, _, _) => ReturnType::Int,
            Expression::Defined(_, function) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(domain, _) => ReturnType::Set(Box::new(*domain)),
                    _ => bug!(
                        "Invalid defined operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::Range(_, function) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(_, codomain) => ReturnType::Set(Box::new(*codomain)),
                    _ => bug!(
                        "Invalid range operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::Image(_, function, _) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(_, codomain) => *codomain,
                    _ => bug!(
                        "Invalid image operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::ImageSet(_, function, _) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(_, codomain) => ReturnType::Set(Box::new(*codomain)),
                    _ => bug!(
                        "Invalid imageSet operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::PreImage(_, function, _) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(domain, _) => ReturnType::Set(Box::new(*domain)),
                    _ => bug!(
                        "Invalid preImage operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::Restrict(_, function, new_domain) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(_, codomain) => {
                        ReturnType::Function(Box::new(new_domain.return_type()), codomain)
                    }
                    _ => bug!(
                        "Invalid preImage operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::Inverse(..) => ReturnType::Bool,
            Expression::LexLt(..) => ReturnType::Bool,
            Expression::LexGt(..) => ReturnType::Bool,
1360
            Expression::LexLeq(..) => ReturnType::Bool,
            Expression::LexGeq(..) => ReturnType::Bool,
            Expression::FlatLexLt(..) => ReturnType::Bool,
            Expression::FlatLexLeq(..) => ReturnType::Bool,
            Expression::Active(..) => ReturnType::Bool,
            Expression::ToSet(_, other) => {
                let subject = other.return_type();
                match subject {
                    ReturnType::Function(domain, codomain) => {
                        ReturnType::Set(Box::new(ReturnType::Tuple(vec![*domain, *codomain])))
                    }
                    ReturnType::Relation(domains) => {
                        ReturnType::Set(Box::new(ReturnType::Tuple(domains)))
                    }
                    ReturnType::MSet(domain) => ReturnType::Set(Box::new(*domain)),
                    ReturnType::Matrix(domain) => ReturnType::Set(Box::new(*domain)),
                    _ => bug!(
                        "Invalid toSet operation: expected the operand to be a mset, matrix, relation, or function, got {self}: {subject}"
                    ),
                }
            }
            Expression::ToMSet(_, other) => {
                let subject = other.return_type();
                match subject {
                    ReturnType::Function(domain, codomain) => {
                        ReturnType::MSet(Box::new(ReturnType::Tuple(vec![*domain, *codomain])))
                    }
                    ReturnType::Relation(domains) => {
                        ReturnType::MSet(Box::new(ReturnType::Tuple(domains)))
                    }
                    ReturnType::Set(domain) => ReturnType::MSet(Box::new(*domain)),
                    _ => bug!(
                        "Invalid toMSet operation: expected the operand to be a set, relation, or function, got {self}: {subject}"
                    ),
                }
            }
            Expression::ToRelation(_, function) => {
                let subject = function.return_type();
                match subject {
                    ReturnType::Function(domain, codomain) => {
                        ReturnType::Relation(vec![*domain, *codomain])
                    }
                    _ => bug!(
                        "Invalid toRelation operation: expected the operand to be a function, got {self}: {subject}"
                    ),
                }
            }
            Expression::RelationProj(_, relation, projections) => {
                let subject = relation.return_type();
                match subject {
                    ReturnType::Relation(domains) => {
                        let new_doms = domains
                            .iter()
                            .zip(projections.iter())
                            .filter_map(|(domain, included)| {
                                if included.is_none() {
                                    // The domains corresponding to projections which are None remain in the relation
                                    Some(domain.clone())
                                } else {
                                    None
                                }
                            })
                            .collect();
                        ReturnType::Relation(new_doms)
                    }
                    _ => bug!(
                        "Invalid RelationProj operation: expected the operand to be a relation, got {self}: {subject}"
                    ),
                }
            }
            Expression::Card(..) => ReturnType::Int,
            Expression::Subsequence(_, _, _) => ReturnType::Bool,
            Expression::Substring(_, _, _) => ReturnType::Bool,
        }
2292197
    }
}
impl Expression {
    /// Visit each direct `Expression` child by reference, without cloning.
    fn for_each_expr_child(&self, f: &mut impl FnMut(&Expression)) {
        match self {
            // Special Case
            Expression::AbstractLiteral(_, alit) => match alit {
                AbstractLiteral::Set(v) | AbstractLiteral::MSet(v) | AbstractLiteral::Tuple(v) => {
                    for expr in v {
                        f(expr);
                    }
                }
                AbstractLiteral::Partition(two_d_v) => {
                    for part in two_d_v {
                        for expr in part {
                            f(expr);
                        }
                    }
                }
                AbstractLiteral::Matrix(v, _domain) => {
                    for expr in v {
                        f(expr);
                    }
                }
                AbstractLiteral::Record(rs) => {
                    for r in rs {
                        f(&r.value);
                    }
                }
                AbstractLiteral::Sequence(v) => {
                    for expr in v {
                        f(expr);
                    }
                }
                AbstractLiteral::Function(vs) => {
                    for (a, b) in vs {
                        f(a);
                        f(b);
                    }
                }
                AbstractLiteral::Variant(v) => {
                    f(&v.value);
                }
                AbstractLiteral::Relation(vs) => {
                    for exprs in vs {
                        for expr in exprs {
                            f(expr);
                        }
                    }
                }
            },
            Expression::Root(_, vs) => {
                for expr in vs {
                    f(expr);
                }
            }
            // Moo<Expression>
            Expression::DominanceRelation(_, m1)
            | Expression::ToInt(_, m1)
            | Expression::Abs(_, m1)
            | Expression::Sum(_, m1)
            | Expression::Product(_, m1)
            | Expression::Min(_, m1)
            | Expression::Max(_, m1)
            | Expression::Not(_, m1)
            | Expression::Or(_, m1)
            | Expression::And(_, m1)
            | Expression::Neg(_, m1)
            | Expression::Defined(_, m1)
            | Expression::AllDiff(_, m1)
            | Expression::Factorial(_, m1)
            | Expression::Range(_, m1)
            | Expression::Participants(_, m1)
            | Expression::Parts(_, m1)
            | Expression::ToSet(_, m1)
            | Expression::ToMSet(_, m1)
            | Expression::ToRelation(_, m1)
            | Expression::Card(_, m1)
            | Expression::RecordField(_, m1, _)
            | Expression::Active(_, m1, _) => {
                f(m1);
            }
            // Moo<Expression> + Moo<Expression>
            Expression::Table(_, m1, m2)
            | Expression::NegativeTable(_, m1, m2)
            | Expression::Bubble(_, m1, m2)
            | Expression::Imply(_, m1, m2)
            | Expression::Iff(_, m1, m2)
            | Expression::Union(_, m1, m2)
            | Expression::In(_, m1, m2)
            | Expression::Intersect(_, m1, m2)
            | Expression::Supset(_, m1, m2)
            | Expression::SupsetEq(_, m1, m2)
            | Expression::Subset(_, m1, m2)
            | Expression::SubsetEq(_, m1, m2)
            | Expression::Eq(_, m1, m2)
            | Expression::Neq(_, m1, m2)
            | Expression::Geq(_, m1, m2)
            | Expression::Leq(_, m1, m2)
            | Expression::Gt(_, m1, m2)
            | Expression::Lt(_, m1, m2)
            | Expression::SafeDiv(_, m1, m2)
            | Expression::UnsafeDiv(_, m1, m2)
            | Expression::SafeMod(_, m1, m2)
            | Expression::UnsafeMod(_, m1, m2)
            | Expression::UnsafePow(_, m1, m2)
            | Expression::SafePow(_, m1, m2)
            | Expression::Minus(_, m1, m2)
            | Expression::PairwiseSum(_, m1, m2)
            | Expression::PairwiseProduct(_, m1, m2)
            | Expression::Image(_, m1, m2)
            | Expression::ImageSet(_, m1, m2)
            | Expression::PreImage(_, m1, m2)
            | Expression::Inverse(_, m1, m2)
            | Expression::Restrict(_, m1, m2)
            | Expression::Apart(_, m1, m2)
            | Expression::Together(_, m1, m2)
            | Expression::Party(_, m1, m2)
            | Expression::LexLt(_, m1, m2)
            | Expression::LexLeq(_, m1, m2)
            | Expression::LexGt(_, m1, m2)
            | Expression::LexGeq(_, m1, m2)
            | Expression::Subsequence(_, m1, m2)
            | Expression::Substring(_, m1, m2) => {
                f(m1);
                f(m2);
            }
            // Moo<Expression> + Vec<Expression>
            Expression::UnsafeIndex(_, m, vs) | Expression::SafeIndex(_, m, vs) => {
                f(m);
                for v in vs {
                    f(v);
                }
            }
            // Moo<Expression> + Vec<Option<Expression>>
            Expression::UnsafeSlice(_, m, vs)
            | Expression::SafeSlice(_, m, vs)
            | Expression::RelationProj(_, m, vs) => {
                f(m);
                for e in vs.iter().flatten() {
                    f(e);
                }
            }
            // Moo<Expression> + DomainPtr
            Expression::InDomain(_, m, _) => {
                f(m);
            }
            // Option<Moo<Expression>> + Moo<Expression>
            Expression::Flatten(_, opt, m) => {
                if let Some(e) = opt {
                    f(e);
                }
                f(m);
            }
            // Moo<Expression> + Atom
            Expression::MinionReify(_, m, _) | Expression::MinionReifyImply(_, m, _) => {
                f(m);
            }
            // Reference + Moo<Expression>
            Expression::AuxDeclaration(_, _, m) => {
                f(m);
            }
            // SATIntEncoding + Moo<Expression> + (i32, i32)
            Expression::SATInt(_, _, m, _) => {
                f(m);
            }
            // No Expression children
            Expression::Comprehension(_, _)
            | Expression::AbstractComprehension(_, _)
            | Expression::Atomic(_, _)
            | Expression::FromSolution(_, _)
            | Expression::Metavar(_, _)
            | Expression::FlatAbsEq(_, _, _)
            | Expression::FlatMinusEq(_, _, _)
            | Expression::FlatProductEq(_, _, _, _)
            | Expression::MinionDivEqUndefZero(_, _, _, _)
            | Expression::MinionModuloEqUndefZero(_, _, _, _)
            | Expression::MinionPow(_, _, _, _)
            | Expression::FlatAllDiff(_, _)
            | Expression::FlatSumGeq(_, _, _)
            | Expression::FlatSumLeq(_, _, _)
            | Expression::FlatIneq(_, _, _, _)
            | Expression::FlatWatchedLiteral(_, _, _)
            | Expression::FlatWeightedSumLeq(_, _, _, _)
            | Expression::FlatWeightedSumGeq(_, _, _, _)
            | Expression::MinionWInIntervalSet(_, _, _)
            | Expression::MinionWInSet(_, _, _)
            | Expression::MinionElementOne(_, _, _, _)
            | Expression::FlatLexLt(_, _, _)
            | Expression::FlatLexLeq(_, _, _) => {}
        }
    }
}
impl CacheHashable for Expression {
    fn invalidate_cache(&self) {
        self.meta_ref()
            .stored_hash
            .store(NO_HASH, Ordering::Relaxed);
    }
    fn invalidate_cache_recursive(&self) {
        self.invalidate_cache();
        self.for_each_expr_child(&mut |child| {
            child.invalidate_cache_recursive();
        });
    }
    fn get_cached_hash(&self) -> u64 {
        let stored = self.meta_ref().stored_hash.load(Ordering::Relaxed);
        if stored != NO_HASH {
            HASH_HITS.fetch_add(1, Ordering::Relaxed);
            return stored;
        }
        HASH_MISSES.fetch_add(1, Ordering::Relaxed);
        self.calculate_hash()
    }
    fn calculate_hash(&self) -> u64 {
        let mut hasher = DefaultHasher::new();
        std::mem::discriminant(self).hash(&mut hasher);
        match self {
            // Special Case
            Expression::AbstractLiteral(_, alit) => match alit {
                AbstractLiteral::Set(v)
                | AbstractLiteral::MSet(v)
                | AbstractLiteral::Tuple(v)
                | AbstractLiteral::Sequence(v) => {
                    for expr in v {
                        expr.get_cached_hash().hash(&mut hasher);
                    }
                }
                AbstractLiteral::Matrix(v, domain) => {
                    domain.hash(&mut hasher);
                    for expr in v {
                        expr.get_cached_hash().hash(&mut hasher);
                    }
                }
                AbstractLiteral::Record(rs) => {
                    for r in rs {
                        r.name.hash(&mut hasher);
                        r.value.get_cached_hash().hash(&mut hasher);
                    }
                }
                AbstractLiteral::Function(vs) => {
                    for (a, b) in vs {
                        a.get_cached_hash().hash(&mut hasher);
                        b.get_cached_hash().hash(&mut hasher);
                    }
                }
                AbstractLiteral::Variant(v) => {
                    v.name.hash(&mut hasher);
                    v.value.get_cached_hash().hash(&mut hasher);
                }
                AbstractLiteral::Relation(v) => {
                    for exprs in v {
                        for expr in exprs {
                            expr.get_cached_hash().hash(&mut hasher);
                        }
                    }
                }
                AbstractLiteral::Partition(v) => {
                    for exprs in v {
                        for expr in exprs {
                            expr.get_cached_hash().hash(&mut hasher);
                        }
                    }
                }
            },
            Expression::Root(_, vs) => {
                for expr in vs {
                    expr.get_cached_hash().hash(&mut hasher);
                }
            }
            // Moo<Expression>
            Expression::DominanceRelation(_, m1)
            | Expression::ToInt(_, m1)
            | Expression::Abs(_, m1)
            | Expression::Sum(_, m1)
            | Expression::Product(_, m1)
            | Expression::Min(_, m1)
            | Expression::Max(_, m1)
            | Expression::Not(_, m1)
            | Expression::Or(_, m1)
            | Expression::And(_, m1)
            | Expression::Neg(_, m1)
            | Expression::Defined(_, m1)
            | Expression::AllDiff(_, m1)
            | Expression::Factorial(_, m1)
            | Expression::Participants(_, m1)
            | Expression::Parts(_, m1)
            | Expression::Range(_, m1)
            | Expression::ToSet(_, m1)
            | Expression::ToMSet(_, m1)
            | Expression::ToRelation(_, m1)
            | Expression::Card(_, m1) => {
                m1.get_cached_hash().hash(&mut hasher);
            }
            // Moo<Expression> + Moo<Expression>
            Expression::Table(_, m1, m2)
            | Expression::NegativeTable(_, m1, m2)
            | Expression::Bubble(_, m1, m2)
            | Expression::Imply(_, m1, m2)
            | Expression::Iff(_, m1, m2)
            | Expression::Union(_, m1, m2)
            | Expression::In(_, m1, m2)
            | Expression::Intersect(_, m1, m2)
            | Expression::Supset(_, m1, m2)
            | Expression::SupsetEq(_, m1, m2)
            | Expression::Subset(_, m1, m2)
            | Expression::SubsetEq(_, m1, m2)
            | Expression::Eq(_, m1, m2)
            | Expression::Neq(_, m1, m2)
            | Expression::Geq(_, m1, m2)
            | Expression::Leq(_, m1, m2)
            | Expression::Gt(_, m1, m2)
            | Expression::Lt(_, m1, m2)
            | Expression::Apart(_, m1, m2)
            | Expression::Together(_, m1, m2)
            | Expression::Party(_, m1, m2)
            | Expression::SafeDiv(_, m1, m2)
            | Expression::UnsafeDiv(_, m1, m2)
            | Expression::SafeMod(_, m1, m2)
            | Expression::UnsafeMod(_, m1, m2)
            | Expression::UnsafePow(_, m1, m2)
            | Expression::SafePow(_, m1, m2)
            | Expression::Minus(_, m1, m2)
            | Expression::PairwiseSum(_, m1, m2)
            | Expression::PairwiseProduct(_, m1, m2)
            | Expression::Image(_, m1, m2)
            | Expression::ImageSet(_, m1, m2)
            | Expression::PreImage(_, m1, m2)
            | Expression::Inverse(_, m1, m2)
            | Expression::Restrict(_, m1, m2)
            | Expression::LexLt(_, m1, m2)
            | Expression::LexLeq(_, m1, m2)
            | Expression::LexGt(_, m1, m2)
            | Expression::LexGeq(_, m1, m2)
            | Expression::Subsequence(_, m1, m2)
            | Expression::Substring(_, m1, m2) => {
                m1.get_cached_hash().hash(&mut hasher);
                m2.get_cached_hash().hash(&mut hasher);
            }
            // Moo<Expression> + Vec<Expression>
            Expression::UnsafeIndex(_, m, vs) | Expression::SafeIndex(_, m, vs) => {
                m.get_cached_hash().hash(&mut hasher);
                for v in vs {
                    v.get_cached_hash().hash(&mut hasher);
                }
            }
            // Moo<Expression> + Vec<Option<Expression>>
            Expression::UnsafeSlice(_, m, vs)
            | Expression::SafeSlice(_, m, vs)
            | Expression::RelationProj(_, m, vs) => {
                m.get_cached_hash().hash(&mut hasher);
                for v in vs {
                    match v {
                        Some(e) => e.get_cached_hash().hash(&mut hasher),
                        None => 0u64.hash(&mut hasher),
                    }
                }
            }
            // Moo<Expression> + Name
            Expression::RecordField(_, m, n) | Expression::Active(_, m, n) => {
                m.get_cached_hash().hash(&mut hasher);
                n.hash(&mut hasher);
            }
            // Moo<Expression> + DomainPtr
            Expression::InDomain(_, m, d) => {
                m.get_cached_hash().hash(&mut hasher);
                d.hash(&mut hasher);
            }
            // Option<Moo<Expression>> + Moo<Expression>
            Expression::Flatten(_, opt, m) => {
                if let Some(e) = opt {
                    e.get_cached_hash().hash(&mut hasher);
                }
                m.get_cached_hash().hash(&mut hasher);
            }
            // Moo<Expression> + Atom
            Expression::MinionReify(_, m, a) | Expression::MinionReifyImply(_, m, a) => {
                m.get_cached_hash().hash(&mut hasher);
                a.hash(&mut hasher);
            }
            // Reference + Moo<Expression>
            Expression::AuxDeclaration(_, r, m) => {
                r.hash(&mut hasher);
                m.get_cached_hash().hash(&mut hasher);
            }
            // SATIntEncoding + Moo<Expression> + (i32, i32)
            Expression::SATInt(_, enc, m, bounds) => {
                enc.hash(&mut hasher);
                m.get_cached_hash().hash(&mut hasher);
                bounds.hash(&mut hasher);
            }
            // Non-Expression Moo types - hash normally
            Expression::Comprehension(_, c) => c.hash(&mut hasher),
            Expression::AbstractComprehension(_, c) => c.hash(&mut hasher),
            // Leaf types - no Expression children
            Expression::Atomic(_, a) => a.hash(&mut hasher),
            Expression::FromSolution(_, a) => a.hash(&mut hasher),
            Expression::Metavar(_, u) => u.hash(&mut hasher),
            // Two Moo<Atom>
            Expression::FlatAbsEq(_, a1, a2) | Expression::FlatMinusEq(_, a1, a2) => {
                a1.hash(&mut hasher);
                a2.hash(&mut hasher);
            }
            // Three Moo<Atom>
            Expression::FlatProductEq(_, a1, a2, a3)
            | Expression::MinionDivEqUndefZero(_, a1, a2, a3)
            | Expression::MinionModuloEqUndefZero(_, a1, a2, a3)
            | Expression::MinionPow(_, a1, a2, a3) => {
                a1.hash(&mut hasher);
                a2.hash(&mut hasher);
                a3.hash(&mut hasher);
            }
            // Vec<Atom>
            Expression::FlatAllDiff(_, vs) => {
                for v in vs {
                    v.hash(&mut hasher);
                }
            }
            // Vec<Atom> + Atom
            Expression::FlatSumGeq(_, vs, a) | Expression::FlatSumLeq(_, vs, a) => {
                for v in vs {
                    v.hash(&mut hasher);
                }
                a.hash(&mut hasher);
            }
            // Moo<Atom> + Moo<Atom> + Box<Literal>
            Expression::FlatIneq(_, a1, a2, lit) => {
                a1.hash(&mut hasher);
                a2.hash(&mut hasher);
                lit.hash(&mut hasher);
            }
            // Reference + Literal
            Expression::FlatWatchedLiteral(_, r, l) => {
                r.hash(&mut hasher);
                l.hash(&mut hasher);
            }
            // Vec<Literal> + Vec<Atom> + Moo<Atom>
            Expression::FlatWeightedSumLeq(_, lits, atoms, a)
            | Expression::FlatWeightedSumGeq(_, lits, atoms, a) => {
                for l in lits {
                    l.hash(&mut hasher);
                }
                for at in atoms {
                    at.hash(&mut hasher);
                }
                a.hash(&mut hasher);
            }
            // Atom + Vec<i32>
            Expression::MinionWInIntervalSet(_, a, vs) | Expression::MinionWInSet(_, a, vs) => {
                a.hash(&mut hasher);
                for v in vs {
                    v.hash(&mut hasher);
                }
            }
            // Vec<Atom> + Moo<Atom> + Moo<Atom>
            Expression::MinionElementOne(_, vs, a1, a2) => {
                for v in vs {
                    v.hash(&mut hasher);
                }
                a1.hash(&mut hasher);
                a2.hash(&mut hasher);
            }
            // Vec<Atom> + Vec<Atom>
            Expression::FlatLexLt(_, v1, v2) | Expression::FlatLexLeq(_, v1, v2) => {
                for v in v1 {
                    v.hash(&mut hasher);
                }
                for v in v2 {
                    v.hash(&mut hasher);
                }
            }
        };
        let result = hasher.finish();
        self.meta_ref().stored_hash.swap(result, Ordering::Relaxed);
        result
    }
}
#[cfg(test)]
mod tests {
    use crate::matrix_expr;
    use super::*;
    #[test]
1
    fn test_domain_of_constant_sum() {
1
        let c1 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1)));
1
        let c2 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2)));
1
        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![c1, c2]));
1
        assert_eq!(sum.domain_of(), Some(Domain::int(vec![Range::Single(3)])));
1
    }
    #[test]
1
    fn test_domain_of_constant_invalid_type() {
1
        let c1 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1)));
1
        let c2 = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)));
1
        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![c1, c2]));
1
        assert_eq!(sum.domain_of(), None);
1
    }
    #[test]
1
    fn test_domain_of_empty_sum() {
1
        let sum = Expression::Sum(Metadata::new(), Moo::new(matrix_expr![]));
1
        assert_eq!(sum.domain_of(), None);
1
    }
}