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

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

            
22
pub type DomainPtr = Moo<Domain>;
23

            
24
impl DomainPtr {
25
67026311
    pub fn resolve(&self) -> Result<Moo<GroundDomain>, DomainOpError> {
26
67026311
        self.as_ref().resolve()
27
67026311
    }
28

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

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

            
42
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
43
#[biplate(to=DomainPtr)]
44
#[biplate(to=GroundDomain)]
45
#[biplate(to=UnresolvedDomain)]
46
#[biplate(to=Expression)]
47
#[biplate(to=Reference)]
48
#[biplate(to=IntVal)]
49
#[path_prefix(conjure_cp::ast)]
50
pub enum Domain {
51
    /// A fully resolved domain
52
    Ground(Moo<GroundDomain>),
53
    /// A domain which may contain references
54
    Unresolved(Moo<UnresolvedDomain>),
55
}
56

            
57
/// Types that have a [`Domain`].
58
pub trait HasDomain {
59
    /// Gets the [`Domain`] of `self`.
60
    fn domain_of(&self) -> DomainPtr;
61
}
62

            
63
impl<T: HasDomain> Typeable for T {
64
1646084
    fn return_type(&self) -> ReturnType {
65
1646084
        self.domain_of().return_type()
66
1646084
    }
67
}
68

            
69
// Domain::Bool is completely static, so reuse the same chunk of memory
70
// for all bool domains to avoid many small memory allocations
71
thread_local! {
72
    static BOOL_DOMAIN: DomainPtr =
73
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
74
}
75

            
76
impl Domain {
77
    /// Create a new boolean domain and return a pointer to it.
78
    /// Boolean domains are always ground (see [GroundDomain::Bool]).
79
3824279
    pub fn bool() -> DomainPtr {
80
3824279
        BOOL_DOMAIN.with(Clone::clone)
81
3824279
    }
82

            
83
    /// Create a new empty domain of the given type and return a pointer to it.
84
    /// Empty domains are always ground (see [GroundDomain::Empty]).
85
13
    pub fn empty(ty: ReturnType) -> DomainPtr {
86
13
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Empty(ty))))
87
13
    }
88

            
89
    /// Create a new int domain with the given ranges.
90
    /// If the ranges are all ground, the variant will be [GroundDomain::Int].
91
    /// Otherwise, it will be [UnresolvedDomain::Int].
92
4613216
    pub fn int<T>(ranges: Vec<T>) -> DomainPtr
93
4613216
    where
94
4613216
        T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
95
    {
96
4613216
        if let Ok(int_rngs) = ranges
97
4613216
            .iter()
98
4613216
            .cloned()
99
4613216
            .map(TryInto::try_into)
100
4613216
            .collect::<Result<Vec<_>, _>>()
101
        {
102
4607348
            return Domain::int_ground(int_rngs);
103
5868
        }
104
5868
        let unresolved_rngs: Vec<Range<IntVal>> = ranges.into_iter().map(Into::into).collect();
105
5868
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Int(
106
5868
            unresolved_rngs,
107
5868
        ))))
108
4613216
    }
109

            
110
    /// Create a new ground integer domain with the given ranges
111
4627516
    pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
112
4627516
        let rngs = Range::squeeze(&ranges);
113
4627516
        Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
114
4627516
    }
115

            
116
    /// Create a new set domain with the given element domain and attributes.
117
    /// If the element domain and the attributes are ground, the variant
118
    /// will be [GroundDomain::Set]. Otherwise, it will be [UnresolvedDomain::Set].
119
9540
    pub fn set<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
120
9540
    where
121
9540
        T: Into<SetAttr<IntVal>> + TryInto<SetAttr<Int>> + Clone,
122
    {
123
9540
        if let Domain::Ground(gd) = inner_dom.as_ref()
124
9540
            && let Ok(int_attr) = attr.clone().try_into()
125
        {
126
9540
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Set(
127
9540
                int_attr,
128
9540
                gd.clone(),
129
9540
            ))));
130
        }
131
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Set(
132
            attr.into(),
133
            inner_dom,
134
        ))))
135
9540
    }
136

            
137
    /// Create a new multiset domain with the given element domain and attributes
138
524
    pub fn mset<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
139
524
    where
