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::FieldValue;
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=FieldValue<Expression>)]
89
#[biplate(to=FieldValue<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
    /// A matrix index.
131
    ///
132
    /// Defined iff the indices are within their respective index domains.
133
    #[compatible(JsonInput)]
134
    UnsafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
135

            
136
    /// A safe matrix index.
137
    ///
138
    /// See [`Expression::UnsafeIndex`]
139
    #[compatible(SMT)]
140
    SafeIndex(Metadata, Moo<Expression>, Vec<Expression>),
141

            
142
    /// A matrix slice: `a[indices]`.
143
    ///
144
    /// One of the indicies may be `None`, representing the dimension of the matrix we want to take
145
    /// a slice of. For example, for some 3d matrix a, `a[1,..,2]` has the indices
146
    /// `Some(1),None,Some(2)`.
147
    ///
148
    /// It is assumed that the slice only has one "wild-card" dimension and thus is 1 dimensional.
149
    ///
150
    /// Defined iff the defined indices are within their respective index domains.
151
    #[compatible(JsonInput)]
152
    UnsafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
153

            
154
    /// A safe matrix slice: `a[indices]`.
155
    ///
156
    /// See [`Expression::UnsafeSlice`].
157
    SafeSlice(Metadata, Moo<Expression>, Vec<Option<Expression>>),
158

            
159
    /// `inDomain(x,domain)` iff `x` is in the domain `domain`.
160
    ///
161
    /// This cannot be constructed from Essence input, nor passed to a solver: this expression is
162
    /// mainly used during the conversion of `UnsafeIndex` and `UnsafeSlice` to `SafeIndex` and
163
    /// `SafeSlice` respectively.
164
    InDomain(Metadata, Moo<Expression>, DomainPtr),
165

            
166
    /// `toInt(b)` casts boolean expression b to an integer.
167
    ///
168
    /// - If b is false, then `toInt(b) == 0`
169
    ///
170
    /// - If b is true, then `toInt(b) == 1`
171
    #[compatible(SMT)]
172
    ToInt(Metadata, Moo<Expression>),
173

            
174
    /// `|x|` - absolute value of `x`
175
    #[compatible(JsonInput, SMT)]
176
    Abs(Metadata, Moo<Expression>),
177

            
178
    /// `sum(<vec_expr>)`
179
    #[compatible(JsonInput, SMT)]
180
    Sum(Metadata, Moo<Expression>),
181

            
182
    /// `a * b * c * ...`
183
    #[compatible(JsonInput, SMT)]
184
    Product(Metadata, Moo<Expression>),
185

            
186
    /// `min(<vec_expr>)`
187
    #[compatible(JsonInput, SMT)]
188
    Min(Metadata, Moo<Expression>),
189

            
190
    /// `max(<vec_expr>)`
191
    #[compatible(JsonInput, SMT)]
192
    Max(Metadata, Moo<Expression>),
193

            
194
    /// `not(a)`
195
    #[compatible(JsonInput, SAT, SMT)]
196
    Not(Metadata, Moo<Expression>),
197

            
198
    /// `or(<vec_expr>)`
199
    #[compatible(JsonInput, SAT, SMT)]
200
    Or(Metadata, Moo<Expression>),
201

            
202
    /// `and(<vec_expr>)`
203
    #[compatible(JsonInput, SAT, SMT)]
204
    And(Metadata, Moo<Expression>),
205

            
206
    /// Ensures that `a->b` (material implication).
207
    #[compatible(JsonInput, SMT)]
208
    Imply(Metadata, Moo<Expression>, Moo<Expression>),
209

            
210
    /// `iff(a, b)` a <-> b
211
    #[compatible(JsonInput, SMT)]
212
    Iff(Metadata, Moo<Expression>, Moo<Expression>),
213

            
214
    #[compatible(JsonInput)]
215
    Union(Metadata, Moo<Expression>, Moo<Expression>),
216

            
217
    #[compatible(JsonInput)]
218
    In(Metadata, Moo<Expression>, Moo<Expression>),
219

            
220
    #[compatible(JsonInput)]
221
    Intersect(Metadata, Moo<Expression>, Moo<Expression>),
222

            
223
    #[compatible(JsonInput)]
224
    Supset(Metadata, Moo<Expression>, Moo<Expression>),
225

            
226
    #[compatible(JsonInput)]
227
    SupsetEq(Metadata, Moo<Expression>, Moo<Expression>),
228

            
229
    #[compatible(JsonInput)]
230
    Subset(Metadata, Moo<Expression>, Moo<Expression>),
231

            
232
    #[compatible(JsonInput)]
233
    SubsetEq(Metadata, Moo<Expression>, Moo<Expression>),
234

            
235
    #[compatible(JsonInput, SMT)]
236
    Eq(Metadata, Moo<Expression>, Moo<Expression>),
237

            
238
    #[compatible(JsonInput, SMT)]
239
    Neq(Metadata, Moo<Expression>, Moo<Expression>),
240

            
241
    #[compatible(JsonInput, SMT)]
242
    Geq(Metadata, Moo<Expression>, Moo<Expression>),
243

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

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

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

            
253
    /// `s subsequence t` tests whether the list of values taken by s occurs in the same order
254
    /// in the list of values taken by t
255
    #[compatible(JsonInput)]
256
    Subsequence(Metadata, Moo<Expression>, Moo<Expression>),
257

            
258
    /// `s substring t` tests whether the list of values taken by s occurs in the same order
259
    /// and contiguously in the list of values taken by t
260
    #[compatible(JsonInput)]
261
    Substring(Metadata, Moo<Expression>, Moo<Expression>),
262

            
263
    /// Division after preventing division by zero, usually with a bubble
264
    #[compatible(SMT)]
265
    SafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
266

            
267
    /// Division with a possibly undefined value (division by 0)
268
    #[compatible(JsonInput)]
269
    UnsafeDiv(Metadata, Moo<Expression>, Moo<Expression>),
270

            
271
    /// Modulo after preventing mod 0, usually with a bubble
272
    #[compatible(SMT)]
273
    SafeMod(Metadata, Moo<Expression>, Moo<Expression>),
274

            
275
    /// Modulo with a possibly undefined value (mod 0)
276
    #[compatible(JsonInput)]
277
    UnsafeMod(Metadata, Moo<Expression>, Moo<Expression>),
278

            
279
    /// Negation: `-x`
280
    #[compatible(JsonInput, SMT)]
281
    Neg(Metadata, Moo<Expression>),
282

            
283
    /// Factorial: `x!` or 'factorial(x)`
284
    #[compatible(JsonInput)]
285
    Factorial(Metadata, Moo<Expression>),
286

            
287
    /// Set of domain values function is defined for
288
    #[compatible(JsonInput)]
289
    Defined(Metadata, Moo<Expression>),
290

            
291
    /// Set of codomain values function is defined for
292
    #[compatible(JsonInput)]
293
    Range(Metadata, Moo<Expression>),
294

            
295
    #[compatible(JsonInput)]
296
    ToSet(Metadata, Moo<Expression>),
297

            
298
    #[compatible(JsonInput)]
299
    ToMSet(Metadata, Moo<Expression>),
300

            
301
    #[compatible(JsonInput)]
302
    ToRelation(Metadata, Moo<Expression>),
303

            
304
    /// Unsafe power`x**y` (possibly undefined)
305
    ///
306
    /// Defined when (X!=0 \\/ Y!=0) /\ Y>=0
307
    #[compatible(JsonInput)]
308
    UnsafePow(Metadata, Moo<Expression>, Moo<Expression>),
309

            
310
    /// `UnsafePow` after preventing undefinedness
311
    SafePow(Metadata, Moo<Expression>, Moo<Expression>),
312

            
313
    /// Flatten matrix operator
314
    /// `flatten(M)` or `flatten(n, M)`
315
    /// where M is a matrix and n is an optional integer argument indicating depth of flattening
316
    Flatten(Metadata, Option<Moo<Expression>>, Moo<Expression>),
317

            
318
    /// `allDiff(<vec_expr>)`
319
    #[compatible(JsonInput)]
320
    AllDiff(Metadata, Moo<Expression>),
321

            
322
    /// `table([x1, x2, ...], [[r11, r12, ...], [r21, r22, ...], ...])`
323
    ///
