conjure_cp_core/ast/domains/
domain.rs

1use super::attrs::SetAttr;
2use super::ground::GroundDomain;
3use super::range::Range;
4use super::unresolved::{IntVal, UnresolvedDomain};
5use crate::ast::{
6    DeclarationPtr, DomainOpError, Expression, FuncAttr, Literal, Moo, RecordEntry,
7    RecordEntryGround, Reference, ReturnType, Typeable,
8};
9use itertools::Itertools;
10use polyquine::Quine;
11use serde::{Deserialize, Serialize};
12use std::fmt::{Display, Formatter};
13use std::thread_local;
14use uniplate::Uniplate;
15
16/// The integer type used in all domain code (int ranges, set sizes, etc)
17pub type Int = i32;
18
19pub type DomainPtr = Moo<Domain>;
20
21impl 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
39impl From<Moo<GroundDomain>> for DomainPtr {
40    fn from(value: Moo<GroundDomain>) -> Self {
41        Moo::new(Domain::Ground(value))
42    }
43}
44
45impl From<Moo<UnresolvedDomain>> for DomainPtr {
46    fn from(value: Moo<UnresolvedDomain>) -> Self {
47        Moo::new(Domain::Unresolved(value))
48    }
49}
50
51impl From<GroundDomain> for DomainPtr {
52    fn from(value: GroundDomain) -> Self {
53        Moo::new(Domain::Ground(Moo::new(value)))
54    }
55}
56
57impl 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)]
72pub 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`].
80pub trait HasDomain {
81    /// Gets the [`Domain`] of `self`.
82    fn domain_of(&self) -> DomainPtr;
83}
84
85impl<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
93thread_local! {
94    static BOOL_DOMAIN: DomainPtr =
95        Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
96}
97
98impl 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    pub fn int<T>(ranges: Vec<T>) -> DomainPtr
115    where
116        T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
117    {
118        if let Ok(int_rngs) = ranges
119            .iter()
120            .cloned()
121            .map(TryInto::try_into)
122            .collect::<Result<Vec<_>, _>>()
123        {
124            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    }
131
132    /// Create a new ground integer domain with the given ranges
133    pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
134        let rngs = Range::squeeze(&ranges);
135        Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
136    }
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
579impl 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
588impl 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
597fn 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)]
607mod 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}