1
use super::attrs::SetAttr;
2
use super::ground::GroundDomain;
3
use super::range::Range;
4
use super::unresolved::{IntVal, UnresolvedDomain};
5
use crate::ast::domains::attrs::{MSetAttr, PartitionAttr};
6
use crate::ast::{
7
    DeclarationPtr, DomainOpError, Expression, FieldEntry, FieldEntryGround, FuncAttr, Literal,
8
    Moo, Reference, RelAttr, ReturnType, SequenceAttr, Typeable,
9
};
10
use itertools::Itertools;
11
use polyquine::Quine;
12
use serde::{Deserialize, Serialize};
13
use std::fmt::{Display, Formatter};
14
use std::thread_local;
15
use uniplate::Uniplate;
16

            
17
/// The integer type used in all domain code (int ranges, set sizes, etc)
18
pub type Int = i32;
19

            
20
pub type DomainPtr = Moo<Domain>;
21

            
22
impl DomainPtr {
23
65956949
    pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
24
65956949
        self.as_ref().resolve()
25
65956949
    }
26

            
27
    /// Convenience method to take [Domain::union] of the [Domain]s behind two [DomainPtr]s
28
    /// and wrap the result in a new [DomainPtr].
29
1318790
    pub fn union(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
30
1318790
        self.as_ref().union(other.as_ref()).map(DomainPtr::new)
31
1318790
    }
32

            
33
    /// Convenience method to take [Domain::intersect] of the [Domain]s behind two [DomainPtr]s
34
    /// and wrap the result in a new [DomainPtr].
35
90
    pub fn intersect(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
36
90
        self.as_ref().intersect(other.as_ref()).map(DomainPtr::new)
37
90
    }
38
}
39

            
40
impl From<Moo<GroundDomain>> for DomainPtr {
41
2538870
    fn from(value: Moo<GroundDomain>) -> Self {
42
2538870
        Moo::new(Domain::Ground(value))
43
2538870
    }
44
}
45

            
46
impl From<Moo<UnresolvedDomain>> for DomainPtr {
47
    fn from(value: Moo<UnresolvedDomain>) -> Self {
48
        Moo::new(Domain::Unresolved(value))
49
    }
50
}
51

            
52
impl From<GroundDomain> for DomainPtr {
53
1066245
    fn from(value: GroundDomain) -> Self {
54
1066245
        Moo::new(Domain::Ground(Moo::new(value)))
55
1066245
    }
56
}
57

            
58
impl From<UnresolvedDomain> for DomainPtr {
59
    fn from(value: UnresolvedDomain) -> Self {
60
        Moo::new(Domain::Unresolved(Moo::new(value)))
61
    }
62
}
63

            
64
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
65
#[biplate(to=DomainPtr)]
66
#[biplate(to=GroundDomain)]
67
#[biplate(to=UnresolvedDomain)]
68
#[biplate(to=Expression)]
69
#[biplate(to=Reference)]
70
#[biplate(to=FieldEntry)]
71
#[biplate(to=IntVal)]
72
#[path_prefix(conjure_cp::ast)]
73
pub enum Domain {
74
    /// A fully resolved domain
75
    Ground(Moo<GroundDomain>),
76
    /// A domain which may contain references
77
    Unresolved(Moo<UnresolvedDomain>),
78
}
79

            
80
/// Types that have a [`Domain`].
81
pub trait HasDomain {
82
    /// Gets the [`Domain`] of `self`.
83
    fn domain_of(&self) -> DomainPtr;
84
}
85

            
86
impl<T: HasDomain> Typeable for T {
87
1594617
    fn return_type(&self) -> ReturnType {
88
1594617
        self.domain_of().return_type()
89
1594617
    }
90
}
91

            
92
// Domain::Bool is completely static, so reuse the same chunk of memory
93
// for all bool domains to avoid many small memory allocations
94
thread_local! {
95
    static BOOL_DOMAIN: DomainPtr =
96
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
97
}
98

            
99
impl Domain {
100
    /// Create a new boolean domain and return a pointer to it.
101
    /// Boolean domains are always ground (see [GroundDomain::Bool]).
102
3840778
    pub fn bool() -> DomainPtr {
103
3840778
        BOOL_DOMAIN.with(Clone::clone)
104
3840778
    }
105

            
106
    /// Create a new empty domain of the given type and return a pointer to it.
107
    /// Empty domains are always ground (see [GroundDomain::Empty]).
108
7
    pub fn empty(ty: ReturnType) -> DomainPtr {
109
7
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Empty(ty))))
110
7
    }
111

            
112
    /// Create a new int domain with the given ranges.
113
    /// If the ranges are all ground, the variant will be [GroundDomain::Int].
114
    /// Otherwise, it will be [UnresolvedDomain::Int].
115
4416522
    pub fn int<T>(ranges: Vec<T>) -> DomainPtr
116
4416522
    where
117
4416522
        T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