140
524
        T: Into<MSetAttr<IntVal>> + TryInto<MSetAttr<Int>> + Clone,
141
    {
142
524
        if let Domain::Ground(gd) = inner_dom.as_ref()
143
524
            && let Ok(int_attr) = attr.clone().try_into()
144
        {
145
524
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::MSet(
146
524
                int_attr,
147
524
                gd.clone(),
148
524
            ))));
149
        }
150
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(
151
            attr.into(),
152
            inner_dom,
153
        ))))
154
524
    }
155

            
156
    /// Create a new matrix domain with the given element domain and index domains.
157
    /// If the given domains are all ground, the variant will be [GroundDomain::Matrix].
158
    /// Otherwise, it will be [UnresolvedDomain::Matrix].
159
242628
    pub fn matrix(inner_dom: DomainPtr, idx_doms: Vec<DomainPtr>) -> DomainPtr {
160
242628
        if let Domain::Ground(gd) = inner_dom.as_ref()
161
242056
            && let Some(idx_gds) = as_grounds(&idx_doms)
162
        {
163
241044
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Matrix(
164
241044
                gd.clone(),
165
241044
                idx_gds,
166
241044
            ))));
167
1584
        }
168
1584
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(
169
1584
            inner_dom, idx_doms,
170
1584
        ))))
171
242628
    }
172

            
173
    /// Create a new tuple domain with the given element domains.
174
    /// If the given domains are all ground, the variant will be [GroundDomain::Tuple].
175
    /// Otherwise, it will be [UnresolvedDomain::Tuple].
176
881
    pub fn tuple(inner_doms: Vec<DomainPtr>) -> DomainPtr {
177
881
        if let Some(inner_gds) = as_grounds(&inner_doms) {
178
801
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Tuple(inner_gds))));
179
80
        }
180
80
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(
181
80
            inner_doms,
182
80
        ))))
183
881
    }
184

            
185
    /// Create a new tuple domain with the given entries.
186
    /// If the entries are all ground, the variant will be [GroundDomain::Record].
187
    /// Otherwise, it will be [UnresolvedDomain::Record].
188
182
    pub fn record(entries: Vec<Field<DomainPtr>>) -> DomainPtr {
189
182
        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
190
142
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Record(entries_gds))));
191
40
        }
192
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Record(
193
40
            entries,
194
40
        ))))
195
182
    }
196

            
197
    /// Create a new [UnresolvedDomain::Reference] domain from a domain letting
198
2316
    pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
199
2316
        let _ = ptr.as_domain_letting()?;
200
2296
        Some(Moo::new(Domain::Unresolved(Moo::new(
201
2296
            UnresolvedDomain::Reference(Reference::new(ptr)),
202
2296
        ))))
203
2316
    }
204

            
205
    /// Create a new multiset domain with the given element domain and attributes
206
416
    pub fn partition<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
207
416
    where
208
416
        T: Into<PartitionAttr<IntVal>> + TryInto<PartitionAttr<Int>> + Clone,
209
    {
210
416
        if let Domain::Ground(gd) = inner_dom.as_ref()
211
416
            && let Ok(int_attr) = attr.clone().try_into()
212
        {
213
416
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Partition(
214
416
                int_attr,
215
416
                gd.clone(),
216
416
            ))));
217
        }
218
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(
219
            attr.into(),
220
            inner_dom,
221
        ))))
222
416
    }
223

            
224
    /// Create a new function domain
225
808
    pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
226
808
    where
227
808
        T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
228
    {
229
808
        if let Ok(attrs_gd) = attrs.clone().try_into()
230
808
            && let Some(dom_gd) = dom.as_ground()
231
808
            && let Some(cdom_gd) = cdom.as_ground()
232
        {
233
808
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
234
808
                attrs_gd,
235
808
                Moo::new(dom_gd.clone()),
236
808
                Moo::new(cdom_gd.clone()),
237
808
            ))));
238
        }
239

            
240
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
241
            attrs.into(),
242
            dom,
243
            cdom,
244
        ))))
245
808
    }
246

            
247
    /// Create a new variant domain with the given entries.
248
    /// If the entries are all ground, the variant will be [GroundDomain::Variant].
249
    /// Otherwise, it will be [UnresolvedDomain::Variant].
