Skip to main content

conjure_cp_core/ast/domains/
domain.rs

1use crate::ast::domains::{
2    attrs::{MSetAttr, PartitionAttr, PermutationAttr, SetAttr},
3    ground::{FieldGround, GroundDomain},
4    int_val::IntVal,
5    range::Range,
6    unresolved::{FieldUnresolved, UnresolvedDomain},
7};
8use crate::ast::{
9    DeclarationPtr, DomainOpError, Expression, Field, FuncAttr, Literal, Moo, Reference, RelAttr,
10    ReturnType, SequenceAttr, Typeable,
11};
12use itertools::Itertools;
13use polyquine::Quine;
14use serde::{Deserialize, Serialize};
15use std::fmt::{Display, Formatter};
16use std::thread_local;
17use uniplate::Uniplate;
18
19/// The integer type used in all domain code (int ranges, set sizes, etc)
20pub type Int = i32;
21
22/// Lower bound of the default fully-bounded integer domain used by Conjure Oxide.
23///
24/// One greater than [`i32::MIN`] so the magnitude of `OXIDE_INT_MIN` is representable as a
25/// positive [`i32`].
26pub const OXIDE_INT_MIN: Int = Int::MIN + 1;
27
28/// Upper bound of the default fully-bounded integer domain used by Conjure Oxide.
29pub const OXIDE_INT_MAX: Int = Int::MAX;
30
31pub type DomainPtr = Moo<Domain>;
32
33impl DomainPtr {
34    pub fn resolve(&self) -> Result<Moo<GroundDomain>, DomainOpError> {
35        self.as_ref().resolve()
36    }
37
38    /// Convenience method to take [Domain::union] of the [Domain]s behind two [DomainPtr]s
39    /// and wrap the result in a new [DomainPtr].
40    pub fn union(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
41        self.as_ref().union(other.as_ref()).map(DomainPtr::new)
42    }
43
44    /// Convenience method to take [Domain::intersect] of the [Domain]s behind two [DomainPtr]s
45    /// and wrap the result in a new [DomainPtr].
46    pub fn intersect(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
47        self.as_ref().intersect(other.as_ref()).map(DomainPtr::new)
48    }
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
52#[biplate(to=DomainPtr)]
53#[biplate(to=GroundDomain)]
54#[biplate(to=UnresolvedDomain)]
55#[biplate(to=Expression)]
56#[biplate(to=Reference)]
57#[biplate(to=IntVal)]
58#[path_prefix(conjure_cp::ast)]
59/// Variants are ordered from fully resolved to unresolved; keep broad matches in this order.
60pub enum Domain {
61    /// A fully resolved domain
62    Ground(Moo<GroundDomain>),
63    /// A domain which may contain references
64    Unresolved(Moo<UnresolvedDomain>),
65}
66
67/// Types that have a [`Domain`].
68pub trait HasDomain {
69    /// Gets the [`Domain`] of `self`.
70    fn domain_of(&self) -> DomainPtr;
71}
72
73impl<T: HasDomain> Typeable for T {
74    fn return_type(&self) -> ReturnType {
75        self.domain_of().return_type()
76    }
77}
78
79// Domain::Bool is completely static, so reuse the same chunk of memory
80// for all bool domains to avoid many small memory allocations
81thread_local! {
82    static BOOL_DOMAIN: DomainPtr =
83        Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
84}
85
86impl Domain {
87    /// Create a new boolean domain and return a pointer to it.
88    /// Boolean domains are always ground (see [GroundDomain::Bool]).
89    pub fn bool() -> DomainPtr {
90        BOOL_DOMAIN.with(Clone::clone)
91    }
92
93    /// Create a new empty domain of the given type and return a pointer to it.
94    /// Empty domains are always ground (see [GroundDomain::Empty]).
95    pub fn empty(ty: ReturnType) -> DomainPtr {
96        Moo::new(Domain::Ground(Moo::new(GroundDomain::Empty(ty))))
97    }
98
99    /// Create a new int domain with the given ranges.
100    /// If the ranges are all ground, the variant will be [GroundDomain::Int].
101    /// Otherwise, it will be [UnresolvedDomain::Int].
102    pub fn int<T>(ranges: Vec<T>) -> DomainPtr
103    where
104        T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
105    {
106        if let Ok(int_rngs) = ranges
107            .iter()
108            .cloned()
109            .map(TryInto::try_into)
110            .collect::<Result<Vec<_>, _>>()
111        {
112            return Domain::int_ground(int_rngs);
113        }
114        let unresolved_rngs: Vec<Range<IntVal>> = ranges.into_iter().map(Into::into).collect();
115        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Int(
116            unresolved_rngs,
117        ))))
118    }
119
120    /// Creates an integer domain holding the values of a collection, as in `int([i | i <- nums])`.
121    ///
122    /// Resolves to the concrete values once the collection can be evaluated, which for a
123    /// collection built from `given`s is after instantiation.
124    pub fn int_from_values(values: Expression) -> DomainPtr {
125        // Evaluate now when the collection is already constant. Leaving it as an expression means
126        // re-evaluating it on every domain query during rewriting, which is ruinous: `domain_or_init`
127        // runs per node per rule attempt.
128        if let Some(ranges) = int_ranges_from_constant_collection(&values) {
129            return Domain::int_ground(ranges);
130        }
131
132        Moo::new(Domain::Unresolved(Moo::new(
133            UnresolvedDomain::IntFromValues(Moo::new(values)),
134        )))
135    }
136
137    /// Create a new ground integer domain with the given ranges
138    pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
139        let rngs = Range::squeeze(&ranges);
140        Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
141    }
142
143    /// Create a new set domain with the given element domain and attributes.
144    /// If the element domain and the attributes are ground, the variant
145    /// will be [GroundDomain::Set]. Otherwise, it will be [UnresolvedDomain::Set].
146    pub fn set<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
147    where
148        T: Into<SetAttr<IntVal>> + TryInto<SetAttr<Int>> + Clone,
149    {
150        if let Domain::Ground(gd) = inner_dom.as_ref()
151            && let Ok(int_attr) = attr.clone().try_into()
152        {
153            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Set(
154                int_attr,
155                gd.clone(),
156            ))));
157        }
158        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Set(
159            attr.into(),
160            inner_dom,
161        ))))
162    }
163
164    /// Create a new multiset domain with the given element domain and attributes
165    pub fn mset<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
166    where
167        T: Into<MSetAttr<IntVal>> + TryInto<MSetAttr<Int>> + Clone,
168    {
169        if let Domain::Ground(gd) = inner_dom.as_ref()
170            && let Ok(int_attr) = attr.clone().try_into()
171        {
172            return Moo::new(Domain::Ground(Moo::new(GroundDomain::MSet(
173                int_attr,
174                gd.clone(),
175            ))));
176        }
177        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(
178            attr.into(),
179            inner_dom,
180        ))))
181    }
182
183    /// Create a new matrix domain with the given element domain and index domains.
184    /// If the given domains are all ground, the variant will be [GroundDomain::Matrix].
185    /// Otherwise, it will be [UnresolvedDomain::Matrix].
186    pub fn matrix(inner_dom: DomainPtr, idx_doms: Vec<DomainPtr>) -> DomainPtr {
187        if let Domain::Ground(gd) = inner_dom.as_ref()
188            && let Some(idx_gds) = as_grounds(&idx_doms)
189        {
190            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Matrix(
191                gd.clone(),
192                idx_gds,
193            ))));
194        }
195        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(
196            inner_dom, idx_doms,
197        ))))
198    }
199
200    /// Create a new tuple domain with the given element domains.
201    /// If the given domains are all ground, the variant will be [GroundDomain::Tuple].
202    /// Otherwise, it will be [UnresolvedDomain::Tuple].
203    pub fn tuple(inner_doms: Vec<DomainPtr>) -> DomainPtr {
204        if let Some(inner_gds) = as_grounds(&inner_doms) {
205            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Tuple(inner_gds))));
206        }
207        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(
208            inner_doms,
209        ))))
210    }
211
212    /// Create a new tuple domain with the given entries.
213    /// If the entries are all ground, the variant will be [GroundDomain::Record].
214    /// Otherwise, it will be [UnresolvedDomain::Record].
215    pub fn record(entries: Vec<Field<DomainPtr>>) -> DomainPtr {
216        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
217            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Record(entries_gds))));
218        }
219        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Record(
220            entries,
221        ))))
222    }
223
224    /// Create a new [UnresolvedDomain::Reference] domain from a domain letting
225    pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
226        let _ = ptr.as_domain_letting()?;
227        Some(Moo::new(Domain::Unresolved(Moo::new(
228            UnresolvedDomain::Reference(Reference::new(ptr)),
229        ))))
230    }
231
232    /// Create a new multiset domain with the given element domain and attributes
233    pub fn partition<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
234    where
235        T: Into<PartitionAttr<IntVal>> + TryInto<PartitionAttr<Int>> + Clone,
236    {
237        if let Domain::Ground(gd) = inner_dom.as_ref()
238            && let Ok(int_attr) = attr.clone().try_into()
239        {
240            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Partition(
241                int_attr,
242                gd.clone(),
243            ))));
244        }
245        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(
246            attr.into(),
247            inner_dom,
248        ))))
249    }
250
251    /// Create a new permutation domain with the given element domain and attributes
252    pub fn permutation<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
253    where
254        T: Into<PermutationAttr<IntVal>> + TryInto<PermutationAttr<Int>> + Clone,
255    {
256        if let Domain::Ground(gd) = inner_dom.as_ref()
257            && let Ok(int_attr) = attr.clone().try_into()
258        {
259            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Permutation(
260                int_attr,
261                gd.clone(),
262            ))));
263        }
264        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Permutation(
265            attr.into(),
266            inner_dom,
267        ))))
268    }
269
270    /// Create a new function domain
271    pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
272    where
273        T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
274    {
275        if let Ok(attrs_gd) = attrs.clone().try_into()
276            && let Some(dom_gd) = dom.as_ground()
277            && let Some(cdom_gd) = cdom.as_ground()
278        {
279            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
280                attrs_gd,
281                Moo::new(dom_gd.clone()),
282                Moo::new(cdom_gd.clone()),
283            ))));
284        }
285
286        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
287            attrs.into(),
288            dom,
289            cdom,
290        ))))
291    }
292
293    /// Create a new variant domain with the given entries.
294    /// If the entries are all ground, the variant will be [GroundDomain::Variant].
295    /// Otherwise, it will be [UnresolvedDomain::Variant].
296    pub fn variant(entries: Vec<Field<DomainPtr>>) -> DomainPtr {
297        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
298            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Variant(entries_gds))));
299        }
300        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(
301            entries,
302        ))))
303    }
304
305    /// Create a new relation domain
306    /// If the entries are all ground, the variant will be [GroundDomain::Relation].
307    /// Otherwise, it will be [UnresolvedDomain::Relation].
308    pub fn relation<T>(attrs: T, inner_doms: Vec<DomainPtr>) -> DomainPtr
309    where
310        T: Into<RelAttr<IntVal>> + TryInto<RelAttr<Int>> + Clone,
311    {
312        if let Ok(attrs_gd) = attrs.clone().try_into()
313            && let Some(doms_gd) = as_grounds(&inner_doms)
314        {
315            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Relation(
316                attrs_gd, doms_gd,
317            ))));
318        }
319
320        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
321            attrs.into(),
322            inner_doms,
323        ))))
324    }
325
326    /// Create a new Sequence domain
327    pub fn sequence<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
328    where
329        T: Into<SequenceAttr<IntVal>> + TryInto<SequenceAttr<Int>> + Clone,
330    {
331        if let Domain::Ground(gd) = inner_dom.as_ref()
332            && let Ok(int_attr) = attr.clone().try_into()
333        {
334            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Sequence(
335                int_attr,
336                gd.clone(),
337            ))));
338        }
339        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
340            attr.into(),
341            inner_dom,
342        ))))
343    }
344
345    /// If this domain is ground, return a [Moo] to the underlying [GroundDomain].
346    /// Otherwise, try to resolve it; Return None if this is not yet possible.
347    /// Domains which contain references to givens cannot be resolved until these
348    /// givens are substituted for their concrete values.
349    pub fn resolve(&self) -> Result<Moo<GroundDomain>, DomainOpError> {
350        match self {
351            Domain::Ground(gd) => Ok(gd.clone()),
352            Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
353        }
354    }
355
356    /// If this domain is already ground, return a reference to the underlying [GroundDomain].
357    /// Otherwise, return None. This method does NOT perform any resolution.
358    /// See also: [Domain::resolve].
359    pub fn as_ground(&self) -> Option<&GroundDomain> {
360        match self {
361            Domain::Ground(gd) => Some(gd.as_ref()),
362            _ => None,
363        }
364    }
365
366    /// If this domain is already ground, return a mutable reference to the underlying [GroundDomain].
367    /// Otherwise, return None. This method does NOT perform any resolution.
368    pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
369        match self {
370            Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
371            _ => None,
372        }
373    }
374
375    /// If this domain is unresolved, return a reference to the underlying [UnresolvedDomain].
376    pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
377        match self {
378            Domain::Unresolved(ud) => Some(ud.as_ref()),
379            _ => None,
380        }
381    }
382
383    /// If this domain is unresolved, return a mutable reference to the underlying [UnresolvedDomain].
384    pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
385        match self {
386            Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
387            _ => None,
388        }
389    }
390
391    /// If this is [GroundDomain::Empty(ty)], get a reference to the return type `ty`
392    pub fn as_dom_empty(&self) -> Option<&ReturnType> {
393        if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
394            return Some(ty);
395        }
396        None
397    }
398
399    /// If this is [GroundDomain::Empty(ty)], get a mutable reference to the return type `ty`
400    pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
401        if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
402            return Some(ty);
403        }
404        None
405    }
406
407    /// True if this is [GroundDomain::Bool]
408    pub fn is_bool(&self) -> bool {
409        self.return_type() == ReturnType::Bool
410    }
411
412    /// True if this is a [GroundDomain::Int] or an [UnresolvedDomain::Int]
413    pub fn is_int(&self) -> bool {
414        self.return_type() == ReturnType::Int
415    }
416
417    /// If this domain is [GroundDomain::Int] or [UnresolveDomain::Int], get
418    /// its ranges. The ranges are cloned and upcast to Range<IntVal> if necessary.
419    pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
420        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
421            return Some(rngs.iter().cloned().map(|r| r.into()).collect());
422        }
423        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
424            return Some(rngs.clone());
425        }
426        None
427    }
428
429    /// If this is an int domain, get a mutable reference to its ranges.
430    /// The domain always becomes [UnresolvedDomain::Int] after this operation.
431    pub fn as_int_mut(&mut self) -> Option<&mut Vec<Range<IntVal>>> {
432        // We're "upcasting" ground ranges (Range<Int>) to the more general
433        // Range<IntVal>, which may contain references or expressions.
434        // We know that for now they are still ground, but we're giving the user a mutable
435        // reference, so they can overwrite the ranges with values that aren't ground.
436        // So, the entire domain has to become non-ground as well.
437        if let Some(GroundDomain::Int(rngs_gds)) = self.as_ground() {
438            let rngs: Vec<Range<IntVal>> = rngs_gds.iter().cloned().map(|r| r.into()).collect();
439            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Int(rngs)))
440        }
441
442        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
443            return Some(rngs);
444        }
445        None
446    }
447
448    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
449    pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
450        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
451            return Some(rngs);
452        }
453        None
454    }
455
456    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
457    pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
458        if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
459            return Some(rngs);
460        }
461        None
462    }
463
464    /// If this is a matrix domain, get pointers to its element domain
465    /// and index domains.
466    pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
467        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
468            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
469            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
470            return Some((inner_dom, idx_doms));
471        }
472        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
473            return Some((inner_dom.clone(), idx_doms.clone()));
474        }
475        None
476    }
477
478    /// If this is a matrix domain, get mutable references to its element
479    /// domain and its vector of index domains.
480    /// The domain always becomes [UnresolvedDomain::Matrix] after this operation.
481    pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
482        // "upcast" the entire domain to UnresolvedDomain
483        // See [Domain::as_dom_int_mut] for an explanation of why this is necessary
484        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
485            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
486            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
487            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
488        }
489
490        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
491            return Some((inner_dom, idx_doms));
492        }
493        None
494    }
495
496    /// If this is a [GroundDomain::Matrix], get immutable references to its element and index domains
497    pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
498        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
499            return Some((inner_dom, idx_doms));
500        }
501        None
502    }
503
504    /// If this is a [GroundDomain::Matrix], get mutable references to its element and index domains
505    pub fn as_matrix_ground_mut(
506        &mut self,
507    ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
508        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
509            return Some((inner_dom, idx_doms));
510        }
511        None
512    }
513
514    /// If this is a set domain, get its attributes and a pointer to its element domain.
515    pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
516        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
517            return Some((attr.clone().into(), inner_dom.clone().into()));
518        }
519        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
520            return Some((attr.clone(), inner_dom.clone()));
521        }
522        None
523    }
524
525    /// If this is a set domain, get mutable reference to its attributes and element domain.
526    /// The domain always becomes [UnresolvedDomain::Set] after this operation.
527    pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
528        if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
529            let attr: SetAttr<IntVal> = attr_gd.clone().into();
530            let inner_dom = inner_dom_gd.clone().into();
531            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
532        }
533
534        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
535            return Some((attr, inner_dom));
536        }
537        None
538    }
539
540    /// If this is a [GroundDomain::Set], get immutable references to its attributes and inner domain
541    pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
542        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
543            return Some((attr, inner_dom));
544        }
545        None
546    }
547
548    /// If this is a [GroundDomain::Set], get mutable references to its attributes and inner domain
549    pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
550        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
551            return Some((attr, inner_dom));
552        }
553        None
554    }
555
556    /// User-specified representation preference on this domain, if any (Essence short name).
557    ///
558    /// For set, multiset, and sequence domains this is the optional name in
559    /// `set (representation packed) of …` / `mset (representation repetition) of …`. Nested
560    /// preferences on element domains are not returned here; only the preference attached to
561    /// this domain node.
562    pub fn representation_preference(&self) -> Option<&str> {
563        if let Some(GroundDomain::Set(attr, _)) = self.as_ground() {
564            return attr.representation.as_deref();
565        }
566        if let Some(UnresolvedDomain::Set(attr, _)) = self.as_unresolved() {
567            return attr.representation.as_deref();
568        }
569        if let Some(GroundDomain::MSet(attr, _)) = self.as_ground() {
570            return attr.representation.as_deref();
571        }
572        if let Some(UnresolvedDomain::MSet(attr, _)) = self.as_unresolved() {
573            return attr.representation.as_deref();
574        }
575        if let Some(GroundDomain::Sequence(attr, _)) = self.as_ground() {
576            return attr.representation.as_deref();
577        }
578        if let Some(UnresolvedDomain::Sequence(attr, _)) = self.as_unresolved() {
579            return attr.representation.as_deref();
580        }
581        None
582    }
583
584    /// Whether any representation preference appears anywhere in this domain tree.
585    pub fn has_representation_preference(&self) -> bool {
586        if self.representation_preference().is_some() {
587            return true;
588        }
589        match self {
590            Domain::Ground(gd) => gd.has_representation_preference(),
591            Domain::Unresolved(ud) => ud.has_representation_preference(),
592        }
593    }
594
595    /// Format this domain in Essence type style (`int`, `set (representation packed) of int`, …),
596    /// omitting size attributes and integer ranges.
597    pub fn as_type_string(&self) -> String {
598        match self {
599            Domain::Ground(gd) => gd.as_type_string(),
600            Domain::Unresolved(ud) => ud.as_type_string(),
601        }
602    }
603
604    /// If this is a mset domain, get its attributes and a pointer to its element domain.
605    pub fn as_mset(&self) -> Option<(MSetAttr<IntVal>, DomainPtr)> {
606        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
607            return Some((attr.clone().into(), inner_dom.clone().into()));
608        }
609        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved() {
610            return Some((attr.clone(), inner_dom.clone()));
611        }
612        None
613    }
614
615    /// If this is a set domain, get mutable reference to its attributes and element domain.
616    /// The domain always becomes [UnresolvedDomain::MSet] after this operation.
617    pub fn as_mset_mut(&mut self) -> Option<(&mut MSetAttr<IntVal>, &mut DomainPtr)> {
618        if let Some(GroundDomain::MSet(attr_gd, inner_dom_gd)) = self.as_ground() {
619            let attr: MSetAttr<IntVal> = attr_gd.clone().into();
620            let inner_dom = inner_dom_gd.clone().into();
621            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(attr, inner_dom)));
622        }
623
624        if let Some(UnresolvedDomain::MSet(attr, inner_dom)) = self.as_unresolved_mut() {
625            return Some((attr, inner_dom));
626        }
627        None
628    }
629
630    /// If this is a [GroundDomain::MSet], get immutable references to its attributes and inner domain
631    pub fn as_mset_ground(&self) -> Option<(&MSetAttr<Int>, &Moo<GroundDomain>)> {
632        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground() {
633            return Some((attr, inner_dom));
634        }
635        None
636    }
637
638    /// If this is a [GroundDomain::MSet], get mutable references to its attributes and inner domain
639    pub fn as_mset_ground_mut(&mut self) -> Option<(&mut MSetAttr<Int>, &mut Moo<GroundDomain>)> {
640        if let Some(GroundDomain::MSet(attr, inner_dom)) = self.as_ground_mut() {
641            return Some((attr, inner_dom));
642        }
643        None
644    }
645
646    /// If this is a tuple domain, get pointers to its element domains.
647    pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
648        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
649            return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
650        }
651        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
652            return Some(inner_doms.clone());
653        }
654        None
655    }
656
657    /// If this is a tuple domain, get a mutable reference to its vector of element domains.
658    /// The domain always becomes [UnresolvedDomain::Tuple] after this operation.
659    pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
660        if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
661            let inner_doms: Vec<DomainPtr> =
662                inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
663            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
664        }
665
666        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
667            return Some(inner_doms);
668        }
669        None
670    }
671
672    /// If this is a [GroundDomain::Tuple], get immutable references to its element domains
673    pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
674        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
675            return Some(inner_doms);
676        }
677        None
678    }
679
680    /// If this is a [GroundDomain::Tuple], get mutable reference to its element domains
681    pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
682        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
683            return Some(inner_doms);
684        }
685        None
686    }
687
688    /// If this is a record domain, clone and return its entries.
689    pub fn as_record(&self) -> Option<Vec<FieldUnresolved>> {
690        if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
691            return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
692        }
693        if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
694            return Some(record_entries.clone());
695        }
696        None
697    }
698
699    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
700    pub fn as_record_ground(&self) -> Option<&Vec<FieldGround>> {
701        if let Some(GroundDomain::Record(entries)) = self.as_ground() {
702            return Some(entries);
703        }
704        None
705    }
706
707    /// If this is a record domain, get a mutable reference to its list of entries.
708    /// The domain always becomes [UnresolvedDomain::Record] after this operation.
709    pub fn as_record_mut(&mut self) -> Option<&mut Vec<FieldUnresolved>> {
710        if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
711            let entries: Vec<FieldUnresolved> =
712                entries_gds.iter().cloned().map(|r| r.into()).collect();
713            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
714        }
715
716        if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
717            return Some(entries_gds);
718        }
719        None
720    }
721
722    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
723    pub fn as_record_ground_mut(&mut self) -> Option<&mut Vec<FieldGround>> {
724        if let Some(GroundDomain::Record(entries)) = self.as_ground_mut() {
725            return Some(entries);
726        }
727        None
728    }
729
730    /// If this is a sequence domain, get its (attributes, domain)
731    pub fn as_sequence(&self) -> Option<(SequenceAttr<IntVal>, Moo<Domain>)> {
732        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
733            return Some((attrs.clone().into(), dom.clone().into()));
734        }
735        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved() {
736            return Some((attrs.clone(), dom.clone()));
737        }
738        None
739    }
740
741    /// If this is a function domain, convert it to unresolved and get mutable references to
742    /// its (attrs, domain, co-domain).
743    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
744    pub fn as_sequence_mut(&mut self) -> Option<(&mut SequenceAttr<IntVal>, &mut Moo<Domain>)> {
745        if let Some(GroundDomain::Sequence(attrs, dom)) = self.as_ground() {
746            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Sequence(
747                attrs.clone().into(),
748                dom.clone().into(),
749            )));
750        }
751
752        if let Some(UnresolvedDomain::Sequence(attrs, dom)) = self.as_unresolved_mut() {
753            return Some((attrs, dom));
754        }
755        None
756    }
757
758    /// If this is a function domain, get its (attributes, domain, co-domain)
759    pub fn as_function(&self) -> Option<(FuncAttr<IntVal>, Moo<Domain>, Moo<Domain>)> {
760        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
761            return Some((
762                attrs.clone().into(),
763                dom.clone().into(),
764                codom.clone().into(),
765            ));
766        }
767        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved() {
768            return Some((attrs.clone(), dom.clone(), codom.clone()));
769        }
770        None
771    }
772
773    /// If this is a function domain, convert it to unresolved and get mutable references to
774    /// its (attrs, domain, co-domain).
775    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
776    pub fn as_function_mut(
777        &mut self,
778    ) -> Option<(&mut FuncAttr<IntVal>, &mut Moo<Domain>, &mut Moo<Domain>)> {
779        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
780            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
781                attrs.clone().into(),
782                dom.clone().into(),
783                codom.clone().into(),
784            )));
785        }
786
787        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved_mut() {
788            return Some((attrs, dom, codom));
789        }
790        None
791    }
792
793    /// If this is a [GroundDomain::Function], get its (attrs, domain, co-domain)
794    pub fn as_function_ground(
795        &self,
796    ) -> Option<(&FuncAttr, &Moo<GroundDomain>, &Moo<GroundDomain>)> {
797        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
798            return Some((attrs, dom, codom));
799        }
800        None
801    }
802
803    /// If this is a [GroundDomain::Function], get mutable references to its (attrs, domain, co-domain)
804    pub fn as_function_ground_mut(
805        &mut self,
806    ) -> Option<(
807        &mut FuncAttr,
808        &mut Moo<GroundDomain>,
809        &mut Moo<GroundDomain>,
810    )> {
811        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground_mut() {
812            return Some((attrs, dom, codom));
813        }
814        None
815    }
816
817    /// If this is a partition domain, get its (attributes, domain)
818    pub fn as_partition(&self) -> Option<(PartitionAttr<IntVal>, Moo<Domain>)> {
819        if let Some(GroundDomain::Partition(attrs, doms)) = self.as_ground() {
820            return Some((attrs.clone().into(), doms.clone().into()));
821        }
822        if let Some(UnresolvedDomain::Partition(attrs, doms)) = self.as_unresolved() {
823            return Some((attrs.clone(), doms.clone()));
824        }
825        None
826    }
827
828    /// If this is a partition domain, get mutable reference to its attributes and element domain.
829    /// The domain always becomes [UnresolvedDomain::Partition] after this operation.
830    pub fn as_partition_mut(&mut self) -> Option<(&mut PartitionAttr<IntVal>, &mut DomainPtr)> {
831        if let Some(GroundDomain::Partition(attr_gd, inner_dom_gd)) = self.as_ground() {
832            let attr: PartitionAttr<IntVal> = attr_gd.clone().into();
833            let inner_dom = inner_dom_gd.clone().into();
834            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Partition(attr, inner_dom)));
835        }
836
837        if let Some(UnresolvedDomain::Partition(attr, inner_dom)) = self.as_unresolved_mut() {
838            return Some((attr, inner_dom));
839        }
840        None
841    }
842
843    /// If this is a [GroundDomain::Partition], get immutable references to its attributes and inner domain
844    pub fn as_partition_ground(&self) -> Option<(&PartitionAttr<Int>, &Moo<GroundDomain>)> {
845        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground() {
846            return Some((attr, inner_dom));
847        }
848        None
849    }
850
851    /// If this is a [GroundDomain::Partition], get mutable references to its attributes and inner domain
852    pub fn as_partition_ground_mut(
853        &mut self,
854    ) -> Option<(&mut PartitionAttr<Int>, &mut Moo<GroundDomain>)> {
855        if let Some(GroundDomain::Partition(attr, inner_dom)) = self.as_ground_mut() {
856            return Some((attr, inner_dom));
857        }
858        None
859    }
860
861    /// If this is a permutation domain, get its (attributes, domain)
862    pub fn as_permutation(&self) -> Option<(PermutationAttr<IntVal>, Moo<Domain>)> {
863        if let Some(GroundDomain::Permutation(attrs, doms)) = self.as_ground() {
864            return Some((attrs.clone().into(), doms.clone().into()));
865        }
866        if let Some(UnresolvedDomain::Permutation(attrs, doms)) = self.as_unresolved() {
867            return Some((attrs.clone(), doms.clone()));
868        }
869        None
870    }
871
872    /// If this is a permutation domain, get mutable reference to its attributes and element
873    /// domain. The domain always becomes [UnresolvedDomain::Permutation] after this operation.
874    pub fn as_permutation_mut(&mut self) -> Option<(&mut PermutationAttr<IntVal>, &mut DomainPtr)> {
875        if let Some(GroundDomain::Permutation(attr_gd, inner_dom_gd)) = self.as_ground() {
876            let attr: PermutationAttr<IntVal> = attr_gd.clone().into();
877            let inner_dom = inner_dom_gd.clone().into();
878            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Permutation(attr, inner_dom)));
879        }
880
881        if let Some(UnresolvedDomain::Permutation(attr, inner_dom)) = self.as_unresolved_mut() {
882            return Some((attr, inner_dom));
883        }
884        None
885    }
886
887    /// If this is a [GroundDomain::Permutation], get immutable references to its attributes and inner domain
888    pub fn as_permutation_ground(&self) -> Option<(&PermutationAttr<Int>, &Moo<GroundDomain>)> {
889        if let Some(GroundDomain::Permutation(attr, inner_dom)) = self.as_ground() {
890            return Some((attr, inner_dom));
891        }
892        None
893    }
894
895    /// If this is a [GroundDomain::Permutation], get mutable references to its attributes and inner domain
896    pub fn as_permutation_ground_mut(
897        &mut self,
898    ) -> Option<(&mut PermutationAttr<Int>, &mut Moo<GroundDomain>)> {
899        if let Some(GroundDomain::Permutation(attr, inner_dom)) = self.as_ground_mut() {
900            return Some((attr, inner_dom));
901        }
902        None
903    }
904
905    /// If this is a variant domain, clone and return its entries.
906    pub fn as_variant(&self) -> Option<Vec<FieldUnresolved>> {
907        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
908            return Some(entries.iter().cloned().map(|r| r.into()).collect());
909        }
910        if let Some(UnresolvedDomain::Variant(entries)) = self.as_unresolved() {
911            return Some(entries.clone());
912        }
913        None
914    }
915
916    /// If this is a [GroundDomain::Variant], get a mutable reference to its entries
917    pub fn as_variant_ground(&self) -> Option<&Vec<FieldGround>> {
918        if let Some(GroundDomain::Variant(entries)) = self.as_ground() {
919            return Some(entries);
920        }
921        None
922    }
923
924    /// If this is a variant domain, get a mutable reference to its list of entries.
925    /// The domain always becomes [UnresolvedDomain::Variant] after this operation.
926    pub fn as_variant_mut(&mut self) -> Option<&mut Vec<FieldUnresolved>> {
927        if let Some(GroundDomain::Variant(entries_gds)) = self.as_ground() {
928            let entries: Vec<FieldUnresolved> =
929                entries_gds.iter().cloned().map(|r| r.into()).collect();
930            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Variant(entries)));
931        }
932
933        if let Some(UnresolvedDomain::Variant(entries_gds)) = self.as_unresolved_mut() {
934            return Some(entries_gds);
935        }
936        None
937    }
938
939    /// If this is a [GroundDomain::Variant], get a mutable reference to its entries
940    pub fn as_variant_ground_mut(&mut self) -> Option<&mut Vec<FieldGround>> {
941        if let Some(GroundDomain::Variant(entries)) = self.as_ground_mut() {
942            return Some(entries);
943        }
944        None
945    }
946
947    /// If this is a relation domain, get its (attributes, [domains])
948    pub fn as_relation(&self) -> Option<(RelAttr<IntVal>, Vec<Moo<Domain>>)> {
949        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
950            return Some((
951                attrs.clone().into(),
952                doms.iter().cloned().map(|d| d.into()).collect(),
953            ));
954        }
955        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved() {
956            return Some((attrs.clone(), doms.clone()));
957        }
958        None
959    }
960
961    /// If this is a relation domain, convert it to unresolved and get mutable references to
962    /// its (attrs, [domains]).
963    /// The domain always becomes [UnresolvedDomain::Relation] after this operation.
964    pub fn as_relation_mut(&mut self) -> Option<(&mut RelAttr<IntVal>, &mut Vec<Moo<Domain>>)> {
965        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
966            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Relation(
967                attrs.clone().into(),
968                doms.iter().cloned().map(|d| d.into()).collect(),
969            )));
970        }
971
972        if let Some(UnresolvedDomain::Relation(attrs, doms)) = self.as_unresolved_mut() {
973            return Some((attrs, doms));
974        }
975        None
976    }
977
978    /// If this is a [GroundDomain::Relation], get its (attrs, [domains])
979    pub fn as_relation_ground(&self) -> Option<(&RelAttr, &Vec<Moo<GroundDomain>>)> {
980        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground() {
981            return Some((attrs, doms));
982        }
983        None
984    }
985
986    /// If this is a [GroundDomain::Relation], get mutable references to its (attrs, [domains])
987    pub fn as_relation_ground_mut(
988        &mut self,
989    ) -> Option<(&mut RelAttr, &mut Vec<Moo<GroundDomain>>)> {
990        if let Some(GroundDomain::Relation(attrs, doms)) = self.as_ground_mut() {
991            return Some((attrs, doms));
992        }
993        None
994    }
995
996    /// Compute the union of two domains.
997    ///
998    /// Domain-letting references are transparent here: the result may remain unresolved when the
999    /// referenced domain has symbolic bounds.
1000    pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
1001        if let Domain::Unresolved(domain) = self
1002            && let UnresolvedDomain::Reference(reference) = domain.as_ref()
1003        {
1004            let referenced_domain = reference.domain().unwrap_or_else(|| {
1005                crate::bug!("domain reference should point to a domain letting: {reference}")
1006            });
1007            return referenced_domain.as_ref().union(other);
1008        }
1009
1010        if let Domain::Unresolved(domain) = other
1011            && let UnresolvedDomain::Reference(reference) = domain.as_ref()
1012        {
1013            let referenced_domain = reference.domain().unwrap_or_else(|| {
1014                crate::bug!("domain reference should point to a domain letting: {reference}")
1015            });
1016            return self.union(referenced_domain.as_ref());
1017        }
1018
1019        match (self, other) {
1020            (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
1021            (Domain::Unresolved(a), Domain::Unresolved(b)) => {
1022                Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
1023            }
1024            (Domain::Unresolved(u), Domain::Ground(g))
1025            | (Domain::Ground(g), Domain::Unresolved(u)) => {
1026                if let GroundDomain::Empty(ty) = g.as_ref() {
1027                    return if *ty == u.return_type() {
1028                        Ok(Domain::Unresolved(u.clone()))
1029                    } else {
1030                        Err(DomainOpError::WrongType)
1031                    };
1032                }
1033
1034                let ground_as_unresolved =
1035                    UnresolvedDomain::from_ground(g).ok_or(DomainOpError::WrongType)?;
1036                Ok(Domain::Unresolved(Moo::new(
1037                    u.union_unresolved(&ground_as_unresolved)?,
1038                )))
1039            }
1040        }
1041    }
1042
1043    /// Compute the intersection of two ground domains
1044    pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
1045        match (self, other) {
1046            (Domain::Ground(a), Domain::Ground(b)) => {
1047                a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
1048            }
1049            _ => Err(DomainOpError::NotGround),
1050        }
1051    }
1052
1053    /// If the domain is ground, return an iterator over its values
1054    pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
1055        if let Some(gd) = self.as_ground() {
1056            return gd.values();
1057        }
1058        Err(DomainOpError::NotGround)
1059    }
1060
1061    /// If the domain is ground, return its size bound
1062    pub fn length(&self) -> Result<u64, DomainOpError> {
1063        if let Some(gd) = self.as_ground() {
1064            return gd.length();
1065        }
1066        Err(DomainOpError::NotGround)
1067    }
1068    /// Get the size of some domain
1069    ///
1070    /// As opposed to `Domain::length`, this function returns a signed integer (`i32`) rather than unsigned.
1071    /// * `DomainOpError::NotGround` - This function only applies to `ground` domains
1072    /// * `DomainOpError::TooLarge` - Converting to an integer my not be possible if the domain is too big
1073    pub fn length_signed(&self) -> Result<i32, DomainOpError> {
1074        let gd = self.as_ground().ok_or(DomainOpError::NotGround)?;
1075        let len = gd.length()?;
1076        len.try_into().map_err(|_| DomainOpError::TooLarge)
1077    }
1078
1079    /// Construct a ground domain from a slice of values
1080    pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
1081        GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
1082    }
1083
1084    /// Returns true if `lit` is a valid value of this domain
1085    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
1086        if let Some(gd) = self.as_ground() {
1087            return gd.contains(lit);
1088        }
1089        Err(DomainOpError::NotGround)
1090    }
1091
1092    pub fn element_domain(&self) -> Option<DomainPtr> {
1093        match self {
1094            Domain::Ground(gd) => gd.element_domain().map(DomainPtr::from),
1095            Domain::Unresolved(ud) => ud.element_domain(),
1096        }
1097    }
1098}
1099
1100impl Typeable for Domain {
1101    fn return_type(&self) -> ReturnType {
1102        match self {
1103            Domain::Ground(dom) => dom.return_type(),
1104            Domain::Unresolved(dom) => dom.return_type(),
1105        }
1106    }
1107}
1108
1109impl Display for Domain {
1110    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1111        match &self {
1112            Domain::Ground(gd) => gd.fmt(f),
1113            Domain::Unresolved(ud) => ud.fmt(f),
1114        }
1115    }
1116}
1117
1118fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
1119    doms.iter()
1120        .map(|idx| match idx.as_ref() {
1121            Domain::Ground(idx_gd) => Some(idx_gd.clone()),
1122            _ => None,
1123        })
1124        .collect()
1125}
1126
1127#[cfg(test)]
1128mod tests {
1129    use super::*;
1130    use crate::ast::Name;
1131    use crate::{domain_int, range};
1132
1133    #[test]
1134    fn unresolved_int_domain_resolve_squeezes_ranges() {
1135        let domain = UnresolvedDomain::Int(vec![
1136            Range::Single(IntVal::Const(5)),
1137            Range::Bounded(IntVal::Const(3), IntVal::Const(7)),
1138            Range::Bounded(IntVal::Const(5), IntVal::Const(3)),
1139            Range::Single(IntVal::Const(7)),
1140        ]);
1141
1142        assert_eq!(
1143            domain.resolve(),
1144            Ok(GroundDomain::Int(vec![Range::Bounded(3, 7)]))
1145        );
1146    }
1147
1148    #[test]
1149    fn union_dereferences_domain_lettings() {
1150        let declaration = DeclarationPtr::new_domain_letting(
1151            Name::user("Alias"),
1152            Domain::int(vec![Range::Bounded(1, 3)]),
1153        );
1154        let alias = Domain::reference(declaration).unwrap();
1155
1156        assert_eq!(
1157            alias
1158                .union(&Domain::int(vec![Range::Bounded(5, 7)]))
1159                .unwrap()
1160                .resolve(),
1161            Ok(Moo::new(GroundDomain::Int(vec![
1162                Range::Bounded(1, 3),
1163                Range::Bounded(5, 7),
1164            ])))
1165        );
1166    }
1167
1168    #[test]
1169    fn union_preserves_symbolic_domain_bounds() {
1170        let upper_bound =
1171            DeclarationPtr::new_given(Name::user("n"), Domain::int(vec![Range::Bounded(1, 10)]));
1172        let symbolic_upper = IntVal::new_ref(&Reference::new(upper_bound)).unwrap();
1173        let symbolic_domain = Domain::int(vec![Range::Bounded(
1174            IntVal::Const(1),
1175            symbolic_upper.clone(),
1176        )]);
1177        let declaration = DeclarationPtr::new_domain_letting(Name::user("Alias"), symbolic_domain);
1178        let alias = Domain::reference(declaration).unwrap();
1179
1180        let union = alias.union(&Domain::int(vec![Range::Single(20)])).unwrap();
1181
1182        assert_eq!(
1183            union.as_int(),
1184            Some(vec![
1185                Range::Bounded(IntVal::Const(1), symbolic_upper),
1186                Range::Single(IntVal::Const(20)),
1187            ])
1188        );
1189        assert_eq!(union.resolve(), Err(DomainOpError::NotGround));
1190    }
1191
1192    #[test]
1193    fn test_negative_product() {
1194        let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
1195        let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
1196        let res = d1
1197            .as_ground()
1198            .unwrap()
1199            .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
1200            .unwrap();
1201
1202        assert!(matches!(res, GroundDomain::Int(_)));
1203        if let GroundDomain::Int(ranges) = res {
1204            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
1205        }
1206    }
1207
1208    #[test]
1209    fn test_negative_div() {
1210        let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
1211        let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
1212        let res = d1
1213            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
1214            .unwrap();
1215
1216        assert!(matches!(res, GroundDomain::Int(_)));
1217        if let GroundDomain::Int(ranges) = res {
1218            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
1219        }
1220    }
1221
1222    #[test]
1223    fn test_length_basic() {
1224        assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
1225        assert_eq!(Domain::bool().length(), Ok(2));
1226        assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
1227        assert_eq!(
1228            domain_int!(1..2, 5..).length(),
1229            Err(DomainOpError::Unbounded)
1230        );
1231    }
1232    #[test]
1233    fn test_length_set_basic() {
1234        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
1235        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..3));
1236        assert_eq!(s.length(), Ok(8));
1237
1238        // {{1,2}, {1,3}, {2,3}}
1239        let s = Domain::set(SetAttr::new_size(2), domain_int!(1..3));
1240        assert_eq!(s.length(), Ok(3));
1241
1242        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
1243        let s = Domain::set(SetAttr::new_min_max_size(1, 2), domain_int!(1..3));
1244        assert_eq!(s.length(), Ok(6));
1245
1246        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
1247        let s = Domain::set(SetAttr::new_min_size(1), domain_int!(1..3));
1248        assert_eq!(s.length(), Ok(7));
1249
1250        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
1251        let s = Domain::set(SetAttr::new_max_size(2), domain_int!(1..3));
1252        assert_eq!(s.length(), Ok(7));
1253    }
1254
1255    #[test]
1256    fn test_set_representation_preference_display() {
1257        let s = Domain::set(
1258            SetAttr::new_max_size(3).with_representation("packed"),
1259            domain_int!(1..4),
1260        );
1261        assert_eq!(
1262            s.to_string(),
1263            "set (representation packed, maxSize 3) of int(1..4)"
1264        );
1265        assert_eq!(s.as_type_string(), "set (representation packed) of int");
1266        assert_eq!(s.representation_preference(), Some("packed"));
1267
1268        let nested = Domain::set(
1269            SetAttr::<IntVal>::default().with_representation("explicit"),
1270            Domain::set(
1271                SetAttr::<IntVal>::default().with_representation("occurrence"),
1272                domain_int!(1..2),
1273            ),
1274        );
1275        assert_eq!(
1276            nested.to_string(),
1277            "set (representation explicit) of set (representation occurrence) of int(1..2)"
1278        );
1279        assert_eq!(
1280            nested.as_type_string(),
1281            "set (representation explicit) of set (representation occurrence) of int"
1282        );
1283    }
1284
1285    #[test]
1286    fn test_mset_representation_preference_survives_nesting() {
1287        let mset = Domain::mset(
1288            MSetAttr::new_max_size(IntVal::Const(6)).with_representation("repetition"),
1289            domain_int!(1..9),
1290        );
1291        let nested = Domain::matrix(
1292            Domain::record(vec![Field {
1293                name: Name::user("before"),
1294                value: mset.clone(),
1295            }]),
1296            vec![domain_int!(1..2)],
1297        );
1298
1299        assert_eq!(mset.representation_preference(), Some("repetition"));
1300        assert_eq!(
1301            mset.as_type_string(),
1302            "mset (representation repetition) of int"
1303        );
1304        assert_eq!(
1305            mset.to_string(),
1306            "mset (representation repetition, maxSize 6) of int(1..9)"
1307        );
1308        assert!(nested.has_representation_preference());
1309        assert_eq!(
1310            nested.to_string(),
1311            "matrix indexed by [int(1..2)] of record {before: mset (representation repetition, maxSize 6) of int(1..9)}"
1312        );
1313    }
1314
1315    #[test]
1316    fn test_length_set_nested() {
1317        // {
1318        // ∅,                                          -- all size 0
1319        // {∅}, {{1}}, {{2}}, {{1, 2}},                -- all size 1
1320        // {∅, {1}}, {∅, {2}}, {∅, {1, 2}},            -- all size 2
1321        // {{1}, {2}}, {{1}, {1, 2}}, {{2}, {1, 2}}
1322        // }
1323        let s2 = Domain::set(
1324            SetAttr::new_max_size(2),
1325            // {∅, {1}, {2}, {1,2}}
1326            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
1327        );
1328        assert_eq!(s2.length(), Ok(11));
1329    }
1330
1331    #[test]
1332    fn test_length_set_unbounded_inner() {
1333        // leaf domain is unbounded
1334        let s2_bad = Domain::set(
1335            SetAttr::new_max_size(2),
1336            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..)),
1337        );
1338        assert_eq!(s2_bad.length(), Err(DomainOpError::Unbounded));
1339    }
1340
1341    #[test]
1342    fn test_length_set_overflow() {
1343        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..20));
1344        assert!(s.length().is_ok());
1345
1346        // current way of calculating the formula overflows for anything larger than this
1347        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..63));
1348        assert_eq!(s.length(), Err(DomainOpError::TooLarge));
1349    }
1350
1351    #[test]
1352    fn test_length_tuple() {
1353        // 3 ways to pick first element, 2 ways to pick second element
1354        let t = Domain::tuple(vec![domain_int!(1..3), Domain::bool()]);
1355        assert_eq!(t.length(), Ok(6));
1356    }
1357
1358    #[test]
1359    fn test_length_record() {
1360        // 3 ways to pick rec.a, 2 ways to pick rec.b
1361        let t = Domain::record(vec![
1362            Field {
1363                name: Name::user("a"),
1364                value: domain_int!(1..3),
1365            },
1366            Field {
1367                name: Name::user("b"),
1368                value: Domain::bool(),
1369            },
1370        ]);
1371        assert_eq!(t.length(), Ok(6));
1372    }
1373
1374    #[test]
1375    fn test_length_matrix_basic() {
1376        // 3 booleans -> [T, T, T], [T, T, F], ..., [F, F, F]
1377        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..3)]);
1378        assert_eq!(m.length(), Ok(8));
1379
1380        // 2 numbers, each 1..3 -> 3*3 options
1381        let m = Domain::matrix(domain_int!(1..3), vec![domain_int!(1..2)]);
1382        assert_eq!(m.length(), Ok(9));
1383    }
1384
1385    #[test]
1386    fn test_length_matrix_2d() {
1387        // 2x3 matrix of booleans -> (2**2)**3 = 64 options
1388        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..2), domain_int!(1..3)]);
1389        assert_eq!(m.length(), Ok(64));
1390    }
1391
1392    #[test]
1393    fn test_length_matrix_of_sets() {
1394        // 3 sets drawn from 1..2; 4**3 = 64 total options
1395        let m = Domain::matrix(
1396            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
1397            vec![domain_int!(1..3)],
1398        );
1399        assert_eq!(m.length(), Ok(64));
1400    }
1401}
1402
1403/// The ranges of an integer collection that can already be evaluated, if it can.
1404fn int_ranges_from_constant_collection(values: &Expression) -> Option<Vec<Range<Int>>> {
1405    let values = crate::ast::eval::generator_values_from_expr(values)?;
1406    let mut ranges = Vec::with_capacity(values.len());
1407    for value in values {
1408        let crate::ast::Literal::Int(value) = value else {
1409            return None;
1410        };
1411        ranges.push(Range::Single(value));
1412    }
1413    Some(ranges)
1414}