118
    {
119
4416522
        if let Ok(int_rngs) = ranges
120
4416522
            .iter()
121
4416522
            .cloned()
122
4416522
            .map(TryInto::try_into)
123
4416522
            .collect::<Result<Vec<_>, _>>()
124
        {
125
4411488
            return Domain::int_ground(int_rngs);
126
5034
        }
127
5034
        let unresolved_rngs: Vec<Range<IntVal>> = ranges.into_iter().map(Into::into).collect();
128
5034
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Int(
129
5034
            unresolved_rngs,
130
5034
        ))))
131
4416522
    }
132

            
133
    /// Create a new ground integer domain with the given ranges
134
4431462
    pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
135
4431462
        let rngs = Range::squeeze(&ranges);
136
4431462
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
137
4431462
    }
138

            
139
    /// Create a new set domain with the given element domain and attributes.
140
    /// If the element domain and the attributes are ground, the variant
141
    /// will be [GroundDomain::Set]. Otherwise, it will be [UnresolvedDomain::Set].
142
9396
    pub fn set<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
143
9396
    where
144
9396
        T: Into<SetAttr<IntVal>> + TryInto<SetAttr<Int>> + Clone,
145
    {
146
9396
        if let Domain::Ground(gd) = inner_dom.as_ref()
147
9396
            && let Ok(int_attr) = attr.clone().try_into()
148
        {
149
9396
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Set(
150
9396
                int_attr,
151
9396
                gd.clone(),
152
9396
            ))));
153
        }
154
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Set(
155
            attr.into(),
156
            inner_dom,
157
        ))))
158
9396
    }
159

            
160
    /// Create a new multiset domain with the given element domain and attributes
161
522
    pub fn mset<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
162
522
    where
163
522
        T: Into<MSetAttr<IntVal>> + TryInto<MSetAttr<Int>> + Clone,
164
    {
165
522
        if let Domain::Ground(gd) = inner_dom.as_ref()
166
522
            && let Ok(int_attr) = attr.clone().try_into()
167
        {
168
522
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::MSet(
169
522
                int_attr,
170
522
                gd.clone(),
171
522
            ))));
172
        }
173
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(
174
            attr.into(),
175
            inner_dom,
176
        ))))
177
522
    }
178

            
179
    /// Create a new matrix domain with the given element domain and index domains.
180
    /// If the given domains are all ground, the variant will be [GroundDomain::Matrix].
181
    /// Otherwise, it will be [UnresolvedDomain::Matrix].
182
197986
    pub fn matrix(inner_dom: DomainPtr, idx_doms: Vec<DomainPtr>) -> DomainPtr {
183
197986
        if let Domain::Ground(gd) = inner_dom.as_ref()
184
197490
            && let Some(idx_gds) = as_grounds(&idx_doms)
185
        {
186
196564
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Matrix(
187
196564
                gd.clone(),
188
196564
                idx_gds,
189
196564
            ))));
190
1422
        }
191
1422
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(
192
1422
            inner_dom, idx_doms,
193
1422
        ))))
194
197986
    }
195

            
196
    /// Create a new tuple domain with the given element domains.
197
    /// If the given domains are all ground, the variant will be [GroundDomain::Tuple].
198
    /// Otherwise, it will be [UnresolvedDomain::Tuple].
199
881
    pub fn tuple(inner_doms: Vec<DomainPtr>) -> DomainPtr {
200
881
        if let Some(inner_gds) = as_grounds(&inner_doms) {
201
801
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Tuple(inner_gds))));
202
80
        }
203
80
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(
204
80
            inner_doms,
205
80
        ))))
206
881
    }
207

            
208
    /// Create a new tuple domain with the given entries.
209
    /// If the entries are all ground, the variant will be [GroundDomain::Record].
210
    /// Otherwise, it will be [UnresolvedDomain::Record].
211
181
    pub fn record(entries: Vec<FieldEntry>) -> DomainPtr {
212
181
        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
213
141
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Record(entries_gds))));
214
40
        }
215
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Record(
216
40
            entries,
217
40
        ))))
218
181
    }
219

            
220
    /// Create a new [UnresolvedDomain::Reference] domain from a domain letting
221
2308
    pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
222
2308
        let _ = ptr.as_domain_letting()?;
223
2288
        Some(Moo::new(Domain::Unresolved(Moo::new(
224
2288
            UnresolvedDomain::Reference(Reference::new(ptr)),
225
2288
        ))))
226
2308
    }
227

            
228
    /// Create a new multiset domain with the given element domain and attributes
229
408
    pub fn partition<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
230
408
    where
231
408
        T: Into<PartitionAttr<IntVal>> + TryInto<PartitionAttr<Int>> + Clone,
232
    {
233
408
        if let Domain::Ground(gd) = inner_dom.as_ref()
234
408
            && let Ok(int_attr) = attr.clone().try_into()
235
        {
236
408
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Partition(
237
408
                int_attr,
238
408
                gd.clone(),
239
408
            ))));
240
        }
241
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(
242
            attr.into(),
243
            inner_dom,
244
        ))))
245
408
    }
246

            
247
    /// Create a new function domain
248
724
    pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
249
724
    where
250
724
        T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
251
    {
252
724
        if let Ok(attrs_gd) = attrs.clone().try_into()
253
724
            && let Some(dom_gd) = dom.as_ground()
254
724
            && let Some(cdom_gd) = cdom.as_ground()
255
        {
256
724
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
257
724
                attrs_gd,
258
724
                Moo::new(dom_gd.clone()),
259
724
                Moo::new(cdom_gd.clone()),
260
724
            ))));