250
200
    pub fn variant(entries: Vec<Field<DomainPtr>>) -> DomainPtr {
251
200
        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
252
160
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Variant(entries_gds))));
253
40
        }
254
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(
255
40
            entries,
256
40
        ))))
257
200
    }
258

            
259
    /// Create a new relation domain
260
    /// If the entries are all ground, the variant will be [GroundDomain::Relation].
261
    /// Otherwise, it will be [UnresolvedDomain::Relation].
262
496
    pub fn relation<T>(attrs: T, inner_doms: Vec<DomainPtr>) -> DomainPtr
263
496
    where
264
496
        T: Into<RelAttr<IntVal>> + TryInto<RelAttr<Int>> + Clone,
265
    {
266
496
        if let Ok(attrs_gd) = attrs.clone().try_into()
267
496
            && let Some(doms_gd) = as_grounds(&inner_doms)
268
        {
269
456
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Relation(
270
456
                attrs_gd, doms_gd,
271
456
            ))));
272
40
        }
273

            
274
40
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
275
40
            attrs.into(),
276
40
            inner_doms,
277
40
        ))))
278
496
    }
279

            
280
    /// Create a new Sequence domain
281
440
    pub fn sequence<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
282
440
    where
283
440
        T: Into<SequenceAttr<IntVal>> + TryInto<SequenceAttr<Int>> + Clone,
284
    {
285
440
        if let Domain::Ground(gd) = inner_dom.as_ref()
286
440
            && let Ok(int_attr) = attr.clone().try_into()
287
        {
288
440
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Sequence(
289
440
                int_attr,
290
440
                gd.clone(),
291
440
            ))));
292
        }
293
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
294
            attr.into(),
295
            inner_dom,
296
        ))))
297
440
    }
298

            
299
    /// If this domain is ground, return a [Moo] to the underlying [GroundDomain].
300
    /// Otherwise, try to resolve it; Return None if this is not yet possible.
301
    /// Domains which contain references to givens cannot be resolved until these
302
    /// givens are substituted for their concrete values.
303
67026311
    pub fn resolve(&self) -> Result<Moo<GroundDomain>, DomainOpError> {
304
67026311
        match self {
305
65086199
            Domain::Ground(gd) => Ok(gd.clone()),
306
1940112
            Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
307
        }
308
67026311
    }
309

            
310
    /// If this domain is already ground, return a reference to the underlying [GroundDomain].
311
    /// Otherwise, return None. This method does NOT perform any resolution.
312
    /// See also: [Domain::resolve].
313
50184228
    pub fn as_ground(&self) -> Option<&GroundDomain> {
314
50184228
        match self {
315
49860092
            Domain::Ground(gd) => Some(gd.as_ref()),
316
324136
            _ => None,
317
        }
318
50184228
    }
319

            
320
    /// If this domain is already ground, return a mutable reference to the underlying [GroundDomain].
321
    /// Otherwise, return None. This method does NOT perform any resolution.
322
32
    pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
323
32
        match self {
324
32
            Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
325
            _ => None,
326
        }
327
32
    }
328

            
329
    /// If this domain is unresolved, return a reference to the underlying [UnresolvedDomain].
330
34550720
    pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
331
34550720
        match self {
332
324136
            Domain::Unresolved(ud) => Some(ud.as_ref()),
333
34226584
            _ => None,
334
        }
335
34550720
    }
336

            
337
    /// If this domain is unresolved, return a mutable reference to the underlying [UnresolvedDomain].
338
16
    pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
339
16
        match self {
340
16
            Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
341
            _ => None,
342
        }
343
16
    }
344

            
345
    /// If this is [GroundDomain::Empty(ty)], get a reference to the return type `ty`
346
    pub fn as_dom_empty(&self) -> Option<&ReturnType> {
347
        if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
348
            return Some(ty);
349
        }
350
        None
351
    }
352

            
353
    /// If this is [GroundDomain::Empty(ty)], get a mutable reference to the return type `ty`
354
    pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
355
        if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
356
            return Some(ty);
357
        }
358
        None
359
    }
360

            
361
    /// True if this is [GroundDomain::Bool]
362
79272
    pub fn is_bool(&self) -> bool {
363
79272
        self.return_type() == ReturnType::Bool
364
79272
    }
365

            
366
    /// True if this is a [GroundDomain::Int] or an [UnresolvedDomain::Int]
