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::{
6
    DeclarationPtr, DomainOpError, Expression, FuncAttr, Literal, Moo, RecordEntry,
7
    RecordEntryGround, Reference, ReturnType, Typeable,
8
};
9
use itertools::Itertools;
10
use polyquine::Quine;
11
use serde::{Deserialize, Serialize};
12
use std::fmt::{Display, Formatter};
13
use std::thread_local;
14
use uniplate::Uniplate;
15

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

            
19
pub type DomainPtr = Moo<Domain>;
20

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
200
    /// Create a new [UnresolvedDomain::Reference] domain from a domain letting
201
    pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
202
        ptr.as_domain_letting()?;
203
        Some(Moo::new(Domain::Unresolved(Moo::new(
204
            UnresolvedDomain::Reference(Reference::new(ptr)),
205
        ))))
206
    }
207

            
208
    /// Create a new function domain
209
    pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
210
    where
211
        T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
212
    {
213
        if let Ok(attrs_gd) = attrs.clone().try_into()
214
            && let Some(dom_gd) = dom.as_ground()
215
            && let Some(cdom_gd) = cdom.as_ground()
216
        {
217
            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
218
                attrs_gd,
219
                Moo::new(dom_gd.clone()),
220
                Moo::new(cdom_gd.clone()),
221
            ))));
222
        }
223

            
224
        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
225
            attrs.into(),
226
            dom,
227
            cdom,
228
        ))))
229
    }
230

            
231
    /// If this domain is ground, return a [Moo] to the underlying [GroundDomain].
232
    /// Otherwise, try to resolve it; Return None if this is not yet possible.
233
    /// Domains which contain references to givens cannot be resolved until these
234
    /// givens are substituted for their concrete values.
235
    pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
236
        match self {
237
            Domain::Ground(gd) => Some(gd.clone()),
238
            Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
239
        }
240
    }
241

            
242
    /// If this domain is already ground, return a reference to the underlying [GroundDomain].
243
    /// Otherwise, return None. This method does NOT perform any resolution.
244
    /// See also: [Domain::resolve].
245
    pub fn as_ground(&self) -> Option<&GroundDomain> {
246
        match self {
247
            Domain::Ground(gd) => Some(gd.as_ref()),
248
            _ => None,
249
        }
250
    }
251

            
252
    /// If this domain is already ground, return a mutable reference to the underlying [GroundDomain].
253
    /// Otherwise, return None. This method does NOT perform any resolution.
254
    pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
255
        match self {
256
            Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
257
            _ => None,
258
        }
259
    }
260

            
261
    /// If this domain is unresolved, return a reference to the underlying [UnresolvedDomain].
262
    pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
263
        match self {
264
            Domain::Unresolved(ud) => Some(ud.as_ref()),
265
            _ => None,
266
        }
267
    }
268

            
269
    /// If this domain is unresolved, return a mutable reference to the underlying [UnresolvedDomain].
270
    pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
271
        match self {
272
            Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
273
            _ => None,
274
        }
275
    }
276

            
277
    /// If this is [GroundDomain::Empty(ty)], get a reference to the return type `ty`
278
    pub fn as_dom_empty(&self) -> Option<&ReturnType> {
279
        if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
280
            return Some(ty);
281
        }
282
        None
283
    }
284

            
285
    /// If this is [GroundDomain::Empty(ty)], get a mutable reference to the return type `ty`
286
    pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
287
        if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
288
            return Some(ty);
289
        }
290
        None
291
    }
292

            
293
    /// True if this is [GroundDomain::Bool]
294
    pub fn is_bool(&self) -> bool {
295
        self.return_type() == ReturnType::Bool
296
    }
297

            
298
    /// True if this is a [GroundDomain::Int] or an [UnresolvedDomain::Int]
299
    pub fn is_int(&self) -> bool {
300
        self.return_type() == ReturnType::Int
301
    }
302

            
303
    /// If this domain is [GroundDomain::Int] or [UnresolveDomain::Int], get
304
    /// its ranges. The ranges are cloned and upcast to Range<IntVal> if necessary.
305
    pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
306
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
307
            return Some(rngs.iter().cloned().map(|r| r.into()).collect());
308
        }
309
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
310
            return Some(rngs.clone());
311
        }
312
        None
313
    }
314

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

            
328
        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
329
            return Some(rngs);