324
    /// Represents a positive table constraint: the tuple `[x1, x2, ...]` must match one of the
325
    /// allowed rows.
326
    #[compatible(JsonInput)]
327
    Table(Metadata, Moo<Expression>, Moo<Expression>),
328

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

            
343
    /// Partition Operator: test if a list of elements are not all contained in one part of the partition
344
    /// First Expr Arg is a list of elements
345
    /// Second Expr Arg is the partition
346
    #[compatible(JsonInput)]
347
    Apart(Metadata, Moo<Expression>, Moo<Expression>),
348

            
349
    /// Partition Operator: union of all parts of a partition
350
    /// Expr Arg is a partition
351
    #[compatible(JsonInput)]
352
    Participants(Metadata, Moo<Expression>),
353

            
354
    /// Partition Operator: part of partition that contains specified element
355
    /// First Expr Arg is an element that should be contained
356
    /// Second Expr Arg is the partition that should contain that element
357
    #[compatible(JsonInput)]
358
    Party(Metadata, Moo<Expression>, Moo<Expression>),
359

            
360
    /// Partition Operator: partition to its set of parts
361
    /// Expr Arg is the partition from which the parts come from
362
    #[compatible(JsonInput)]
363
    Parts(Metadata, Moo<Expression>),
364

            
365
    /// Partition Operator: test if a list of elements are all in the same part of the partition
366
    /// First Expr Arg is the list of elements to test with
367
    /// Second Expr Arg is the partition to test on
368
    #[compatible(JsonInput)]
369
    Together(Metadata, Moo<Expression>, Moo<Expression>),
370

            
371
    /// Ensures that x=|y| i.e. x is the absolute value of y.
372
    ///
373
    /// Low-level Minion constraint.
374
    ///
375
    /// # See also
376
    ///
377
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#abs)
378
    #[compatible(Minion)]
379
    FlatAbsEq(Metadata, Moo<Atom>, Moo<Atom>),
380

            
381
    /// Ensures that `alldiff([a,b,...])`.
382
    ///
383
    /// Low-level Minion constraint.
384
    ///
385
    /// # See also
386
    ///
387
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#alldiff)
388
    #[compatible(Minion)]
389
    FlatAllDiff(Metadata, Vec<Atom>),
390

            
391
    /// Ensures that sum(vec) >= x.
392
    ///
393
    /// Low-level Minion constraint.
394
    ///
395
    /// # See also
396
    ///
397
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#sumgeq)
398
    #[compatible(Minion)]
399
    FlatSumGeq(Metadata, Vec<Atom>, 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#sumleq)
408
    #[compatible(Minion)]
409
    FlatSumLeq(Metadata, Vec<Atom>, Atom),
410

            
411
    /// `ineq(x,y,k)` ensures that x <= y + k.
412
    ///
413
    /// Low-level Minion constraint.
414
    ///
415
    /// # See also
416
    ///
417
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#ineq)
418
    #[compatible(Minion)]
419
    FlatIneq(Metadata, Moo<Atom>, Moo<Atom>, Box<Literal>),
420

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

            
437
    /// `weightedsumleq(cs,xs,total)` ensures that cs.xs <= total, where cs.xs is the scalar dot
438
    /// product of cs and xs.
439
    ///
440
    /// Low-level Minion constraint.
441
    ///
442
    /// Represents a weighted sum of the form `ax + by + cz + ...`
443
    ///
444
    /// # See also
445
    ///
446
    /// + [Minion
447
    /// documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#weightedsumleq)
448
    FlatWeightedSumLeq(Metadata, Vec<Literal>, Vec<Atom>, Moo<Atom>),
449

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

            
463
    /// Ensures that x =-y, where x and y are atoms.
464
    ///
465
    /// Low-level Minion constraint.
466
    ///
467
    /// # See also
468
    ///
469
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#minuseq)
470
    #[compatible(Minion)]
471
    FlatMinusEq(Metadata, Moo<Atom>, Moo<Atom>),
472

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

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

            
493
    /// Ensures that 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#mod_undefzero)
500
    #[compatible(Minion)]
501
    MinionModuloEqUndefZero(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
502

            
503
    /// Ensures that `x**y = z`.
504
    ///
505
    /// Low-level Minion constraint.
506
    ///
507
    /// This constraint is false when `y<0` except for `1**y=1` and `(-1)**y=z` (where z is 1 if y
508
    /// is odd and z is -1 if y is even).
509
    ///
510
    /// # See also
511
    ///
512
    /// + [Github comment about `pow` semantics](https://github.com/minion/minion/issues/40#issuecomment-2595914891)
513
    /// + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#pow)
514
    MinionPow(Metadata, Moo<Atom>, Moo<Atom>, Moo<Atom>),
515

            
516
    /// `reify(constraint,r)` ensures that r=1 iff `constraint` is satisfied, where r is a 0/1
517
    /// variable.
518
    ///
519
    /// Low-level Minion constraint.
520
    ///
521
    /// # See also
522
    ///
523
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#reify)
524
    #[compatible(Minion)]
525
    MinionReify(Metadata, Moo<Expression>, Atom),
526

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

            
538
    /// `w-inintervalset(x, [a1,a2, b1,b2, … ])` ensures that the value of x belongs to one of the
539
    /// intervals {a1,…,a2}, {b1,…,b2} etc.
540
    ///
541
    /// The list of intervals must be given in numerical order.
542
    ///
543
    /// Low-level Minion constraint.
544
    ///
545
    /// # See also
546
    ///>
547
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inintervalset)
548
    #[compatible(Minion)]
549
    MinionWInIntervalSet(Metadata, Atom, Vec<i32>),
550

            
551
    /// `w-inset(x, [v1, v2, … ])` ensures that the value of `x` is one of the explicitly given values `v1`, `v2`, etc.
552
    ///
553
    /// This constraint enforces membership in a specific set of discrete values rather than intervals.
554
    ///
555
    /// The list of values must be given in numerical order.
556
    ///
557
    /// Low-level Minion constraint.
558
    ///
559
    /// # See also
560
    ///
561
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#w-inset)
562
    #[compatible(Minion)]
563
    MinionWInSet(Metadata, Atom, Vec<i32>),
564

            
565
    /// `element_one(vec, i, e)` specifies that `vec[i] = e`. This implies that i is
566
    /// in the range `[1..len(vec)]`.
567
    ///
568
    /// Low-level Minion constraint.
569
    ///
570
    /// # See also
571
    ///
572
    ///  + [Minion documentation](https://minion-solver.readthedocs.io/en/stable/usage/constraints.html#element_one)
573
    #[compatible(Minion)]
574
    MinionElementOne(Metadata, Vec<Atom>, Moo<Atom>, Moo<Atom>),
575

            
576
    /// Declaration of an auxiliary variable.
577
    ///
578
    /// As with Savile Row, we semantically distinguish this from `Eq`.
579
    #[compatible(Minion)]
580
    #[polyquine_skip]
581
    AuxDeclaration(Metadata, Reference, Moo<Expression>),
582

            
583
    /// 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.
584
    #[compatible(SAT)]
585
    SATInt(Metadata, SATIntEncoding, Moo<Expression>, (i32, i32)),
586

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

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

            
597
    #[compatible(JsonInput)]
598
    Image(Metadata, Moo<Expression>, Moo<Expression>),
599

            
600
    #[compatible(JsonInput)]
601
    ImageSet(Metadata, Moo<Expression>, Moo<Expression>),
602

            
603
    #[compatible(JsonInput)]
604
    PreImage(Metadata, Moo<Expression>, Moo<Expression>),
605

            
606
    #[compatible(JsonInput)]
607
    Inverse(Metadata, Moo<Expression>, Moo<Expression>),
608

            
609
    #[compatible(JsonInput)]
610
    Restrict(Metadata, Moo<Expression>, Moo<Expression>),
611

            
612
    /// Lexicographical < between two matrices.
613
    ///
614
    /// A <lex B iff: A[i] < B[i] for some i /\ (A[j] > B[j] for some j -> i < j)
615
    /// I.e. A must be less than B at some index i, and if it is greater than B at another index j,
616
    /// then j comes after i.
617
    /// I.e. A must be greater than B at the first index where they differ.
618
    ///
619
    /// E.g. [1, 1] <lex [2, 1] and [1, 1] <lex [1, 2]
620
    LexLt(Metadata, Moo<Expression>, Moo<Expression>),
621

            
622
    /// Lexicographical <= between two matrices
623
    LexLeq(Metadata, Moo<Expression>, Moo<Expression>),
624

            
625
    /// Lexicographical > between two matrices
626
    /// This is a parser-level construct, and is immediately normalised to LexLt(b, a)
627
    LexGt(Metadata, Moo<Expression>, Moo<Expression>),
628

            
629
    /// Lexicographical >= between two matrices
630
    /// This is a parser-level construct, and is immediately normalised to LexLeq(b, a)
631
    LexGeq(Metadata, Moo<Expression>, Moo<Expression>),
632

            
633
    /// Low-level minion constraint. See Expression::LexLt
634
    FlatLexLt(Metadata, Vec<Atom>, Vec<Atom>),
635

            
636
    /// Low-level minion constraint. See Expression::LexLeq
637
    FlatLexLeq(Metadata, Vec<Atom>, Vec<Atom>),
638

            
639
    /// To tell which field is used in a variant domain
640
    #[compatible(JsonInput)]
641
    Active(Metadata, Moo<Expression>, Moo<Expression>),
642

            
643
    /// Alters the shape of relations by projection
644
    #[compatible(JsonInput)]
645
    RelationProj(Metadata, Moo<Expression>, Vec<Option<Expression>>),
646

            
647
    /// Cardinality of a collection type
648
    #[compatible(JsonInput)]
649
    Card(Metadata, Moo<Expression>),
650
}
651

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

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

            
680
1807523
    let expr = exprs.pop()?;