367
    pub fn is_int(&self) -> bool {
368
        self.return_type() == ReturnType::Int
369
    }
370

            
371
    /// If this domain is [GroundDomain::Int] or [UnresolveDomain::Int], get
372
    /// its ranges. The ranges are cloned and upcast to Range<IntVal> if necessary.
373
37713012
    pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
374
37713012
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
375
3533668
            return Some(rngs.iter().cloned().map(|r| r.into()).collect());
376
34262872
        }
377
34262872
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
378
46584
            return Some(rngs.clone());
379
34216288
        }
380
34216288
        None
381
37713012
    }
382

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

            
396
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
397
            return Some(rngs);
398
        }
399
        None
400
    }
401

            
402
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
403
1640
    pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
404
1640
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
405
1640
            return Some(rngs);
406
        }
407
        None
408
1640
    }
409

            
410
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
411
32
    pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
412
32
        if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
413
32
            return Some(rngs);
414
        }
415
        None
416
32
    }
417

            
418
    /// If this is a matrix domain, get pointers to its element domain
419
    /// and index domains.
420
1005332
    pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
421
1005332
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
422
1320496
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
423
723268
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
424
723268
            return Some((inner_dom, idx_doms));
425
282064
        }
426
282064
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
427
30616
            return Some((inner_dom.clone(), idx_doms.clone()));
428
251448
        }
429
251448
        None
430
1005332
    }
431

            
432
    /// If this is a matrix domain, get mutable references to its element
433
    /// domain and its vector of index domains.
434
    /// The domain always becomes [UnresolvedDomain::Matrix] after this operation.
435
    pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
436
        // "upcast" the entire domain to UnresolvedDomain
437
        // See [Domain::as_dom_int_mut] for an explanation of why this is necessary
438
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
439
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
440
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
441
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
442
        }
443

            
444
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
445
            return Some((inner_dom, idx_doms));
446
        }
447
        None
448
    }
449

            
450
    /// If this is a [GroundDomain::Matrix], get immutable references to its element and index domains
451
1442
    pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
452
1442
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
453
1442
            return Some((inner_dom, idx_doms));
454
        }
455
        None
456
1442
    }
457

            
458
    /// If this is a [GroundDomain::Matrix], get mutable references to its element and index domains
459
    pub fn as_matrix_ground_mut(
460
        &mut self,
461
    ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
462
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
463
            return Some((inner_dom, idx_doms));
464
        }
465
        None
466
    }
467

            
468
    /// If this is a set domain, get its attributes and a pointer to its element domain.
469
16
    pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
470
16
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
471
4
            return Some((attr.clone().into(), inner_dom.clone().into()));
472
12
        }
473
12
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
474
            return Some((attr.clone(), inner_dom.clone()));
475
12
        }
476
12
        None
477
16
    }
478

            
479
    /// If this is a set domain, get mutable reference to its attributes and element domain.
480
    /// The domain always becomes [UnresolvedDomain::Set] after this operation.
481
    pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
482
        if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
483
            let attr: SetAttr<IntVal> = attr_gd.clone().into();
484
            let inner_dom = inner_dom_gd.clone().into();
485
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
486
        }
487

            
488
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
489
            return Some((attr, inner_dom));
490
        }
491
        None
492
    }
493

            
494
    /// If this is a [GroundDomain::Set], get immutable references to its attributes and inner domain
495
    pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
496
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
497
            return Some((attr, inner_dom));
498
        }
499
        None
500
    }
501

            
502
    /// If this is a [GroundDomain::Set], get mutable references to its attributes and inner domain
503
    pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
504
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
505
            return Some((attr, inner_dom));
506
        }
507
        None
508
    }
509

            
510
    /// If this is a mset domain, get its attributes and a pointer to its element domain.
511
12
    pub fn as_mset(&self) -> Option<(MSetAttr<IntVal>, DomainPtr)> {
512
12
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
513
4
            return Some((attr.clone().into(), inner_dom.clone().into()));
514
8
        }
515
8
        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved() {
516
            return Some((attr.clone(), inner_dom.clone()));
517
8
        }
518
8
        None
519
12
    }
520

            
521
    /// If this is a set domain, get mutable reference to its attributes and element domain.