261
        }
262

            
263
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
264
            attrs.into(),
265
            dom,
266
            cdom,
267
        ))))
268
724
    }
269

            
270
    /// Create a new variant domain with the given entries.
271
    /// If the entries are all ground, the variant will be [GroundDomain::Variant].
272
    /// Otherwise, it will be [UnresolvedDomain::Variant].
273
200
    pub fn variant(entries: Vec<FieldEntry>) -> DomainPtr {
274
200
        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
275
160
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Variant(entries_gds))));
276
40
        }
277
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(
278
40
            entries,
279
40
        ))))
280
200
    }
281

            
282
    /// Create a new relation domain
283
    /// If the entries are all ground, the variant will be [GroundDomain::Relation].
284
    /// Otherwise, it will be [UnresolvedDomain::Relation].
285
488
    pub fn relation<T>(attrs: T, inner_doms: Vec<DomainPtr>) -> DomainPtr
286
488
    where
287
488
        T: Into<RelAttr<IntVal>> + TryInto<RelAttr<Int>> + Clone,
288
    {
289
488
        if let Ok(attrs_gd) = attrs.clone().try_into()
290
488
            && let Some(doms_gd) = as_grounds(&inner_doms)
291
        {
292
448
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Relation(
293
448
                attrs_gd, doms_gd,
294
448
            ))));
295
40
        }
296

            
297
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
298
40
            attrs.into(),
299
40
            inner_doms,
300
40
        ))))
301
488
    }
302

            
303
    /// Create a new Sequence domain
304
440
    pub fn sequence<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
305
440
    where
306
440
        T: Into<SequenceAttr<IntVal>> + TryInto<SequenceAttr<Int>> + Clone,
307
    {
308
440
        if let Domain::Ground(gd) = inner_dom.as_ref()
309
440
            && let Ok(int_attr) = attr.clone().try_into()
310
        {
311
440
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Sequence(
312
440
                int_attr,
313
440
                gd.clone(),
314
440
            ))));
315
        }
316
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
317
            attr.into(),
318
            inner_dom,
319
        ))))
320
440
    }
321

            
322
    /// If this domain is ground, return a [Moo] to the underlying [GroundDomain].
323
    /// Otherwise, try to resolve it; Return None if this is not yet possible.
324
    /// Domains which contain references to givens cannot be resolved until these
325
    /// givens are substituted for their concrete values.
326
65956949
    pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
327
65956949
        match self {
328
64500329
            Domain::Ground(gd) => Some(gd.clone()),
329
1456620
            Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
330
        }
331
65956949
    }
332

            
333
    /// If this domain is already ground, return a reference to the underlying [GroundDomain].
334
    /// Otherwise, return None. This method does NOT perform any resolution.
335
    /// See also: [Domain::resolve].
336
12195901
    pub fn as_ground(&self) -> Option<&GroundDomain> {
337
12195901
        match self {
338
12160587
            Domain::Ground(gd) => Some(gd.as_ref()),
339
35314
            _ => None,
340
        }
341
12195901
    }
342

            
343
    /// If this domain is already ground, return a mutable reference to the underlying [GroundDomain].
344
    /// Otherwise, return None. This method does NOT perform any resolution.
345
16
    pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
346
16
        match self {
347
16
            Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
348
            _ => None,
349
        }
350
16
    }
351

            
352
    /// If this domain is unresolved, return a reference to the underlying [UnresolvedDomain].
353
244610
    pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
354
244610
        match self {
355
35314
            Domain::Unresolved(ud) => Some(ud.as_ref()),
356
209296
            _ => None,
357
        }
358
244610
    }
359

            
360
    /// If this domain is unresolved, return a mutable reference to the underlying [UnresolvedDomain].
361
8
    pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
362
8
        match self {
363
8
            Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
364
            _ => None,
365
        }
366
8
    }
367

            
368
    /// If this is [GroundDomain::Empty(ty)], get a reference to the return type `ty`
369
    pub fn as_dom_empty(&self) -> Option<&ReturnType> {
370
        if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
371
            return Some(ty);
372
        }
373
        None
374
    }
375

            
376
    /// If this is [GroundDomain::Empty(ty)], get a mutable reference to the return type `ty`
377
    pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
378
        if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
379
            return Some(ty);
380
        }
381
        None
382
    }
383

            
384
    /// True if this is [GroundDomain::Bool]
385
79706
    pub fn is_bool(&self) -> bool {
386
79706
        self.return_type() == ReturnType::Bool
387
79706
    }
388

            
389
    /// True if this is a [GroundDomain::Int] or an [UnresolvedDomain::Int]
390
    pub fn is_int(&self) -> bool {
391
        self.return_type() == ReturnType::Int
392
    }
393

            
394
    /// If this domain is [GroundDomain::Int] or [UnresolveDomain::Int], get
395
    /// its ranges. The ranges are cloned and upcast to Range<IntVal> if necessary.
396
35312
    pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
397
35312
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
398
22416
            return Some(rngs.iter().cloned().map(|r| r.into()).collect());