681
1807522
    let dom = expr.domain_of()?;
682
1804762
    let resolved = dom.resolve()?;
683
1804402
    let GroundDomain::Int(ranges) = resolved.as_ref() else {
684
13
        return None;
685
    };
686

            
687
1804389
    let (mut current_min, mut current_max) = range_vec_bounds_i32(ranges)?;
688

            
689
2053175
    for expr in exprs {
690
2053175
        let dom = expr.domain_of()?;
691
2052815
        let resolved = dom.resolve()?;
692
2052695
        let GroundDomain::Int(ranges) = resolved.as_ref() else {
693
            return None;
694
        };
695

            
696
2052695
        let (min, max) = range_vec_bounds_i32(ranges)?;
697

            
698
        // all the possible new values for current_min / current_max
699
2052695
        let minmax = op(min, current_max)?;
700
2052695
        let minmin = op(min, current_min)?;
701
2052695
        let maxmin = op(max, current_min)?;
702
2052695
        let maxmax = op(max, current_max)?;
703
2052695
        let vals = [minmax, minmin, maxmin, maxmax];
704

            
705
2052695
        current_min = *vals
706
2052695
            .iter()
707
2052695
            .min()
708
2052695
            .expect("vals iterator should not be empty, and should have a minimum.");
709
2052695
        current_max = *vals
710
2052695
            .iter()
711
2052695
            .max()
712
2052695
            .expect("vals iterator should not be empty, and should have a maximum.");
713
    }
714

            
715
1803909
    if current_min == current_max {
716
1061
        Some(Domain::int(vec![Range::Single(current_min)]))
717
    } else {
718
1802848
        Some(Domain::int(vec![Range::Bounded(current_min, current_max)]))
719
    }
720
1829355
}
721

            
722
10
fn matrix_element_domain(e: &Expression) -> Option<DomainPtr> {
723
10
    let (elem_domain, _) = e.domain_of()?.as_matrix()?;
724
10
    elem_domain.as_ref().as_int()?;
725
10
    Some(elem_domain)
726
10
}
727

            
728
// Returns none if unbounded
729
3857084
fn range_vec_bounds_i32(ranges: &Vec<Range<i32>>) -> Option<(i32, i32)> {
730
3857084
    let mut min = i32::MAX;
731
3857084
    let mut max = i32::MIN;
732
3876232
    for r in ranges {
733
3876232
        match r {
734
1385046
            Range::Single(i) => {
735
1385046
                if *i < min {
736
1365898
                    min = *i;
737
1365898
                }
738
1385046
                if *i > max {
739
1385046
                    max = *i;
740
1385046
                }
741
            }
742
2491186
            Range::Bounded(i, j) => {
743
2491186
                if *i < min {
744
2491186
                    min = *i;
745
2491186
                }
746
2491186
                if *j > max {
747
2491186
                    max = *j;
748
2491186
                }
749
            }
750
            Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => return None,
751
        }
752
    }
753
3857084
    Some((min, max))
754
3857084
}
755

            
756
impl Expression {
757
    /// Returns the possible values of the expression, recursing to leaf expressions
758
9102680
    pub fn domain_of(&self) -> Option<DomainPtr> {
759
9102680
        match self {
760
96
            Expression::Union(_, a, b) => Some(Domain::set(
761
96
                SetAttr::<IntVal>::default(),
762
96
                a.domain_of()?.union(&b.domain_of()?).ok()?,
763
            )),
764
90
            Expression::Intersect(_, a, b) => Some(Domain::set(
765
90
                SetAttr::<IntVal>::default(),
766
90
                a.domain_of()?.intersect(&b.domain_of()?).ok()?,
767
            )),
768
            Expression::In(_, _, _) => Some(Domain::bool()),
769
            Expression::Supset(_, _, _) => Some(Domain::bool()),
770
            Expression::SupsetEq(_, _, _) => Some(Domain::bool()),
771
            Expression::Subset(_, _, _) => Some(Domain::bool()),
772
6
            Expression::SubsetEq(_, _, _) => Some(Domain::bool()),
773
198020
            Expression::AbstractLiteral(_, abslit) => abslit.domain_of(),
774
            Expression::DominanceRelation(_, _) => Some(Domain::bool()),
775
            Expression::FromSolution(_, expr) => Some(expr.domain_of()),
776
144
            Expression::Metavar(_, _) => None,
777
26
            Expression::Comprehension(_, comprehension) => comprehension.domain_of(),
778
            Expression::AbstractComprehension(_, comprehension) => comprehension.domain_of(),
779
729804
            Expression::UnsafeIndex(_, matrix, index) | Expression::SafeIndex(_, matrix, index) => {
780
750482
                let dom = matrix.domain_of()?;
781
750482
                if let Some((elem_domain, _)) = dom.as_matrix() {
782
750482
                    return Some(elem_domain);
783
                }
784

            
785
                // may actually use the value in the future
786
                #[allow(clippy::redundant_pattern_matching)]
787
                if let Some(_) = dom.as_tuple() {
788
                    // TODO: We can implement proper indexing for tuples
789
                    return None;
790
                }
791

            
792
                if let Some(doms) = dom.as_variant().or(dom.as_record()) {
793
                    let index_expr = index.first()?;
794
                    return match index_expr {
795
                        Expression::Atomic(_, atom) => {
796
                            let decl = atom.clone().into_declaration();
797
                            for inner_dom in doms {
798
                                if *decl.name() == inner_dom.name {
799
                                    return Some(inner_dom.domain);
800
                                }
801
                            }
802
                            None
803
                        }
804
                        _ => None,
805
                    };
806
                }
807

            
808
                bug!("subject of an index operation should support indexing")
809
            }
810
            Expression::UnsafeSlice(_, matrix, indices)
811
480
            | Expression::SafeSlice(_, matrix, indices) => {
812
480
                let sliced_dimension = indices.iter().position(Option::is_none);
813

            
814
480
                let dom = matrix.domain_of()?;
815
480
                let Some((elem_domain, index_domains)) = dom.as_matrix() else {
816
                    bug!("subject of an index operation should be a matrix");
817
                };
818

            
819
480
                match sliced_dimension {
820
480
                    Some(dimension) => Some(Domain::matrix(
821
480
                        elem_domain,
822
480
                        vec![index_domains[dimension].clone()],
823
480
                    )),
824

            
825
                    // same as index
826
                    None => Some(elem_domain),
827
                }
828
            }
829
            Expression::InDomain(_, _, _) => Some(Domain::bool()),
830
5826319
            Expression::Atomic(_, atom) => Some(atom.domain_of()),
831
1230431
            Expression::Sum(_, e) => {
832
5848268
                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| Some(x + y))
833
            }
834
591474
            Expression::Product(_, e) => {
835
2321232
                bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| Some(x * y))
836
            }
837
24960
            Expression::Min(_, e) => bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
838
24960
                Some(if x < y { x } else { y })
839
24960
            })