330
        }
331
        None
332
    }
333

            
334
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
335
    pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
336
        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
337
            return Some(rngs);
338
        }
339
        None
340
    }
341

            
342
    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
343
    pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
344
        if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
345
            return Some(rngs);
346
        }
347
        None
348
    }
349

            
350
    /// If this is a matrix domain, get pointers to its element domain
351
    /// and index domains.
352
    pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
353
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
354
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
355
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
356
            return Some((inner_dom, idx_doms));
357
        }
358
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
359
            return Some((inner_dom.clone(), idx_doms.clone()));
360
        }
361
        None
362
    }
363

            
364
    /// If this is a matrix domain, get mutable references to its element
365
    /// domain and its vector of index domains.
366
    /// The domain always becomes [UnresolvedDomain::Matrix] after this operation.
367
    pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
368
        // "upcast" the entire domain to UnresolvedDomain
369
        // See [Domain::as_dom_int_mut] for an explanation of why this is necessary
370
        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
371
            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
372
            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
373
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
374
        }
375

            
376
        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
377
            return Some((inner_dom, idx_doms));
378
        }
379
        None
380
    }
381

            
382
    /// If this is a [GroundDomain::Matrix], get immutable references to its element and index domains
383
    pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
384
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
385
            return Some((inner_dom, idx_doms));
386
        }
387
        None
388
    }
389

            
390
    /// If this is a [GroundDomain::Matrix], get mutable references to its element and index domains
391
    pub fn as_matrix_ground_mut(
392
        &mut self,
393
    ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
394
        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
395
            return Some((inner_dom, idx_doms));
396
        }
397
        None
398
    }
399

            
400
    /// If this is a set domain, get its attributes and a pointer to its element domain.
401
    pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
402
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
403
            return Some((attr.clone().into(), inner_dom.clone().into()));
404
        }
405
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
406
            return Some((attr.clone(), inner_dom.clone()));
407
        }
408
        None
409
    }
410

            
411
    /// If this is a set domain, get mutable reference to its attributes and element domain.
412
    /// The domain always becomes [UnresolvedDomain::Set] after this operation.
413
    pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
414
        if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
415
            let attr: SetAttr<IntVal> = attr_gd.clone().into();
416
            let inner_dom = inner_dom_gd.clone().into();
417
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
418
        }
419

            
420
        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
421
            return Some((attr, inner_dom));
422
        }
423
        None
424
    }
425

            
426
    /// If this is a [GroundDomain::Set], get immutable references to its attributes and inner domain
427
    pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
428
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
429
            return Some((attr, inner_dom));
430
        }
431
        None
432
    }
433

            
434
    /// If this is a [GroundDomain::Set], get mutable references to its attributes and inner domain
435
    pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
436
        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
437
            return Some((attr, inner_dom));
438
        }
439
        None
440
    }
441

            
442
    /// If this is a tuple domain, get pointers to its element domains.
443
    pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
444
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
445
            return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
446
        }
447
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
448
            return Some(inner_doms.clone());
449
        }
450
        None
451
    }
452

            
453
    /// If this is a tuple domain, get a mutable reference to its vector of element domains.
454
    /// The domain always becomes [UnresolvedDomain::Tuple] after this operation.
455
    pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
456
        if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
457
            let inner_doms: Vec<DomainPtr> =
458
                inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
459
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
460
        }
461

            
462
        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
463
            return Some(inner_doms);
464
        }
465
        None
466
    }
467

            
468
    /// If this is a [GroundDomain::Tuple], get immutable references to its element domains
469
    pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
470
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
471
            return Some(inner_doms);
472
        }
473
        None
474
    }
475

            
476
    /// If this is a [GroundDomain::Tuple], get mutable reference to its element domains
477
    pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
478
        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
479
            return Some(inner_doms);
480
        }
481
        None
482
    }
483

            
484
    /// If this is a record domain, clone and return its entries.
485
    pub fn as_record(&self) -> Option<Vec<RecordEntry>> {
486
        if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
487
            return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
488
        }
489
        if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
490
            return Some(record_entries.clone());
491
        }
492
        None
493
    }
494

            
495
    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
496
    pub fn as_record_ground(&self) -> Option<&Vec<RecordEntryGround>> {
497
        if let Some(GroundDomain::Record(entries)) = self.as_ground() {
498
            return Some(entries);
499
        }
500
        None
501
    }