522
    /// The domain always becomes [UnresolvedDomain::MSet] after this operation.
523
    pub fn as_mset_mut(&mut self) -> Option<(&mut MSetAttr<IntVal>, &mut DomainPtr)> {
524
        if let Some(GroundDomain::MSet(attr_gd, inner_dom_gd)) = self.as_ground() {
525
            let attr: MSetAttr<IntVal> = attr_gd.clone().into();
526
            let inner_dom = inner_dom_gd.clone().into();
527
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(attr, inner_dom)));
528
        }
529

            
530
        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved_mut() {
531
            return Some((attr, inner_dom));
532
        }
533
        None
534
    }
535

            
536
    /// If this is a [GroundDomain::MSet], get immutable references to its attributes and inner domain
537
    pub fn as_mset_ground(&self) -> Option<(&MSetAttr<Int>, &Moo<GroundDomain>)> {
538
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
539
            return Some((attr, inner_dom));
540
        }
541
        None
542
    }
543

            
544
    /// If this is a [GroundDomain::MSet], get mutable references to its attributes and inner domain
545
    pub fn as_mset_ground_mut(&mut self) -> Option<(&mut MSetAttr<Int>, &mut Moo<GroundDomain>)> {
546
        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground_mut() {
547
            return Some((attr, inner_dom));
548
        }
549
        None
550
    }
551

            
552
    /// If this is a tuple domain, get pointers to its element domains.
553
160
    pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
554
160
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
555
320
            return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
556
        }
557
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
558
            return Some(inner_doms.clone());
559
        }
560
        None
561
160
    }
562

            
563
    /// If this is a tuple domain, get a mutable reference to its vector of element domains.
564
    /// The domain always becomes [UnresolvedDomain::Tuple] after this operation.
565
    pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
566
        if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
567
            let inner_doms: Vec<DomainPtr> =
568
                inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
569
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
570
        }
571

            
572
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
573
            return Some(inner_doms);
574
        }
575
        None
576
    }
577

            
578
    /// If this is a [GroundDomain::Tuple], get immutable references to its element domains
579
    pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
580
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
581
            return Some(inner_doms);
582
        }
583
        None
584
    }
585

            
586
    /// If this is a [GroundDomain::Tuple], get mutable reference to its element domains
587
    pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
588
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
589
            return Some(inner_doms);
590
        }
591
        None
592
    }
593

            
594
    /// If this is a record domain, clone and return its entries.
595
5761
    pub fn as_record(&self) -> Option<Vec<FieldUnresolved>> {
596
5761
        if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
597
1
            return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
598
5760
        }
599
5760
        if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
600
            return Some(record_entries.clone());
601
5760
        }
602
5760
        None
603
5761
    }
604

            
605
    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
606
    pub fn as_record_ground(&self) -> Option<&Vec<FieldGround>> {
607
        if let Some(GroundDomain::Record(entries)) = self.as_ground() {
608
            return Some(entries);
609
        }
610
        None
611
    }
612

            
613
    /// If this is a record domain, get a mutable reference to its list of entries.
614
    /// The domain always becomes [UnresolvedDomain::Record] after this operation.
615
    pub fn as_record_mut(&mut self) -> Option<&mut Vec<FieldUnresolved>> {
616
        if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
617
            let entries: Vec<FieldUnresolved> =
618
                entries_gds.iter().cloned().map(|r| r.into()).collect();
619
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
620
        }
621

            
622
        if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
623
            return Some(entries_gds);
624
        }
625
        None
626
    }
627

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

            
636
    /// If this is a sequence domain, get its (attributes, domain)
637
    pub fn as_sequence(&self) -> Option<(SequenceAttr<IntVal>, Moo<Domain>)> {
638
        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
639
            return Some((attrs.clone().into(), dom.clone().into()));
640
        }
641
        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved() {
642
            return Some((attrs.clone(), dom.clone()));
643
        }
644
        None
645
    }
646

            
647
    /// If this is a function domain, convert it to unresolved and get mutable references to
648
    /// its (attrs, domain, co-domain).
649
    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
650
    pub fn as_sequence_mut(&mut self) -> Option<(&mut SequenceAttr<IntVal>, &mut Moo<Domain>)> {
651
        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
652
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
653
                attrs.clone().into(),
654
                dom.clone().into(),
655
            )));