399
13056
        }
400
13056
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
401
2456
            return Some(rngs.clone());
402
10600
        }
403
10600
        None
404
35312
    }
405

            
406
    /// If this is an int domain, get a mutable reference to its ranges.
407
    /// The domain always becomes [UnresolvedDomain::Int] after this operation.
408
    pub fn as_int_mut(&mut self) -> Option<&mut Vec<Range<IntVal>>> {
409
        // We're "upcasting" ground ranges (Range<Int>) to the more general
410
        // Range<IntVal>, which may contain references or expressions.
411
        // We know that for now they are still ground, but we're giving the user a mutable
412
        // reference, so they can overwrite the ranges with values that aren't ground.
413
        // So, the entire domain has to become non-ground as well.
414
        if let Some(GroundDomain::Int(rngs_gds)) = self.as_ground() {
415
            let rngs: Vec<Range<IntVal>> = rngs_gds.iter().cloned().map(|r| r.into()).collect();
416
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Int(rngs)))
417
        }
418

            
419
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
420
            return Some(rngs);
421
        }
422
        None
423
    }
424

            
425
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
426
980
    pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
427
980
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
428
980
            return Some(rngs);
429
        }
430
        None
431
980
    }
432

            
433
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
434
16
    pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
435
16
        if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
436
16
            return Some(rngs);
437
        }
438
        None
439
16
    }
440

            
441
    /// If this is a matrix domain, get pointers to its element domain
442
    /// and index domains.
443
952966
    pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
444
952966
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
445
1319098
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
446
721944
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
447
721944
            return Some((inner_dom, idx_doms));
448
231022
        }
449
231022
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
450
29718
            return Some((inner_dom.clone(), idx_doms.clone()));
451
201304
        }
452
201304
        None
453
952966
    }
454

            
455
    /// If this is a matrix domain, get mutable references to its element
456
    /// domain and its vector of index domains.
457
    /// The domain always becomes [UnresolvedDomain::Matrix] after this operation.
458
    pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
459
        // "upcast" the entire domain to UnresolvedDomain
460
        // See [Domain::as_dom_int_mut] for an explanation of why this is necessary
461
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
462
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
463
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
464
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
465
        }
466

            
467
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
468
            return Some((inner_dom, idx_doms));
469
        }
470
        None
471
    }
472

            
473
    /// If this is a [GroundDomain::Matrix], get immutable references to its element and index domains
474
1440
    pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
475
1440
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
476
1440
            return Some((inner_dom, idx_doms));
477
        }
478
        None
479
1440
    }
480

            
481
    /// If this is a [GroundDomain::Matrix], get mutable references to its element and index domains
482
    pub fn as_matrix_ground_mut(
483
        &mut self,
484
    ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
485
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
486
            return Some((inner_dom, idx_doms));
487
        }
488
        None
489
    }
490

            
491
    /// If this is a set domain, get its attributes and a pointer to its element domain.
492
8
    pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
493
8
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
494
2
            return Some((attr.clone().into(), inner_dom.clone().into()));
495
6
        }
496
6
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
497
            return Some((attr.clone(), inner_dom.clone()));
498
6
        }
499
6
        None
500
8
    }
501

            
502
    /// If this is a set domain, get mutable reference to its attributes and element domain.
503
    /// The domain always becomes [UnresolvedDomain::Set] after this operation.
504
    pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
505
        if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
506
            let attr: SetAttr<IntVal> = attr_gd.clone().into();
507
            let inner_dom = inner_dom_gd.clone().into();
508
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
509
        }
510

            
511
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
512
            return Some((attr, inner_dom));
513
        }
514
        None
515
    }
516

            
517
    /// If this is a [GroundDomain::Set], get immutable references to its attributes and inner domain
518
    pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
519
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
520
            return Some((attr, inner_dom));
521
        }
522
        None
523
    }
524

            
525
    /// If this is a [GroundDomain::Set], get mutable references to its attributes and inner domain
526
    pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
527
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
528
            return Some((attr, inner_dom));
529
        }
530
        None
531
    }
532

            
533
    /// If this is a mset domain, get its attributes and a pointer to its element domain.
534
6
    pub fn as_mset(&self) -> Option<(MSetAttr<IntVal>, DomainPtr)> {
535
6
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
536
2
            return Some((attr.clone().into(), inner_dom.clone().into()));
537
4
        }
538
4
        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved() {
539
            return Some((attr.clone(), inner_dom.clone()));
540
4
        }
541
4
        None
542
6
    }
543

            
544
    /// If this is a set domain, get mutable reference to its attributes and element domain.
545
    /// The domain always becomes [UnresolvedDomain::MSet] after this operation.
546
    pub fn as_mset_mut(&mut self) -> Option<(&mut MSetAttr<IntVal>, &mut DomainPtr)> {
547
        if let Some(GroundDomain::MSet(attr_gd, inner_dom_gd)) = self.as_ground() {
548
            let attr: MSetAttr<IntVal> = attr_gd.clone().into();
549
            let inner_dom = inner_dom_gd.clone().into();
550
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(attr, inner_dom)));
551
        }