502

            
503
    /// If this is a record domain, get a mutable reference to its list of entries.
504
    /// The domain always becomes [UnresolvedDomain::Record] after this operation.
505
    pub fn as_record_mut(&mut self) -> Option<&mut Vec<RecordEntry>> {
506
        if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
507
            let entries: Vec<RecordEntry> = entries_gds.iter().cloned().map(|r| r.into()).collect();
508
            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
509
        }
510

            
511
        if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
512
            return Some(entries_gds);
513
        }
514
        None
515
    }
516

            
517
    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
518
    pub fn as_record_ground_mut(&mut self) -> Option<&mut Vec<RecordEntryGround>> {
519
        if let Some(GroundDomain::Record(entries)) = self.as_ground_mut() {
520
            return Some(entries);
521
        }
522
        None
523
    }
524

            
525
    /// Compute the intersection of two domains
526
    pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
527
        match (self, other) {
528
            (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
529
            (Domain::Unresolved(a), Domain::Unresolved(b)) => {
530
                Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
531
            }
532
            (Domain::Unresolved(u), Domain::Ground(g))
533
            | (Domain::Ground(g), Domain::Unresolved(u)) => {
534
                todo!("Union of unresolved domain {u} and ground domain {g} is not yet implemented")
535
            }
536
        }
537
    }
538

            
539
    /// Compute the intersection of two ground domains
540
    pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
541
        match (self, other) {
542
            (Domain::Ground(a), Domain::Ground(b)) => {
543
                a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
544
            }
545
            _ => Err(DomainOpError::NotGround),
546
        }
547
    }
548

            
549
    /// If the domain is ground, return an iterator over its values
550
    pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
551
        if let Some(gd) = self.as_ground() {
552
            return gd.values();
553
        }
554
        Err(DomainOpError::NotGround)
555
    }
556

            
557
    /// If the domain is ground, return its size bound
558
    pub fn length(&self) -> Result<u64, DomainOpError> {
559
        if let Some(gd) = self.as_ground() {
560
            return gd.length();
561
        }
562
        Err(DomainOpError::NotGround)
563
    }
564

            
565
    /// Construct a ground domain from a slice of values
566
    pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
567
        GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
568
    }
569

            
570
    /// Returns true if `lit` is a valid value of this domain
571
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
572
        if let Some(gd) = self.as_ground() {
573
            return gd.contains(lit);
574
        }
575
        Err(DomainOpError::NotGround)
576
    }
577
}
578

            
579
impl Typeable for Domain {
580
    fn return_type(&self) -> ReturnType {
581
        match self {
582
            Domain::Ground(dom) => dom.return_type(),
583
            Domain::Unresolved(dom) => dom.return_type(),
584
        }
585
    }
586
}
587

            
588
impl Display for Domain {
589
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
590
        match &self {
591
            Domain::Ground(gd) => gd.fmt(f),
592
            Domain::Unresolved(ud) => ud.fmt(f),
593
        }
594
    }
595
}
596

            
597
fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
598
    doms.iter()
599
        .map(|idx| match idx.as_ref() {
600
            Domain::Ground(idx_gd) => Some(idx_gd.clone()),
601
            _ => None,
602
        })
603
        .collect()
604
}
605

            
606
#[cfg(test)]
607
mod tests {
608
    use super::*;
609
    use crate::ast::Name;
610
    use crate::{domain_int, range};
611

            
612
    #[test]
613
    fn test_negative_product() {
614
        let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
615
        let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
616
        let res = d1
617
            .as_ground()
618
            .unwrap()
619
            .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
620
            .unwrap();
621

            
622
        assert!(matches!(res, GroundDomain::Int(_)));
623
        if let GroundDomain::Int(ranges) = res {
624
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
625
        }
626
    }
627

            
628
    #[test]
629
    fn test_negative_div() {
630
        let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
631
        let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
632
        let res = d1
633
            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
634
            .unwrap();
635

            
636
        assert!(matches!(res, GroundDomain::Int(_)));
637
        if let GroundDomain::Int(ranges) = res {
638
            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
639
        }
640
    }
641

            
642
    #[test]
643
    fn test_length_basic() {
644
        assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
645
        assert_eq!(Domain::bool().length(), Ok(2));
646
        assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
647
        assert_eq!(
648
            domain_int!(1..2, 5..).length(),
649
            Err(DomainOpError::Unbounded)
650
        );
651
    }
652
    #[test]
653
    fn test_length_set_basic() {
654
        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
655
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..3));
656
        assert_eq!(s.length(), Ok(8));