840
4240
            .or_else(|| matrix_element_domain(e)),
841
16330
            Expression::Max(_, e) => bounded_i32_domain_for_matrix_literal_monotonic(e, |x, y| {
842
16320
                Some(if x > y { x } else { y })
843
16320
            })
844
3210
            .or_else(|| matrix_element_domain(e)),
845
2120
            Expression::UnsafeDiv(_, a, b) => a
846
2120
                .domain_of()?
847
2120
                .resolve()?
848
2120
                .apply_i32(
849
                    // rust integer division is truncating; however, we want to always round down,
850
                    // including for negative numbers.
851
56800
                    |x, y| {
852
56800
                        if y != 0 {
853
49720
                            Some((x as f32 / y as f32).floor() as i32)
854
                        } else {
855
7080
                            None
856
                        }
857
56800
                    },
858
2120
                    b.domain_of()?.resolve()?.as_ref(),
859
                )
860
2120
                .map(DomainPtr::from)
861
2120
                .ok(),
862
21720
            Expression::SafeDiv(_, a, b) => {
863
                // rust integer division is truncating; however, we want to always round down
864
                // including for negative numbers.
865
21720
                let domain = a
866
21720
                    .domain_of()?
867
21720
                    .resolve()?
868
21720
                    .apply_i32(
869
897680
                        |x, y| {
870
897680
                            if y != 0 {
871
780040
                                Some((x as f32 / y as f32).floor() as i32)
872
                            } else {
873
117640
                                None
874
                            }
875
897680
                        },
876
21720
                        b.domain_of()?.resolve()?.as_ref(),
877
                    )
878
                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
879

            
880
21720
                if let GroundDomain::Int(ranges) = domain {
881
21720
                    let mut ranges = ranges;
882
21720
                    ranges.push(Range::Single(0));
883
21720
                    Some(Domain::int(ranges))
884
                } else {
885
                    bug!("Domain of {self} was not integer")
886
                }
887
            }
888
800
            Expression::UnsafeMod(_, a, b) => a
889
800
                .domain_of()?
890
800
                .resolve()?
891
800
                .apply_i32(
892
12040
                    |x, y| if y != 0 { Some(x % y) } else { None },
893
800
                    b.domain_of()?.resolve()?.as_ref(),
894
                )
895
800
                .map(DomainPtr::from)
896
800
                .ok(),
897
6960
            Expression::SafeMod(_, a, b) => {
898
6960
                let domain = a
899
6960
                    .domain_of()?
900
6960
                    .resolve()?
901
6960
                    .apply_i32(
902
250320
                        |x, y| if y != 0 { Some(x % y) } else { None },
903
6960
                        b.domain_of()?.resolve()?.as_ref(),
904
                    )
905
                    .unwrap_or_else(|err| bug!("Got {err} when computing domain of {self}"));
906

            
907
6960
                if let GroundDomain::Int(ranges) = domain {
908
6960
                    let mut ranges = ranges;
909
6960
                    ranges.push(Range::Single(0));
910
6960
                    Some(Domain::int(ranges))
911
                } else {
912
                    bug!("Domain of {self} was not integer")
913
                }
914
            }
915
5350
            Expression::SafePow(_, a, b) | Expression::UnsafePow(_, a, b) => a
916
5350
                .domain_of()?
917
5350
                .resolve()?
918
5350
                .apply_i32(
919
162720
                    |x, y| {
920
162720
                        if (x != 0 || y != 0) && y >= 0 {
921
147520
                            Some(x.pow(y as u32))
922
                        } else {
923
15200
                            None
924
                        }
925
162720
                    },
926
5350
                    b.domain_of()?.resolve()?.as_ref(),
927
                )
928
5350
                .map(DomainPtr::from)
929
5350
                .ok(),
930
            Expression::Root(_, _) => None,
931
320
            Expression::Bubble(_, inner, _) => inner.domain_of(),
932
            Expression::AuxDeclaration(_, _, _) => Some(Domain::bool()),
933
31366
            Expression::And(_, _) => Some(Domain::bool()),
934
400
            Expression::Not(_, _) => Some(Domain::bool()),
935
320
            Expression::Or(_, _) => Some(Domain::bool()),
936
4228
            Expression::Imply(_, _, _) => Some(Domain::bool()),
937
            Expression::Iff(_, _, _) => Some(Domain::bool()),
938
16138
            Expression::Eq(_, _, _) => Some(Domain::bool()),
939
            Expression::Neq(_, _, _) => Some(Domain::bool()),
940
            Expression::Geq(_, _, _) => Some(Domain::bool()),
941
1720
            Expression::Leq(_, _, _) => Some(Domain::bool()),
942
82
            Expression::Gt(_, _, _) => Some(Domain::bool()),
943
2
            Expression::Lt(_, _, _) => Some(Domain::bool()),
944
            Expression::Factorial(_, _) => None, // not implemented
945
            Expression::FlatAbsEq(_, _, _) => Some(Domain::bool()),
946
80
            Expression::FlatSumGeq(_, _, _) => Some(Domain::bool()),
947
            Expression::FlatSumLeq(_, _, _) => Some(Domain::bool()),
948
            Expression::MinionDivEqUndefZero(_, _, _, _) => Some(Domain::bool()),
949
            Expression::MinionModuloEqUndefZero(_, _, _, _) => Some(Domain::bool()),
950
360
            Expression::FlatIneq(_, _, _, _) => Some(Domain::bool()),
951
804
            Expression::Flatten(_, n, m) => {
952
804
                if let Some(expr) = n {
953
                    if expr.return_type() == ReturnType::Int {
954
                        // TODO: handle flatten with depth argument
955
                        return None;
956
                    }
957
                } else {
958
                    // TODO: currently only works for matrices
959
804
                    let dom = m.domain_of()?.resolve()?;
960
800
                    let (val_dom, idx_doms) = match dom.as_ref() {
961
800
                        GroundDomain::Matrix(val, idx) => (val, idx),
962
                        _ => return None,
963
                    };
964
800
                    let num_elems = matrix::num_elements(idx_doms).ok()? as i32;
965

            
966
800
                    let new_index_domain = Domain::int(vec![Range::Bounded(1, num_elems)]);
967
800
                    return Some(Domain::matrix(
968
800
                        val_dom.clone().into(),
969
800
                        vec![new_index_domain],
970
800
                    ));
971
                }
972
                None
973
            }
974
            Expression::AllDiff(_, _) => Some(Domain::bool()),
975
            Expression::Table(_, _, _) => Some(Domain::bool()),
976
            Expression::NegativeTable(_, _, _) => Some(Domain::bool()),
977
            Expression::FlatWatchedLiteral(_, _, _) => Some(Domain::bool()),
978
            Expression::MinionReify(_, _, _) => Some(Domain::bool()),
979
4820
            Expression::MinionReifyImply(_, _, _) => Some(Domain::bool()),
980
            Expression::MinionWInIntervalSet(_, _, _) => Some(Domain::bool()),
981
            Expression::MinionWInSet(_, _, _) => Some(Domain::bool()),
982
            Expression::MinionElementOne(_, _, _, _) => Some(Domain::bool()),
983
7366
            Expression::Neg(_, x) => {
984
7366
                let dom = x.domain_of()?;
985
7366
                let mut ranges = dom.as_int()?;
986

            
987
4606
                ranges = ranges
988
4606
                    .into_iter()
989
4606
                    .map(|r| match r {
990
840
                        Range::Single(x) => Range::Single(-x),
991
3766
                        Range::Bounded(x, y) => Range::Bounded(-y, -x),
992
                        Range::UnboundedR(i) => Range::UnboundedL(-i),
993
                        Range::UnboundedL(i) => Range::UnboundedR(-i),
994
                        Range::Unbounded => Range::Unbounded,
995
4606
                    })
996
4606
                    .collect();
997

            
998
4606
                Some(Domain::int(ranges))
999
            }