656
        }
657

            
658
        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved_mut() {
659
            return Some((attrs, dom));
660
        }
661
        None
662
    }
663

            
664
    /// If this is a function domain, get its (attributes, domain, co-domain)
665
124
    pub fn as_function(&self) -> Option<(FuncAttr<IntVal>, Moo<Domain>, Moo<Domain>)> {
666
124
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
667
124
            return Some((
668
124
                attrs.clone().into(),
669
124
                dom.clone().into(),
670
124
                codom.clone().into(),
671
124
            ));
672
        }
673
        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved() {
674
            return Some((attrs.clone(), dom.clone(), codom.clone()));
675
        }
676
        None
677
124
    }
678

            
679
    /// If this is a function domain, convert it to unresolved and get mutable references to
680
    /// its (attrs, domain, co-domain).
681
    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
682
16
    pub fn as_function_mut(
683
16
        &mut self,
684
16
    ) -> Option<(&mut FuncAttr<IntVal>, &mut Moo<Domain>, &mut Moo<Domain>)> {
685
16
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
686
16
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
687
16
                attrs.clone().into(),
688
16
                dom.clone().into(),
689
16
                codom.clone().into(),
690
16
            )));
691
16
        }
692

            
693
16
        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved_mut() {
694
16
            return Some((attrs, dom, codom));
695
        }
696
        None
697
16
    }
698

            
699
    /// If this is a [GroundDomain::Function], get its (attrs, domain, co-domain)
700
    pub fn as_function_ground(
701
        &self,
702
    ) -> Option<(&FuncAttr, &Moo<GroundDomain>, &Moo<GroundDomain>)> {
703
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
704
            return Some((attrs, dom, codom));
705
        }
706
        None
707
    }
708

            
709
    /// If this is a [GroundDomain::Function], get mutable references to its (attrs, domain, co-domain)
710
    pub fn as_function_ground_mut(
711
        &mut self,
712
    ) -> Option<(
713
        &mut FuncAttr,
714
        &mut Moo<GroundDomain>,
715
        &mut Moo<GroundDomain>,
716
    )> {
717
        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground_mut() {
718
            return Some((attrs, dom, codom));
719
        }
720
        None
721
    }
722

            
723
    /// If this is a partition domain, get its (attributes, domain)
724
20
    pub fn as_partition(&self) -> Option<(PartitionAttr<IntVal>, Moo<Domain>)> {
725
20
        if let Some(GroundDomain::Partition(attrs, doms)) = self.as_ground() {
726
20
            return Some((attrs.clone().into(), doms.clone().into()));
727
        }
728
        if let Some(UnresolvedDomain::Partition(attrs, doms)) = self.as_unresolved() {
729
            return Some((attrs.clone(), doms.clone()));
730
        }
731
        None
732
20
    }
733

            
734
    /// If this is a partition domain, get mutable reference to its attributes and element domain.
735
    /// The domain always becomes [UnresolvedDomain::Partition] after this operation.
736
    pub fn as_partition_mut(&mut self) -> Option<(&mut PartitionAttr<IntVal>, &mut DomainPtr)> {
737
        if let Some(GroundDomain::Partition(attr_gd, inner_dom_gd)) = self.as_ground() {
738
            let attr: PartitionAttr<IntVal> = attr_gd.clone().into();
739
            let inner_dom = inner_dom_gd.clone().into();
740
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(attr, inner_dom)));
741
        }
742

            
743
        if let Some(UnresolvedDomain::Partition(attr, inner_dom)) = self.as_unresolved_mut() {
744
            return Some((attr, inner_dom));
745
        }
746
        None
747
    }
748

            
749
    /// If this is a [GroundDomain::Partition], get immutable references to its attributes and inner domain
750
    pub fn as_partition_ground(&self) -> Option<(&PartitionAttr<Int>, &Moo<GroundDomain>)> {
751
        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground() {
752
            return Some((attr, inner_dom));
753
        }
754
        None
755
    }
756

            
757
    /// If this is a [GroundDomain::Partition], get mutable references to its attributes and inner domain
758
    pub fn as_partition_ground_mut(
759
        &mut self,
760
    ) -> Option<(&mut PartitionAttr<Int>, &mut Moo<GroundDomain>)> {
761
        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground_mut() {
762
            return Some((attr, inner_dom));
763
        }
764
        None
765
    }