552

            
553
        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved_mut() {
554
            return Some((attr, inner_dom));
555
        }
556
        None
557
    }
558

            
559
    /// If this is a [GroundDomain::MSet], get immutable references to its attributes and inner domain
560
    pub fn as_mset_ground(&self) -> Option<(&MSetAttr<Int>, &Moo<GroundDomain>)> {
561
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
562
            return Some((attr, inner_dom));
563
        }
564
        None
565
    }
566

            
567
    /// If this is a [GroundDomain::MSet], get mutable references to its attributes and inner domain
568
    pub fn as_mset_ground_mut(&mut self) -> Option<(&mut MSetAttr<Int>, &mut Moo<GroundDomain>)> {
569
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground_mut() {
570
            return Some((attr, inner_dom));
571
        }
572
        None
573
    }
574

            
575
    /// If this is a tuple domain, get pointers to its element domains.
576
160
    pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
577
160
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
578
320
            return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
579
        }
580
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
581
            return Some(inner_doms.clone());
582
        }
583
        None
584
160
    }
585

            
586
    /// If this is a tuple domain, get a mutable reference to its vector of element domains.
587
    /// The domain always becomes [UnresolvedDomain::Tuple] after this operation.
588
    pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
589
        if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
590
            let inner_doms: Vec<DomainPtr> =
591
                inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
592
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
593
        }
594

            
595
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
596
            return Some(inner_doms);
597
        }
598
        None
599
    }
600

            
601
    /// If this is a [GroundDomain::Tuple], get immutable references to its element domains
602
    pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
603
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
604
            return Some(inner_doms);
605
        }
606
        None
607
    }
608

            
609
    /// If this is a [GroundDomain::Tuple], get mutable reference to its element domains
610
    pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
611
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
612
            return Some(inner_doms);
613
        }
614
        None
615
    }
616

            
617
    /// If this is a record domain, clone and return its entries.
618
560
    pub fn as_record(&self) -> Option<Vec<FieldEntry>> {
619
560
        if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
620
80
            return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
621
520
        }
622
520
        if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
623
            return Some(record_entries.clone());
624
520
        }
625
520
        None
626
560
    }
627

            
628
    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
629
    pub fn as_record_ground(&self) -> Option<&Vec<FieldEntryGround>> {
630
        if let Some(GroundDomain::Record(entries)) = self.as_ground() {
631
            return Some(entries);
632
        }
633
        None
634
    }
635

            
636
    /// If this is a record domain, get a mutable reference to its list of entries.
637
    /// The domain always becomes [UnresolvedDomain::Record] after this operation.
638
    pub fn as_record_mut(&mut self) -> Option<&mut Vec<FieldEntry>> {
639
        if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
640
            let entries: Vec<FieldEntry> = entries_gds.iter().cloned().map(|r| r.into()).collect();
641
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
642
        }
643

            
644
        if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
645
            return Some(entries_gds);
646
        }
647
        None
648
    }
649

            
650
    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
651
    pub fn as_record_ground_mut(&mut self) -> Option<&mut Vec<FieldEntryGround>> {
652
        if let Some(GroundDomain::Record(entries)) = self.as_ground_mut() {
653
            return Some(entries);
654
        }
655
        None
656
    }
657

            
658
    /// If this is a sequence domain, get its (attributes, domain)
659
    pub fn as_sequence(&self) -> Option<(SequenceAttr<IntVal>, Moo<Domain>)> {
660
        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
661
            return Some((attrs.clone().into(), dom.clone().into()));
662
        }
663
        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved() {
664
            return Some((attrs.clone(), dom.clone()));
665
        }
666
        None
667
    }
668

            
669
    /// If this is a function domain, convert it to unresolved and get mutable references to
670
    /// its (attrs, domain, co-domain).
671
    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
672
    pub fn as_sequence_mut(&mut self) -> Option<(&mut SequenceAttr<IntVal>, &mut Moo<Domain>)> {
673
        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
674
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
675
                attrs.clone().into(),
676
                dom.clone().into(),
677
            )));
678
        }
679

            
680
        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved_mut() {
681
            return Some((attrs, dom));
682
        }
683
        None
684
    }
685

            
686
    /// If this is a function domain, get its (attributes, domain, co-domain)
687
62
    pub fn as_function(&self) -> Option<(FuncAttr<IntVal>, Moo<Domain>, Moo<Domain>)> {
688
62
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
689
62
            return Some((
690
62
                attrs.clone().into(),
691
62
                dom.clone().into(),
692
62
                codom.clone().into(),
693
62
            ));
694
        }
695
        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved() {
696
            return Some((attrs.clone(), dom.clone(), codom.clone()));
697
        }
698
        None
699
62
    }
700

            
701
    /// If this is a function domain, convert it to unresolved and get mutable references to
702
    /// its (attrs, domain, co-domain).
703
    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
704
8
    pub fn as_function_mut(
705
8
        &mut self,
706
8
    ) -> Option<(&mut FuncAttr<IntVal>, &mut Moo<Domain>, &mut Moo<Domain>)> {
707
8
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
708
8
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
709
8
                attrs.clone().into(),
710
8
                dom.clone().into(),
711
8
                codom.clone().into(),
712
8
            )));