375854
            Expression::Minus(_, a, b) => {
375854
                let a_resolved = a.domain_of()?.resolve()?;
375854
                let b_resolved = b.domain_of()?.resolve()?;
375852
                if matches!(a_resolved.as_ref(), GroundDomain::Int(_))
375852
                    && matches!(b_resolved.as_ref(), GroundDomain::Int(_))
                {
375852
                    a_resolved
1203030
                        .apply_i32(|x, y| Some(x - y), b_resolved.as_ref())
375852
                        .map(DomainPtr::from)
375852
                        .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()?
953360
                .apply_i32(|a, _| Some(a.abs()), a.domain_of()?.resolve()?.as_ref())
5640
                .map(DomainPtr::from)
5640
                .ok(),
            Expression::MinionPow(_, _, _, _) => Some(Domain::bool()),
4204
            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()?
                .apply_i32(|a, b| Some(a + b), b.domain_of()?.resolve()?.as_ref())
                .map(DomainPtr::from)
                .ok(),
            Expression::PairwiseProduct(_, a, b) => a
                .domain_of()?
                .resolve()?
                .apply_i32(|a, b| Some(a * b), b.domain_of()?.resolve()?.as_ref())
                .map(DomainPtr::from)
                .ok(),
16
            Expression::Defined(_, function) => {
16
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
16
                let size = Self::function_elements_size(attrs, &domain, &codomain);
16
                if let Some(size) = size {
14
                    Some(Domain::set(SetAttr::new(size), domain))
                } else {
2
                    Some(Domain::empty(ReturnType::Set(Box::new(
2
                        domain.return_type(),
2
                    ))))
                }
            }
22
            Expression::Range(_, function) => {
22
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
22
                let jectivity = attrs.resolve()?.jectivity;
22
                let size_size = attrs.resolve()?.size;
22
                let size_size = match size_size {
10
                    Range::Unbounded => Range::UnboundedR(0),
                    // If lower bound we can guarantee one mapping (unless size = 0)
4
                    Range::Single(x) => match jectivity {
2
                        JectivityAttr::Injective | JectivityAttr::Surjective => Range::Single(x),
2
                        _ => Range::Bounded(Ord::min(1, x), x),
                    },
                    // Upper bound guarantees the same upper bound
2
                    Range::UnboundedL(x) => Range::Bounded(0, x),
                    // If not bounded by 0 can guarantee min 1
4
                    Range::UnboundedR(x) => match jectivity {
                        JectivityAttr::Injective | JectivityAttr::Surjective => {
2
                            Range::UnboundedR(x)
                        }
2
                        _ => Range::UnboundedR(Ord::min(1, x)),
                    },
2
                    Range::Bounded(x, y) => Range::Bounded(Ord::min(1, x), y),
                };
                // Gets the size imposed by the partiality and jectivity attributes
22
                let partiality = attrs.resolve()?.partiality;
22
                let codomain_length = codomain.length_signed();
22
                let attr_size = match jectivity {
                    // Bijective and surjective functions must have every element in the codomain mapped to
6
                    JectivityAttr::Bijective | JectivityAttr::Surjective => match codomain_length {
6
                        Ok(co_len) => Some(Range::Single(co_len)),
                        Err(_) => None,
                    },
                    JectivityAttr::Injective => {
8
                        let domain_length = domain.length_signed();
8
                        match domain_length {
8
                            Ok(len) => match codomain_length {
8
                                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 => {
2
                                        Some(Range::Single(Ord::min(len, co_len)))
                                    }
                                    PartialityAttr::Partial => {
6
                                        Some(Range::Bounded(0, Ord::min(len, co_len)))
                                    }
                                },
                                Err(_) => None,
                            },
                            Err(_) => None,
                        }
                    }
                    JectivityAttr::None => {
8
                        let domain_length = domain.length_signed();
8
                        match domain_length {
                            // This is the general case, where we know there cannot be more codomain elements mapped to that domain elements
8
                            Ok(len) => Some(Range::Bounded(0, len)),
                            Err(_) => None,
                        }
                    }
                };
22
                let size = match attr_size {
22
                    Some(attr_size) => {
22
                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
22
                        match unsafe_range {
22
                            Ok(range) => range,
                            Err(_) => {
                                return Some(Domain::empty(ReturnType::Set(Box::new(
                                    domain.return_type(),
                                ))));
                            }
                        }
                    }
                    None => size_size,
                };
22
                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))
            }
22
            Expression::PreImage(_, function, _) => {
22
                let (attrs, domain, codomain) = function.domain_of()?.as_function()?;
22
                let size_size = attrs.resolve()?.size;
22
                let size_size = match size_size {
                    // Our only guarantee is an upper bound is the same
10
                    Range::Unbounded => Range::UnboundedR(0),
4
                    Range::Single(x) => Range::Bounded(0, x),
2
                    Range::UnboundedL(x) => Range::Bounded(0, x),
4
                    Range::UnboundedR(_) => Range::UnboundedR(0),
2
                    Range::Bounded(_, y) => Range::Bounded(0, y),
                };
22
                let jectivity = attrs.resolve()?.jectivity;
22
                let codomain_length = codomain.length_signed();
22
                let attr_size = match jectivity {
                    // When there is 1-to-1 mapping we can guarantee no more than 1 occurrence
2
                    JectivityAttr::Bijective => Some(Range::Single(1)),
8
                    JectivityAttr::Injective => match size_size {
2
                        Range::Single(x) | Range::UnboundedL(x) | Range::Bounded(x, _) => {
2
                            match codomain_length {
2
                                Ok(co_len) => {
2
                                    if x >= co_len {
                                        Some(Range::Single(1))
                                    } else {
2
                                        Some(Range::Bounded(0, 1))
                                    }
                                }
                                Err(_) => Some(Range::Bounded(0, 1)),
                            }
                        }
6
                        _ => Some(Range::Bounded(0, 1)),
                    },
                    JectivityAttr::Surjective => {
4
                        let domain_length = domain.length_signed();
4
                        match domain_length {
4
                            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
4
                                Ok(co_len) => match size_size {
2
                                    Range::Bounded(_, x)
                                    | Range::UnboundedL(x)
2
                                    | Range::Single(x) => Some(Range::Bounded(
2
                                        1,
2
                                        Ord::max(Ord::min(len, x) - co_len + 1, 0),
2
                                    )),
2
                                    _ => Some(Range::Bounded(1, Ord::max(len - co_len + 1, 0))),
                                },
                                Err(_) => Some(Range::UnboundedR(1)),
                            },
                            Err(_) => Some(Range::UnboundedR(1)),
                        }
                    }
                    JectivityAttr::None => {
8
                        let domain_length = domain.length_signed();
8
                        match domain_length {
8
                            Ok(len) => Some(Range::Bounded(0, len)),
                            Err(_) => Some(Range::UnboundedR(0)),
                        }
                    }
                };
22
                let size = match attr_size {
22
                    Some(attr_size) => {
22
                        let unsafe_range = Range::minimal(&[size_size, attr_size]);
22
                        match unsafe_range {
22
                            Ok(range) => range,
                            Err(_) => {
                                return Some(Domain::empty(ReturnType::Set(Box::new(
                                    domain.return_type(),
                                ))));
                            }
                        }
                    }
                    None => size_size,
                };
22
                Some(Domain::set(SetAttr::new(size), domain))
            }
8
            Expression::Restrict(_, function, new_domain) => {
8
                let mut domain = function.domain_of()?;
8
                let (attrs_mut, dom, codom_mut) = domain.as_function_mut()?;
                // Stops other references being mutable
8
                let attrs: &FuncAttr<IntVal> = attrs_mut;
8
                let codom: &Moo<Domain> = codom_mut;
                // Gets the minimal range between the old domain and new domain
8
                let mut new_dom = new_domain.domain_of()?;
                // If domains cannot be resolved we just stick to the restricted one
8
                if let Some(new_rng) = new_dom.as_int_ground_mut()
8
                    && let Some(old_rng) = dom.as_int_ground_mut()
                {
8
                    new_rng.append(old_rng);
8
                    if let Ok(rng) = Range::minimal(new_rng) {
8
                        let ranges = vec![rng];
8
                        new_dom = Domain::int(ranges);
8
                    }
                }
8
                let attr_size = attrs.resolve()?.size;
8
                let new_size = match new_dom.length_signed() {
                    // Combines current size attributes with length of new domain
8
                    Ok(len) => match Range::minimal(&[attr_size, Range::Bounded(0, len)]) {
6
                        Ok(size) => size,
                        Err(_) => {
                            // Means the restriction is impossible
2
                            return Some(Domain::empty(ReturnType::Function(
2
                                Box::new(new_dom.return_type()),
2
                                Box::new(codom.return_type()),
2
                            )));
                        }
                    },
                    Err(_) => attr_size,
                };
6
                let jectivity = attrs.jectivity.clone();
6
                let partiality = attrs.partiality.clone();
6
                let new_attrs = FuncAttr {
6
                    size: new_size,
6
                    jectivity,
6
                    partiality,
6
                };
6
                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]))
            }