766

            
767
    /// If this is a variant domain, clone and return its entries.
768
    pub fn as_variant(&self) -> Option<Vec<FieldUnresolved>> {
769
        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
770
            return Some(entries.iter().cloned().map(|r| r.into()).collect());
771
        }
772
        if let Some(UnresolvedDomain::Variant(entries)) = self.as_unresolved() {
773
            return Some(entries.clone());
774
        }
775
        None
776
    }
777

            
778
    /// If this is a [GroundDomain::Variant], get a mutable reference to its entries
779
    pub fn as_variant_ground(&self) -> Option<&Vec<FieldGround>> {
780
        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
781
            return Some(entries);
782
        }
783
        None
784
    }
785

            
786
    /// If this is a variant domain, get a mutable reference to its list of entries.
787
    /// The domain always becomes [UnresolvedDomain::Variant] after this operation.
788
    pub fn as_variant_mut(&mut self) -> Option<&mut Vec<FieldUnresolved>> {
789
        if let Some(GroundDomain::Variant(entries_gds)) = self.as_ground() {
790
            let entries: Vec<FieldUnresolved> =
791
                entries_gds.iter().cloned().map(|r| r.into()).collect();
792
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(entries)));
793
        }
794

            
795
        if let Some(UnresolvedDomain::Variant(entries_gds)) = self.as_unresolved_mut() {
796
            return Some(entries_gds);
797
        }
798
        None
799
    }
800

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

            
809
    /// If this is a relation domain, get its (attributes, [domains])
810
12
    pub fn as_relation(&self) -> Option<(RelAttr<IntVal>, Vec<Moo<Domain>>)> {
811
12
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
812
            return Some((
813
8
                attrs.clone().into(),
814
24
                doms.iter().cloned().map(|d| d.into()).collect(),
815
            ));
816
4
        }
817
4
        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved() {
818
            return Some((attrs.clone(), doms.clone()));
819
4
        }
820
4
        None
821
12
    }
822

            
823
    /// If this is a relation domain, convert it to unresolved and get mutable references to
824
    /// its (attrs, [domains]).
825
    /// The domain always becomes [UnresolvedDomain::Relation] after this operation.
826
    pub fn as_relation_mut(&mut self) -> Option<(&mut RelAttr<IntVal>, &mut Vec<Moo<Domain>>)> {
827
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
828
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
829
                attrs.clone().into(),
830
                doms.iter().cloned().map(|d| d.into()).collect(),
831
            )));
832
        }
833

            
834
        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved_mut() {
835
            return Some((attrs, doms));
836
        }
837
        None
838
    }
839

            
840
    /// If this is a [GroundDomain::Relation], get its (attrs, [domains])
841
    pub fn as_relation_ground(&self) -> Option<(&RelAttr, &Vec<Moo<GroundDomain>>)> {
842
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
843
            return Some((attrs, doms));
844
        }
845
        None
846
    }
847

            
848
    /// If this is a [GroundDomain::Relation], get mutable references to its (attrs, [domains])
849
    pub fn as_relation_ground_mut(
850
        &mut self,
851
    ) -> Option<(&mut RelAttr, &mut Vec<Moo<GroundDomain>>)> {
852
        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground_mut() {
853
            return Some((attrs, doms));
854
        }
855
        None
856
    }
857

            
858
    /// Compute the intersection of two domains
859
1506360
    pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
860
1506360
        match (self, other) {
861
1506360
            (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
862
            (Domain::Unresolved(a), Domain::Unresolved(b)) => {
863
                Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
864
            }
865
            (Domain::Unresolved(u), Domain::Ground(g))
866
            | (Domain::Ground(g), Domain::Unresolved(u)) => {
867
                todo!("Union of unresolved domain {u} and ground domain {g} is not yet implemented")
868
            }
869
        }
870
1506360
    }
871

            
872
    /// Compute the intersection of two ground domains
873
100
    pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
874
100
        match (self, other) {
875
100
            (Domain::Ground(a), Domain::Ground(b)) => {
876
100
                a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
877
            }
878
            _ => Err(DomainOpError::NotGround),
879
        }
880
100
    }