713
8
        }
714

            
715
8
        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved_mut() {
716
8
            return Some((attrs, dom, codom));
717
        }
718
        None
719
8
    }
720

            
721
    /// If this is a [GroundDomain::Function], get its (attrs, domain, co-domain)
722
    pub fn as_function_ground(
723
        &self,
724
    ) -> Option<(&FuncAttr, &Moo<GroundDomain>, &Moo<GroundDomain>)> {
725
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
726
            return Some((attrs, dom, codom));
727
        }
728
        None
729
    }
730

            
731
    /// If this is a [GroundDomain::Function], get mutable references to its (attrs, domain, co-domain)
732
    pub fn as_function_ground_mut(
733
        &mut self,
734
    ) -> Option<(
735
        &mut FuncAttr,
736
        &mut Moo<GroundDomain>,
737
        &mut Moo<GroundDomain>,
738
    )> {
739
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground_mut() {
740
            return Some((attrs, dom, codom));
741
        }
742
        None
743
    }
744

            
745
    /// If this is a partition domain, get its (attributes, domain)
746
10
    pub fn as_partition(&self) -> Option<(PartitionAttr<IntVal>, Moo<Domain>)> {
747
10
        if let Some(GroundDomain::Partition(attrs, doms)) = self.as_ground() {
748
10
            return Some((attrs.clone().into(), doms.clone().into()));
749
        }
750
        if let Some(UnresolvedDomain::Partition(attrs, doms)) = self.as_unresolved() {
751
            return Some((attrs.clone(), doms.clone()));
752
        }
753
        None
754
10
    }
755

            
756
    /// If this is a partition domain, get mutable reference to its attributes and element domain.
757
    /// The domain always becomes [UnresolvedDomain::Partition] after this operation.
758
    pub fn as_partition_mut(&mut self) -> Option<(&mut PartitionAttr<IntVal>, &mut DomainPtr)> {
759
        if let Some(GroundDomain::Partition(attr_gd, inner_dom_gd)) = self.as_ground() {
760
            let attr: PartitionAttr<IntVal> = attr_gd.clone().into();
761
            let inner_dom = inner_dom_gd.clone().into();
762
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(attr, inner_dom)));
763
        }
764

            
765
        if let Some(UnresolvedDomain::Partition(attr, inner_dom)) = self.as_unresolved_mut() {
766
            return Some((attr, inner_dom));
767
        }
768
        None
769
    }
770

            
771
    /// If this is a [GroundDomain::Partition], get immutable references to its attributes and inner domain
772
    pub fn as_partition_ground(&self) -> Option<(&PartitionAttr<Int>, &Moo<GroundDomain>)> {
773
        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground() {
774
            return Some((attr, inner_dom));
775
        }
776
        None
777
    }
778

            
779
    /// If this is a [GroundDomain::Partition], get mutable references to its attributes and inner domain
780
    pub fn as_partition_ground_mut(
781
        &mut self,
782
    ) -> Option<(&mut PartitionAttr<Int>, &mut Moo<GroundDomain>)> {
783
        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground_mut() {
784
            return Some((attr, inner_dom));
785
        }
786
        None
787
    }
788

            
789
    /// If this is a variant domain, clone and return its entries.
790
    pub fn as_variant(&self) -> Option<Vec<FieldEntry>> {
791
        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
792
            return Some(entries.iter().cloned().map(|r| r.into()).collect());
793
        }
794
        if let Some(UnresolvedDomain::Variant(entries)) = self.as_unresolved() {
795
            return Some(entries.clone());
796
        }
797
        None
798
    }
799

            
800
    /// If this is a [GroundDomain::Variant], get a mutable reference to its entries
801
    pub fn as_variant_ground(&self) -> Option<&Vec<FieldEntryGround>> {
802
        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
803
            return Some(entries);
804
        }
805
        None
806
    }
807

            
808
    /// If this is a variant domain, get a mutable reference to its list of entries.
809
    /// The domain always becomes [UnresolvedDomain::Variant] after this operation.
810
    pub fn as_variant_mut(&mut self) -> Option<&mut Vec<FieldEntry>> {
811
        if let Some(GroundDomain::Variant(entries_gds)) = self.as_ground() {
812
            let entries: Vec<FieldEntry> = entries_gds.iter().cloned().map(|r| r.into()).collect();
813
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(entries)));
814
        }
815

            
816
        if let Some(UnresolvedDomain::Variant(entries_gds)) = self.as_unresolved_mut() {
817
            return Some(entries_gds);
818
        }
819
        None
820
    }
821

            
822
    /// If this is a [GroundDomain::Variant], get a mutable reference to its entries
823
    pub fn as_variant_ground_mut(&mut self) -> Option<&mut Vec<FieldEntryGround>> {
824
        if let Some(GroundDomain::Variant(entries)) = self.as_ground_mut() {
825
            return Some(entries);
826
        }
827
        None
828
    }
829

            
830
    /// If this is a relation domain, get its (attributes, [domains])