2
            Expression::RelationProj(_, relation, projections) => {
2
                let (_, domains) = relation.domain_of()?.as_relation()?;
2
                let new_doms = domains
2
                    .iter()
2
                    .zip(projections.iter())
6
                    .filter_map(|(domain, included)| {
6
                        if included.is_none() {
                            // The domains corresponding to projections which are None remain in the relation
4
                            Some(domain.clone())
                        } else {
2
                            None
                        }
6
                    })
2
                    .collect();
2
                Some(Domain::relation(RelAttr::<IntVal>::default(), new_doms))
            }
            Expression::Apart(_, _, _) => Some(Domain::bool()),
            Expression::Together(_, _, _) => Some(Domain::bool()),
4
            Expression::Participants(_, p) => {
                // Every single element of the domain _must_ be in the set, so fixed size on that.
4
                let (attr, inner) = p.domain_of()?.as_partition()?;
4
                let len = inner.length_signed().ok()?;
4
                let p_parts = attr.resolve()?.num_parts;
4
                let p_card = attr.resolve()?.part_len;
                // if
4
                match (p_parts.low(), p_parts.high(), p_card.low(), p_card.high()) {
2
                    (Some(p), Some(q), Some(r), Some(s)) => {
2
                        let lo = p * r;
2
                        let hi = q * s;
2
                        if len < lo || len > hi {
2
                            return Some(Domain::empty(ReturnType::Set(Box::new(
2
                                inner.return_type(),
2
                            ))));
                        }
                    }
                    (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(),
                            ))));
                        }
                    }
2
                    _ => {}
                }
2
                Some(Domain::set(
2
                    SetAttr::new_size(len),
2
                    Domain::int(inner.as_int()?),
                ))
            }
2
            Expression::Party(_, _, p) => {
                // Will pick a part, so set will share same attrs
2
                let (attr, inner) = p.domain_of()?.as_partition()?;
2
                Some(Domain::set(SetAttr::new(attr.part_len), inner))
            }
4
            Expression::Parts(_, p) => {
4
                let (attr, inner) = p.domain_of()?.as_partition()?;
4
                Some(Domain::set(
4
                    SetAttr::new(attr.num_parts.clone()),
4
                    Domain::set(SetAttr::new(attr.part_len), inner),
4
                ))
            }
8
            Expression::Card(_, collection) => {
8
                let domain = collection.domain_of()?;
8
                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]))
                    }
8
                } else if let Some((attr, dom)) = domain.as_set() {
2
                    let attr_size = attr.resolve()?.size;
2
                    if let Ok(length) = dom.length_signed() {
2
                        let unsafe_range = Range::minimal(&[attr_size, Range::Bounded(0, length)]);
2
                        match unsafe_range {
2
                            Ok(range) => return Some(Domain::int(vec![range])),
                            Err(_) => return None,
                        }
                    }
                    // If the domain is not known we just need to go off of attributes
                    Some(Domain::int(vec![attr_size]))
6
                } else if let Some((attrs, dom)) = domain.as_mset() {
2
                    let attr_size = attrs.resolve()?.size;
2
                    let attr_occ_range = attrs.resolve()?.occurrence;
                    // Gets maximum value of the occurrence
2
                    let attr_occ = match attr_occ_range {
                        Range::Single(x) => Some(x),
                        Range::Unbounded | Range::UnboundedR(_) => None,
                        Range::Bounded(_, x) => Some(x),
2
                        Range::UnboundedL(x) => Some(x),
                    };
2
                    if let Some(occ) = attr_occ {
2
                        if let Ok(length) = dom.length_signed() {
2
                            let unsafe_range =
2
                                Range::minimal(&[attr_size, Range::Bounded(0, length * occ)]);
2
                            match unsafe_range {
2
                                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![attr_size]))
                        }
                    } else {
                        // If no occurrence is provided then it must have bounded size
                        Some(Domain::int(vec![attr_size]))
                    }
4
                } else if let Some((attrs, doms)) = domain.as_relation() {
                    // TODO: Further inference may be possible using the binary attributes
2
                    let attr_size = attrs.resolve()?.size;
                    // See if all domains are ground
2
                    let doms_sizes: Result<Vec<i32>, _> =
6
                        doms.iter().map(|x| x.length_signed()).collect();
2
                    if let Ok(doms_sizes) = doms_sizes {
2
                        let length = Range::Bounded(0, doms_sizes.iter().product());
                        // Combine the attributes and the domain possibilities
2
                        let unsafe_range = Range::minimal(&[attr_size, length]);
2
                        match unsafe_range {
2
                            Ok(range) => return Some(Domain::int(vec![range])),
                            Err(_) => return None,
                        }
                    }
                    // If the domain is not known we just need to go off of attributes
                    Some(Domain::int(vec![attr_size]))
2
                } else if let Some((attrs, dom, codom)) = domain.as_function() {
2
                    let size = Self::function_elements_size(attrs, &dom, &codom);
2
                    size.map(|size| Domain::int(vec![size]))
                } else {
                    bug!(
                        "Domain of {self} needed to be a matrix, set, mset, relation, or function for cardinality"
                    )
                }
            }
        }
9102680
    }
    // Gets the number of domain elements mapped in a function. This is the cardinality and also the defined
18
    fn function_elements_size(
18
        attrs: FuncAttr<IntVal>,
18
        domain: &DomainPtr,
18
        codomain: &DomainPtr,
18
    ) -> Option<Range> {
        // Gets the size imposed by the size attribute
        // The elements defined in the domain is the same as the size of the function itself
18
        let size_size = attrs.resolve()?.size;
        // Gets the size imposed by the partiality and jectivity attributes
18
        let partiality = attrs.resolve()?.partiality;
18
        let jectivity = attrs.resolve()?.jectivity;
18
        let domain_length = domain.length_signed();
        // We can only infer if the domain is ground and the length is known
18
        let attr_size = match domain_length {
18
            Ok(len) => match partiality {
2
                PartialityAttr::Total => Some(Range::Single(len)),
                PartialityAttr::Partial => {
                    // When partial we also need the codomain to be ground and known
16
                    let codomain_length = codomain.length_signed();
16
                    match codomain_length {
16
                        Ok(co_len) => match jectivity {
4
                            JectivityAttr::Bijective => Some(Range::Single(co_len)),
4
                            JectivityAttr::Surjective => Some(Range::Bounded(co_len, len)),
                            JectivityAttr::Injective => {
6
                                Some(Range::Bounded(0, Ord::min(len, co_len)))
                            }
2
                            JectivityAttr::None => Some(Range::Bounded(0, len)),
                        },
                        Err(_) => None,
                    }
                }
            },
            Err(_) => None,
        };
        // We combine the sizes:
        // size_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
18
        match attr_size {
18
            Some(attr_size) => {
18
                let unsafe_range = Range::minimal(&[size_size, attr_size]);
18
                unsafe_range.ok()
            }
            None => Some(size_size),
        }
18
    }
    /// 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,
            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.
5266174
    pub fn is_safe(&self) -> bool {
        // TODO: memoise in Metadata
42781776
        for expr in self.universe() {
42781776
            match expr {
                Expression::UnsafeDiv(_, _, _)
                | Expression::UnsafeMod(_, _, _)
                | Expression::UnsafePow(_, _, _)
                | Expression::UnsafeIndex(_, _, _)
                | Expression::Bubble(_, _, _)
                | Expression::UnsafeSlice(_, _, _) => {
807154
                    return false;
                }
41974622
                _ => {}
            }
        }
4459020
        true
5266174
    }
    /// True if the expression is an associative and commutative operator