881

            
882
    /// If the domain is ground, return an iterator over its values
883
40
    pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
884
40
        if let Some(gd) = self.as_ground() {
885
40
            return gd.values();
886
        }
887
        Err(DomainOpError::NotGround)
888
40
    }
889

            
890
    /// If the domain is ground, return its size bound
891
19
    pub fn length(&self) -> Result<u64, DomainOpError> {
892
19
        if let Some(gd) = self.as_ground() {
893
19
            return gd.length();
894
        }
895
        Err(DomainOpError::NotGround)
896
19
    }
897
    /// Get the size of some domain
898
    ///
899
    /// As opposed to `Domain::length`, this function returns a signed integer (`i32`) rather than unsigned.
900
    /// * `DomainOpError::NotGround` - This function only applies to `ground` domains
901
    /// * `DomainOpError::TooLarge` - Converting to an integer my not be possible if the domain is too big
902
256
    pub fn length_signed(&self) -> Result<i32, DomainOpError> {
903
256
        let gd = self.as_ground().ok_or(DomainOpError::NotGround)?;
904
256
        let len = gd.length()?;
905
256
        len.try_into().map_err(|_| DomainOpError::TooLarge)
906
256
    }
907

            
908
    /// Construct a ground domain from a slice of values
909
25738
    pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
910
25738
        GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
911
25738
    }
912

            
913
    /// Returns true if `lit` is a valid value of this domain
914
288
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
915
288
        if let Some(gd) = self.as_ground() {
916
288
            return gd.contains(lit);
917
        }
918
        Err(DomainOpError::NotGround)
919
288
    }
920

            
921
40
    pub fn element_domain(&self) -> Option<DomainPtr> {
922
40
        match self {
923
40
            Domain::Ground(gd) => gd.element_domain().map(DomainPtr::from),
924
            Domain::Unresolved(ud) => ud.element_domain(),
925
        }
926
40
    }
927
}
928

            
929
impl Typeable for Domain {
930
1806425
    fn return_type(&self) -> ReturnType {
931
1806425
        match self {
932
1698689
            Domain::Ground(dom) => dom.return_type(),
933
107736
            Domain::Unresolved(dom) => dom.return_type(),
934
        }
935
1806425
    }
936
}
937

            
938
impl Display for Domain {
939
1197468924
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
940
1197468924
        match &self {
941
1182945960
            Domain::Ground(gd) => gd.fmt(f),
942
14522964
            Domain::Unresolved(ud) => ud.fmt(f),
943
        }
944
1197468924
    }
945
}
946

            
947
243433
fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
948
243433
    doms.iter()
949
259707
        .map(|idx| match idx.as_ref() {
950
258575
            Domain::Ground(idx_gd) => Some(idx_gd.clone()),
951
1132
            _ => None,
952
259707
        })
953
243433
        .collect()
954
243433
}
955

            
956
#[cfg(test)]
957
mod tests {
958
    use super::*;
959
    use crate::ast::Name;
960
    use crate::{domain_int, range};
961

            
962
    #[test]
963
1
    fn test_negative_product() {
964
1
        let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
965
1
        let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
966
1
        let res = d1
967
1
            .as_ground()
968
1
            .unwrap()
969
16
            .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
970
1
            .unwrap();
971

            
972
1
        assert!(matches!(res, GroundDomain::Int(_)));
973
1
        if let GroundDomain::Int(ranges) = res {
974
1
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
975
        }
976
1
    }
977

            
978
    #[test]
979
1
    fn test_negative_div() {
980
1
        let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
981
1
        let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
982
1
        let res = d1
983
16
            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
984
1
            .unwrap();
985

            
986
1
        assert!(matches!(res, GroundDomain::Int(_)));
987
1
        if let GroundDomain::Int(ranges) = res {
988
1
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
989
        }
990
1
    }
991

            
992
    #[test]
993
1
    fn test_length_basic() {
994
1
        assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
995
1
        assert_eq!(Domain::bool().length(), Ok(2));
996
1
        assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
997
1
        assert_eq!(
998
1
            domain_int!(1..2, 5..).length(),
999
            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
            Field {
1
                name: Name::user("a"),
1
                value: domain_int!(1..3),
1
            },
1
            Field {
1
                name: Name::user("b"),
1
                value: 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
    }
}