831
6
    pub fn as_relation(&self) -> Option<(RelAttr<IntVal>, Vec<Moo<Domain>>)> {
832
6
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
833
            return Some((
834
4
                attrs.clone().into(),
835
12
                doms.iter().cloned().map(|d| d.into()).collect(),
836
            ));
837
2
        }
838
2
        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved() {
839
            return Some((attrs.clone(), doms.clone()));
840
2
        }
841
2
        None
842
6
    }
843

            
844
    /// If this is a relation domain, convert it to unresolved and get mutable references to
845
    /// its (attrs, [domains]).
846
    /// The domain always becomes [UnresolvedDomain::Relation] after this operation.
847
    pub fn as_relation_mut(&mut self) -> Option<(&mut RelAttr<IntVal>, &mut Vec<Moo<Domain>>)> {
848
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
849
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
850
                attrs.clone().into(),
851
                doms.iter().cloned().map(|d| d.into()).collect(),
852
            )));
853
        }
854

            
855
        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved_mut() {
856
            return Some((attrs, doms));
857
        }
858
        None
859
    }
860

            
861
    /// If this is a [GroundDomain::Relation], get its (attrs, [domains])
862
    pub fn as_relation_ground(&self) -> Option<(&RelAttr, &Vec<Moo<GroundDomain>>)> {
863
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
864
            return Some((attrs, doms));
865
        }
866
        None
867
    }
868

            
869
    /// If this is a [GroundDomain::Relation], get mutable references to its (attrs, [domains])
870
    pub fn as_relation_ground_mut(
871
        &mut self,
872
    ) -> Option<(&mut RelAttr, &mut Vec<Moo<GroundDomain>>)> {
873
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground_mut() {
874
            return Some((attrs, doms));
875
        }
876
        None
877
    }
878

            
879
    /// Compute the intersection of two domains
880
1318790
    pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
881
1318790
        match (self, other) {
882
1318790
            (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
883
            (Domain::Unresolved(a), Domain::Unresolved(b)) => {
884
                Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
885
            }
886
            (Domain::Unresolved(u), Domain::Ground(g))
887
            | (Domain::Ground(g), Domain::Unresolved(u)) => {
888
                todo!("Union of unresolved domain {u} and ground domain {g} is not yet implemented")
889
            }
890
        }
891
1318790
    }
892

            
893
    /// Compute the intersection of two ground domains
894
90
    pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
895
90
        match (self, other) {
896
90
            (Domain::Ground(a), Domain::Ground(b)) => {
897
90
                a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
898
            }
899
            _ => Err(DomainOpError::NotGround),
900
        }
901
90
    }
902

            
903
    /// If the domain is ground, return an iterator over its values
904
40
    pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
905
40
        if let Some(gd) = self.as_ground() {
906
40
            return gd.values();
907
        }
908
        Err(DomainOpError::NotGround)
909
40
    }
910

            
911
    /// If the domain is ground, return its size bound
912
19
    pub fn length(&self) -> Result<u64, DomainOpError> {
913
19
        if let Some(gd) = self.as_ground() {
914
19
            return gd.length();
915
        }
916
        Err(DomainOpError::NotGround)
917
19
    }
918
    /// Get the size of some domain
919
    ///
920
    /// As opposed to `Domain::length`, this function returns a signed integer (`i32`) rather than unsigned.
921
    /// * `DomainOpError::NotGround` - This function only applies to `ground` domains
922
    /// * `DomainOpError::TooLarge` - Converting to an integer my not be possible if the domain is too big
923
128
    pub fn length_signed(&self) -> Result<i32, DomainOpError> {
924
128
        let gd = self.as_ground().ok_or(DomainOpError::NotGround)?;
925
128
        let len = gd.length()?;
926
128
        len.try_into().map_err(|_| DomainOpError::TooLarge)
927
128
    }
928

            
929
    /// Construct a ground domain from a slice of values
930
17218
    pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
931
17218
        GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
932
17218
    }
933

            
934
    /// Returns true if `lit` is a valid value of this domain
935
144
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
936
144
        if let Some(gd) = self.as_ground() {
937
144
            return gd.contains(lit);
938
        }
939
        Err(DomainOpError::NotGround)
940
144
    }
941

            
942
40
    pub fn element_domain(&self) -> Option<DomainPtr> {
943
40
        match self {
944
40
            Domain::Ground(gd) => gd.element_domain().map(DomainPtr::from),
945
            Domain::Unresolved(ud) => ud.element_domain(),
946
        }
947
40
    }
948
}
949

            
950
impl Typeable for Domain {
951
1739440
    fn return_type(&self) -> ReturnType {
952
1739440
        match self {
953
1652814
            Domain::Ground(dom) => dom.return_type(),
954
86626
            Domain::Unresolved(dom) => dom.return_type(),
955
        }
956
1739440
    }
957
}
958

            
959
impl Display for Domain {
960
1202620662
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
961
1202620662
        match &self {
962
1189405290
            Domain::Ground(gd) => gd.fmt(f),
963
13215372
            Domain::Unresolved(ud) => ud.fmt(f),
964
        }
965
1202620662
    }
966
}
967

            
968
198859
fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
969
198859
    doms.iter()