14732496
    pub fn is_associative_commutative_operator(&self) -> bool {
14732496
        TryInto::<ACOperatorKind>::try_into(self).is_ok()
14732496
    }
    /// 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`.
2522436
    pub fn identical_atom_to(&self, other: &Expression) -> bool {
2522436
        let atom1: Result<&Atom, _> = self.try_into();
2522436
        let atom2: Result<&Atom, _> = other.try_into();
2522436
        if let (Ok(atom1), Ok(atom2)) = (atom1, atom2) {
562996
            atom2 == atom1
        } else {
1959440
            false
        }
2522436
    }
    /// 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.
8659214
    pub fn unwrap_list(&self) -> Option<Vec<Expression>> {
7448470
        match self {
7448470
            Expression::AbstractLiteral(_, matrix @ AbstractLiteral::Matrix(_, _)) => {
7448470
                matrix.unwrap_list().cloned()
            }
            Expression::Atomic(
                _,
28024
                Atom::Literal(Literal::AbstractLiteral(matrix @ AbstractLiteral::Matrix(_, _))),
28024
            ) => matrix.unwrap_list().map(|elems| {
23844
                elems
23844
                    .clone()
23844
                    .into_iter()
66018
                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
23844
                    .collect_vec()
23844
            }),
1182720
            _ => None,
        }
8659214
    }
    /// 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.
11899709
    pub fn unwrap_matrix_unchecked(self) -> Option<(Vec<Expression>, DomainPtr)> {
8198573
        match self {
8198573
            Expression::AbstractLiteral(_, AbstractLiteral::Matrix(elems, domain)) => {
8198573
                Some((elems, domain))
            }
            Expression::Atomic(
                _,
439672
                Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, domain))),
            ) => Some((
439672
                elems
439672
                    .into_iter()
1015212
                    .map(|x: Literal| Expression::Atomic(Metadata::new(), Atom::Literal(x)))
439672
                    .collect_vec(),
439672
                domain.into(),
            )),
3261464
            _ => None,
        }
11899709
    }
    /// For a Root expression, extends the inner vec with the given vec.
    ///
    /// # Panics
    /// Panics if the expression is not Root.
27980
    pub fn extend_root(self, exprs: Vec<Expression>) -> Expression {
27980
        match self {
27980
            Expression::Root(meta, mut children) => {
27980
                children.extend(exprs);
27980
                Expression::Root(meta, children)
            }
            _ => panic!("extend_root called on a non-Root expression"),
        }
27980
    }
    /// Converts the expression to a literal, if possible.
158262
    pub fn into_literal(self) -> Option<Literal> {
148532
        match self {
138578
            Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
            Expression::AbstractLiteral(_, abslit) => {
                Some(Literal::AbstractLiteral(abslit.into_literals()?))
            }
6302
            Expression::Neg(_, e) => {
6302
                let Literal::Int(i) = Moo::unwrap_or_clone(e).into_literal()? else {
                    bug!("negated literal should be an int");
                };
6300
                Some(Literal::Int(-i))
            }
13382
            _ => None,
        }
158262
    }
    /// If this expression is an associative-commutative operator, return its [ACOperatorKind].
15973716
    pub fn to_ac_operator_kind(&self) -> Option<ACOperatorKind> {
15973716
        TryFrom::try_from(self).ok()
15973716
    }
    /// Returns the categories of all sub-expressions of self.
91836
    pub fn universe_categories(&self) -> HashSet<Category> {
91836
        self.universe()
91836
            .into_iter()
1093382
            .map(|x| x.category_of())
91836
            .collect()
91836
    }
}
pub fn get_function_codomain(function: &Moo<Expression>) -> Option<DomainPtr> {
    let function_domain = function.domain_of()?;
    match function_domain.resolve().as_ref() {
        Some(d) => {
            match d.as_ref() {
                GroundDomain::Function(_, _, codomain) => Some(codomain.clone().into()),
                // Not defined for anything other than a function
                _ => None,
            }
        }
        None => {
            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 = ();
956470
    fn try_from(value: &Expression) -> Result<Self, Self::Error> {
956470
        let Expression::Atomic(_, atom) = value else {
740038
            return Err(());
        };
216432
        let Atom::Literal(lit) = atom else {
215772
            return Err(());
        };
660
        let Literal::Int(i) = lit else {
            return Err(());
        };
660
        Ok(*i)
956470
    }
}
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 {
71096
    fn from(i: i32) -> Self {
71096
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(i)))
71096
    }
}
impl From<bool> for Expression {
38222
    fn from(b: bool) -> Self {
38222
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(b)))
38222
    }
}
impl From<Atom> for Expression {
5494
    fn from(value: Atom) -> Self {
5494
        Expression::Atomic(Metadata::new(), value)
5494
    }
}
impl From<Literal> for Expression {
7040
    fn from(value: Literal) -> Self {
7040
        Expression::Atomic(Metadata::new(), value.into())
7040
    }
}
impl From<Moo<Expression>> for Expression {
88114
    fn from(val: Moo<Expression>) -> Self {
88114
        val.as_ref().clone()
88114
    }
}
impl CategoryOf for Expression {
1388848
    fn category_of(&self) -> Category {
        // take highest category of all the expressions children
5379038
        let category = self.cata(&move |x,children| {
5379038
            if let Some(max_category) = children.iter().max() {
                // if this expression contains subexpressions, return the maximum category of the
                // subexpressions
1660578
                *max_category
            } else {
                // this expression has no children
3718460
                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)
3718460
                if !Biplate::<Model>::universe_bi(&x).is_empty() {
                    // assume that the category is decision
                    return Category::Decision;
3718460
                }
                // if x contains atoms
3773140
                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
3718440
                && max_atom_category > max_category{
                    // update category
3718440
                    max_category = max_atom_category;
3718440
                }
                // if x contains declarationPtrs
3718460
                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
2827886
                && max_declaration_category > max_category{
                    // update category
614
                    max_category = max_declaration_category;
3717846
                }
3718460
                max_category
            }
5379038
        });
1388848
        if cfg!(debug_assertions) {
1388848
            trace!(
                category= %category,
                expression= %self,
                "Called Expression::category_of()"
            );
        };
1388848
        category
1388848
    }
}
impl Display for Expression {
73995320
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73995320
        match &self {
404
            Expression::Union(_, box1, box2) => {
404
                write!(f, "({} union {})", box1.clone(), box2.clone())
            }
3050
            Expression::In(_, e1, e2) => {
3050
                write!(f, "{e1} in {e2}")
            }
380
            Expression::Intersect(_, box1, box2) => {
380
                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())
            }
510
            Expression::SubsetEq(_, box1, box2) => {
510
                write!(f, "({} subsetEq {})", box1.clone(), box2.clone())
            }
2272850
            Expression::AbstractLiteral(_, l) => l.fmt(f),
30286
            Expression::Comprehension(_, c) => c.fmt(f),
            Expression::AbstractComprehension(_, c) => c.fmt(f),
274008
            Expression::UnsafeIndex(_, e1, e2) => write!(f, "{e1}{}", pretty_vec(e2)),
653634
            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})")
            }
209926
            Expression::Root(_, exprs) => {
209926
                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}"),
65704344
            Expression::Atomic(_, atom) => atom.fmt(f),
8376
            Expression::Abs(_, a) | Expression::Card(_, a) => write!(f, "|{a}|"),
638734
            Expression::Sum(_, e) => {
638734
                write!(f, "sum({e})")
            }
101974
            Expression::Product(_, e) => {
101974
                write!(f, "product({e})")
            }
14840
            Expression::Min(_, e) => {
14840
                write!(f, "min({e})")
            }
14964
            Expression::Max(_, e) => {
14964
                write!(f, "max({e})")
            }
44670
            Expression::Not(_, expr_box) => {
44670
                write!(f, "!({})", expr_box.clone())
            }
258256
            Expression::Or(_, e) => {
258256
                write!(f, "or({e})")
            }
258992
            Expression::And(_, e) => {
258992
                write!(f, "and({e})")
            }
45876
            Expression::Imply(_, box1, box2) => {
45876
                write!(f, "({box1}) -> ({box2})")
            }
1680
            Expression::Iff(_, box1, box2) => {
1680
                write!(f, "({box1}) <-> ({box2})")
            }
357166
            Expression::Eq(_, box1, box2) => {
357166
                write!(f, "({} = {})", box1.clone(), box2.clone())
            }
