Skip to main content

conjure_cp_core/ast/domains/
domain.rs

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