970
209547
        .map(|idx| match idx.as_ref() {
971
208501
            Domain::Ground(idx_gd) => Some(idx_gd.clone()),
972
1046
            _ => None,
973
209547
        })
974
198859
        .collect()
975
198859
}
976

            
977
#[cfg(test)]
978
mod tests {
979
    use super::*;
980
    use crate::ast::Name;
981
    use crate::{domain_int, range};
982

            
983
    #[test]
984
1
    fn test_negative_product() {
985
1
        let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
986
1
        let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
987
1
        let res = d1
988
1
            .as_ground()
989
1
            .unwrap()
990
16
            .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
991
1
            .unwrap();
992

            
993
1
        assert!(matches!(res, GroundDomain::Int(_)));
994
1
        if let GroundDomain::Int(ranges) = res {
995
1
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
996
        }
997
1
    }
998

            
999
    #[test]
1
    fn test_negative_div() {
1
        let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
1
        let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
1
        let res = d1
16
            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
1
            .unwrap();
1
        assert!(matches!(res, GroundDomain::Int(_)));
1
        if let GroundDomain::Int(ranges) = res {
1
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
        }
1
    }
    #[test]
1
    fn test_length_basic() {
1
        assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
1
        assert_eq!(Domain::bool().length(), Ok(2));
1
        assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
1
        assert_eq!(
1
            domain_int!(1..2, 5..).length(),
            Err(DomainOpError::Unbounded)
        );
1
    }
    #[test]
1
    fn test_length_set_basic() {
        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
1
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..3));
1
        assert_eq!(s.length(), Ok(8));
        // {{1,2}, {1,3}, {2,3}}
1
        let s = Domain::set(SetAttr::new_size(2), domain_int!(1..3));
1
        assert_eq!(s.length(), Ok(3));
        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
1
        let s = Domain::set(SetAttr::new_min_max_size(1, 2), domain_int!(1..3));
1
        assert_eq!(s.length(), Ok(6));
        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
1
        let s = Domain::set(SetAttr::new_min_size(1), domain_int!(1..3));
1
        assert_eq!(s.length(), Ok(7));
        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
1
        let s = Domain::set(SetAttr::new_max_size(2), domain_int!(1..3));
1
        assert_eq!(s.length(), Ok(7));
1
    }
    #[test]
1
    fn test_length_set_nested() {
        // {
        // ∅,                                          -- all size 0
        // {∅}, {{1}}, {{2}}, {{1, 2}},                -- all size 1
        // {∅, {1}}, {∅, {2}}, {∅, {1, 2}},            -- all size 2
        // {{1}, {2}}, {{1}, {1, 2}}, {{2}, {1, 2}}
        // }
1
        let s2 = Domain::set(
1
            SetAttr::new_max_size(2),
            // {∅, {1}, {2}, {1,2}}
1
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
        );
1
        assert_eq!(s2.length(), Ok(11));
1
    }
    #[test]
1
    fn test_length_set_unbounded_inner() {
        // leaf domain is unbounded
1
        let s2_bad = Domain::set(
1
            SetAttr::new_max_size(2),
1
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..)),
        );
1
        assert_eq!(s2_bad.length(), Err(DomainOpError::Unbounded));
1
    }
    #[test]
1
    fn test_length_set_overflow() {
1
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..20));
1
        assert!(s.length().is_ok());
        // current way of calculating the formula overflows for anything larger than this
1
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..63));
1
        assert_eq!(s.length(), Err(DomainOpError::TooLarge));
1
    }
    #[test]
1
    fn test_length_tuple() {
        // 3 ways to pick first element, 2 ways to pick second element
1
        let t = Domain::tuple(vec![domain_int!(1..3), Domain::bool()]);
1
        assert_eq!(t.length(), Ok(6));
1
    }
    #[test]
1
    fn test_length_record() {
        // 3 ways to pick rec.a, 2 ways to pick rec.b
1
        let t = Domain::record(vec![
1
            FieldEntry {
1
                name: Name::user("a"),
1
                domain: domain_int!(1..3),
1
            },
1
            FieldEntry {
1
                name: Name::user("b"),
1
                domain: Domain::bool(),
1
            },
        ]);
1
        assert_eq!(t.length(), Ok(6));
1
    }
    #[test]
1
    fn test_length_matrix_basic() {
        // 3 booleans -> [T, T, T], [T, T, F], ..., [F, F, F]
1
        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..3)]);
1
        assert_eq!(m.length(), Ok(8));
        // 2 numbers, each 1..3 -> 3*3 options
1
        let m = Domain::matrix(domain_int!(1..3), vec![domain_int!(1..2)]);
1
        assert_eq!(m.length(), Ok(9));
1
    }
    #[test]
1
    fn test_length_matrix_2d() {
        // 2x3 matrix of booleans -> (2**2)**3 = 64 options
1
        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..2), domain_int!(1..3)]);
1
        assert_eq!(m.length(), Ok(64));
1
    }
    #[test]
1
    fn test_length_matrix_of_sets() {
        // 3 sets drawn from 1..2; 4**3 = 64 total options
1
        let m = Domain::matrix(
1
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
1
            vec![domain_int!(1..3)],
        );
1
        assert_eq!(m.length(), Ok(64));
1
    }
}