464106
            Expression::Neq(_, box1, box2) => {
464106
                write!(f, "({} != {})", box1.clone(), box2.clone())
            }
144964
            Expression::Geq(_, box1, box2) => {
144964
                write!(f, "({} >= {})", box1.clone(), box2.clone())
            }
325564
            Expression::Leq(_, box1, box2) => {
325564
                write!(f, "({} <= {})", box1.clone(), box2.clone())
            }
8004
            Expression::Gt(_, box1, box2) => {
8004
                write!(f, "({} > {})", box1.clone(), box2.clone())
            }
45924
            Expression::Lt(_, box1, box2) => {
45924
                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})")
            }
48
            Expression::Participants(_, partition) => {
48
                write!(f, "participants({partition})")
            }
44
            Expression::Party(_, element, partition) => {
44
                write!(f, "party({element}, {partition})")
            }
48
            Expression::Parts(_, partition) => {
48
                write!(f, "parts({partition})")
            }
162148
            Expression::FlatSumGeq(_, box1, box2) => {
162148
                write!(f, "SumGeq({}, {})", pretty_vec(box1), box2.clone())
            }
156908
            Expression::FlatSumLeq(_, box1, box2) => {
156908
                write!(f, "SumLeq({}, {})", pretty_vec(box1), box2.clone())
            }
60572
            Expression::FlatIneq(_, box1, box2, box3) => write!(
60572
                f,
                "Ineq({}, {}, {})",
60572
                box1.clone(),
60572
                box2.clone(),
60572
                box3.clone()
            ),
1994
            Expression::Flatten(_, n, m) => {
1994
                if let Some(n) = n {
                    write!(f, "flatten({n}, {m})")
                } else {
1994
                    write!(f, "flatten({m})")
                }
            }
14876
            Expression::AllDiff(_, e) => {
14876
                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})")
            }
13784
            Expression::Bubble(_, box1, box2) => {
13784
                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())
            }
26584
            Expression::UnsafePow(_, box1, box2) => {
26584
                write!(f, "({} ** {})", box1.clone(), box2.clone())
            }
284134
            Expression::SafePow(_, box1, box2) => {
284134
                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()
                )
            }
4344
            Expression::FlatWatchedLiteral(_, x, l) => {
4344
                write!(f, "WatchedLiteral({x},{l})")
            }
67360
            Expression::MinionReify(_, box1, box2) => {
67360
                write!(f, "Reify({}, {})", box1.clone(), box2.clone())
            }
39920
            Expression::MinionReifyImply(_, box1, box2) => {
39920
                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}])")
            }
90370
            Expression::AuxDeclaration(_, reference, e) => {
90370
                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())
            }
49716
            Expression::Neg(_, a) => {
49716
                write!(f, "-({})", a.clone())
            }
            Expression::Factorial(_, a) => {
                write!(f, "({})!", a.clone())
            }
195146
            Expression::Minus(_, a, b) => {
195146
                write!(f, "({} - {})", a.clone(), b.clone())
            }
10164
            Expression::FlatAllDiff(_, es) => {
10164
                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()
                )
            }
9256
            Expression::MinionPow(_, atom, atom1, atom2) => {
9256
                write!(f, "MinionPow({atom},{atom1},{atom2})")
            }
94576
            Expression::MinionElementOne(_, atoms, atom, atom1) => {
94576
                let atoms = atoms.iter().join(",");
94576
                write!(f, "__minion_element_one([{atoms}],{atom},{atom1})")
            }
1146
            Expression::ToInt(_, expr) => {
1146
                write!(f, "toInt({expr})")
            }
671040
            Expression::SATInt(_, encoding, bits, (min, max)) => {
671040
                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})"),
72
            Expression::Defined(_, function) => write!(f, "defined({function})"),
84
            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})"),
84
            Expression::PreImage(_, function, elems) => write!(f, "preImage({function},{elems})"),
40
            Expression::Inverse(_, a, b) => write!(f, "inverse({a},{b})"),
56
            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})"),
44
            Expression::RelationProj(_, relation, projections) => {
44
                let projections_str = projections
44
                    .iter()
132
                    .map(|x| {
132
                        if let Some(x) = x {
44
                            x.to_string()
                        } else {
88
                            String::from("_")
                        }
132
                    })
44
                    .join(", ");
44
                write!(f, "{relation}({projections_str})")
            }
        }
73995320
    }
}
3200
fn minus_operand_return_type(expr: &Expression) -> ReturnType {
1934
    match expr {
1134
        Expression::Atomic(_, Atom::Reference(reference)) => {
1134
            let decl_kind = reference.ptr.kind().clone();
1134
            match decl_kind {
442
                DeclarationKind::Find(var) => var.return_type(),
                DeclarationKind::Given(domain)
                | DeclarationKind::DomainLetting(domain)
                | DeclarationKind::Field(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
412
                | DeclarationKind::ValueLetting(inner, _) => inner.return_type(),
            }
        }
2066
        _ => expr.return_type(),
    }
3200
}
impl Typeable for Expression {
2222638
    fn return_type(&self) -> ReturnType {
2222638
        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,
133590
            Expression::AbstractLiteral(_, lit) => lit.return_type(),
229078
            Expression::UnsafeIndex(_, subject, idx) | Expression::SafeIndex(_, subject, idx) => {
301998
                let subject_ty = subject.return_type();
301998
                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
292158
                        let mut elem_typ = subject_ty;
292158
                        let mut idx_len = idx.len();
584630
                        while idx_len > 0
342946
                            && let ReturnType::Matrix(new_elem_typ) = &elem_typ
292472
                        {
292472
                            elem_typ = *new_elem_typ.clone();
292472
                            idx_len -= 1;
292472
                        }
292158
                        elem_typ
                    }
                    // TODO: We can implement indexing for these eventually
                    ReturnType::Record(_) | ReturnType::Tuple(_) | ReturnType::Variant(_) => {
9840
                        ReturnType::Unknown
                    }
                    _ => bug!(
                        "Invalid indexing operation: expected the operand to be a collection, got {self}: {subject_ty}"
                    ),
                }
            }
160
            Expression::UnsafeSlice(_, subject, _) | Expression::SafeSlice(_, subject, _) => {
160
                ReturnType::Matrix(Box::new(subject.return_type()))
            }
            Expression::InDomain(_, _, _) => ReturnType::Bool,
4
            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,
1575452
            Expression::Atomic(_, atom) => atom.return_type(),
1120
            Expression::Abs(_, _) => ReturnType::Int,
105244
            Expression::Sum(_, _) => ReturnType::Int,
10762
            Expression::Product(_, _) => ReturnType::Int,
1920
            Expression::Min(_, _) => ReturnType::Int,
1870
            Expression::Max(_, _) => ReturnType::Int,
160
            Expression::Not(_, _) => ReturnType::Bool,
414
            Expression::Or(_, _) => ReturnType::Bool,
1292
            Expression::Imply(_, _, _) => ReturnType::Bool,
            Expression::Iff(_, _, _) => ReturnType::Bool,
6518
            Expression::And(_, _) => ReturnType::Bool,
8662
            Expression::Eq(_, _, _) => ReturnType::Bool,
1080
            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,
11160
            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,
1920
            Expression::Flatten(_, _, matrix) => {
1920
                let matrix_type = matrix.return_type();
1920
                match matrix_type {
                    ReturnType::Matrix(_) => {
                        // unwrap until we get to innermost element
1920
                        let mut elem_type = matrix_type;
3840
                        while let ReturnType::Matrix(new_elem_type) = &elem_type {
1920
                            elem_type = *new_elem_type.clone();
1920
                        }
1920
                        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,
2882
            Expression::Neg(_, _) => ReturnType::Int,
            Expression::Factorial(_, _) => ReturnType::Int,
986
            Expression::UnsafePow(_, _, _) => ReturnType::Int,
2784
            Expression::SafePow(_, _, _) => ReturnType::Int,
1600
            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
1600
                let a_type = minus_operand_return_type(a);
1600
                let b_type = minus_operand_return_type(b);
1600
                if a_type == ReturnType::Int && b_type == ReturnType::Int {
1600
                    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,
1340
            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,
        }
2222638
    }
}
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) => {
                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::Active(_, 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::Active(_, 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> + 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
    }
}