657

            
658
        // {{1,2}, {1,3}, {2,3}}
659
        let s = Domain::set(SetAttr::new_size(2), domain_int!(1..3));
660
        assert_eq!(s.length(), Ok(3));
661

            
662
        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
663
        let s = Domain::set(SetAttr::new_min_max_size(1, 2), domain_int!(1..3));
664
        assert_eq!(s.length(), Ok(6));
665

            
666
        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
667
        let s = Domain::set(SetAttr::new_min_size(1), domain_int!(1..3));
668
        assert_eq!(s.length(), Ok(7));
669

            
670
        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
671
        let s = Domain::set(SetAttr::new_max_size(2), domain_int!(1..3));
672
        assert_eq!(s.length(), Ok(7));
673
    }
674

            
675
    #[test]
676
    fn test_length_set_nested() {
677
        // {
678
        // ∅,                                          -- all size 0
679
        // {∅}, {{1}}, {{2}}, {{1, 2}},                -- all size 1
680
        // {∅, {1}}, {∅, {2}}, {∅, {1, 2}},            -- all size 2
681
        // {{1}, {2}}, {{1}, {1, 2}}, {{2}, {1, 2}}
682
        // }
683
        let s2 = Domain::set(
684
            SetAttr::new_max_size(2),
685
            // {∅, {1}, {2}, {1,2}}
686
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
687
        );
688
        assert_eq!(s2.length(), Ok(11));
689
    }
690

            
691
    #[test]
692
    fn test_length_set_unbounded_inner() {
693
        // leaf domain is unbounded
694
        let s2_bad = Domain::set(
695
            SetAttr::new_max_size(2),
696
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..)),
697
        );
698
        assert_eq!(s2_bad.length(), Err(DomainOpError::Unbounded));
699
    }
700

            
701
    #[test]
702
    fn test_length_set_overflow() {
703
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..20));
704
        assert!(s.length().is_ok());
705

            
706
        // current way of calculating the formula overflows for anything larger than this
707
        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..63));
708
        assert_eq!(s.length(), Err(DomainOpError::TooLarge));
709
    }
710

            
711
    #[test]
712
    fn test_length_tuple() {
713
        // 3 ways to pick first element, 2 ways to pick second element
714
        let t = Domain::tuple(vec![domain_int!(1..3), Domain::bool()]);
715
        assert_eq!(t.length(), Ok(6));
716
    }
717

            
718
    #[test]
719
    fn test_length_record() {
720
        // 3 ways to pick rec.a, 2 ways to pick rec.b
721
        let t = Domain::record(vec![
722
            RecordEntry {
723
                name: Name::user("a"),
724
                domain: domain_int!(1..3),
725
            },
726
            RecordEntry {
727
                name: Name::user("b"),
728
                domain: Domain::bool(),
729
            },
730
        ]);
731
        assert_eq!(t.length(), Ok(6));
732
    }
733

            
734
    #[test]
735
    fn test_length_matrix_basic() {
736
        // 3 booleans -> [T, T, T], [T, T, F], ..., [F, F, F]
737
        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..3)]);
738
        assert_eq!(m.length(), Ok(8));
739

            
740
        // 2 numbers, each 1..3 -> 3*3 options
741
        let m = Domain::matrix(domain_int!(1..3), vec![domain_int!(1..2)]);
742
        assert_eq!(m.length(), Ok(9));
743
    }
744

            
745
    #[test]
746
    fn test_length_matrix_2d() {
747
        // 2x3 matrix of booleans -> (2**2)**3 = 64 options
748
        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..2), domain_int!(1..3)]);
749
        assert_eq!(m.length(), Ok(64));
750
    }
751

            
752
    #[test]
753
    fn test_length_matrix_of_sets() {
754
        // 3 sets drawn from 1..2; 4**3 = 64 total options
755
        let m = Domain::matrix(
756
            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
757
            vec![domain_int!(1..3)],
758
        );
759
        assert_eq!(m.length(), Ok(64));
760
    }
761
}