Skip to main content

conjure_cp_core/ast/domains/
domain.rs

1use super::attrs::SetAttr;
2use super::ground::GroundDomain;
3use super::range::Range;
4use super::unresolved::{IntVal, UnresolvedDomain};
5use crate::ast::domains::attrs::MSetAttr;
6use crate::ast::{
7    DeclarationPtr, DomainOpError, Expression, FuncAttr, Literal, Moo, RecordEntry,
8    RecordEntryGround, Reference, ReturnType, Typeable,
9};
10use itertools::Itertools;
11use polyquine::Quine;
12use serde::{Deserialize, Serialize};
13use std::fmt::{Display, Formatter};
14use std::thread_local;
15use uniplate::Uniplate;
16
17/// The integer type used in all domain code (int ranges, set sizes, etc)
18pub type Int = i32;
19
20pub type DomainPtr = Moo<Domain>;
21
22impl DomainPtr {
23    pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
24        self.as_ref().resolve()
25    }
26
27    /// Convenience method to take [Domain::union] of the [Domain]s behind two [DomainPtr]s
28    /// and wrap the result in a new [DomainPtr].
29    pub fn union(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
30        self.as_ref().union(other.as_ref()).map(DomainPtr::new)
31    }
32
33    /// Convenience method to take [Domain::intersect] of the [Domain]s behind two [DomainPtr]s
34    /// and wrap the result in a new [DomainPtr].
35    pub fn intersect(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
36        self.as_ref().intersect(other.as_ref()).map(DomainPtr::new)
37    }
38}
39
40impl From<Moo<GroundDomain>> for DomainPtr {
41    fn from(value: Moo<GroundDomain>) -> Self {
42        Moo::new(Domain::Ground(value))
43    }
44}
45
46impl From<Moo<UnresolvedDomain>> for DomainPtr {
47    fn from(value: Moo<UnresolvedDomain>) -> Self {
48        Moo::new(Domain::Unresolved(value))
49    }
50}
51
52impl From<GroundDomain> for DomainPtr {
53    fn from(value: GroundDomain) -> Self {
54        Moo::new(Domain::Ground(Moo::new(value)))
55    }
56}
57
58impl From<UnresolvedDomain> for DomainPtr {
59    fn from(value: UnresolvedDomain) -> Self {
60        Moo::new(Domain::Unresolved(Moo::new(value)))
61    }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
65#[biplate(to=DomainPtr)]
66#[biplate(to=GroundDomain)]
67#[biplate(to=UnresolvedDomain)]
68#[biplate(to=Expression)]
69#[biplate(to=Reference)]
70#[biplate(to=RecordEntry)]
71#[biplate(to=IntVal)]
72#[path_prefix(conjure_cp::ast)]
73pub enum Domain {
74    /// A fully resolved domain
75    Ground(Moo<GroundDomain>),
76    /// A domain which may contain references
77    Unresolved(Moo<UnresolvedDomain>),
78}
79
80/// Types that have a [`Domain`].
81pub trait HasDomain {
82    /// Gets the [`Domain`] of `self`.
83    fn domain_of(&self) -> DomainPtr;
84}
85
86impl<T: HasDomain> Typeable for T {
87    fn return_type(&self) -> ReturnType {
88        self.domain_of().return_type()
89    }
90}
91
92// Domain::Bool is completely static, so reuse the same chunk of memory
93// for all bool domains to avoid many small memory allocations
94thread_local! {
95    static BOOL_DOMAIN: DomainPtr =
96        Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
97}
98
99impl Domain {
100    /// Create a new boolean domain and return a pointer to it.
101    /// Boolean domains are always ground (see [GroundDomain::Bool]).
102    pub fn bool() -> DomainPtr {
103        BOOL_DOMAIN.with(Clone::clone)
104    }
105
106    /// Create a new empty domain of the given type and return a pointer to it.
107    /// Empty domains are always ground (see [GroundDomain::Empty]).
108    pub fn empty(ty: ReturnType) -> DomainPtr {
109        Moo::new(Domain::Ground(Moo::new(GroundDomain::Empty(ty))))
110    }
111
112    /// Create a new int domain with the given ranges.
113    /// If the ranges are all ground, the variant will be [GroundDomain::Int].
114    /// Otherwise, it will be [UnresolvedDomain::Int].
115    pub fn int<T>(ranges: Vec<T>) -> DomainPtr
116    where
117        T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
118    {
119        if let Ok(int_rngs) = ranges
120            .iter()
121            .cloned()
122            .map(TryInto::try_into)
123            .collect::<Result<Vec<_>, _>>()
124        {
125            return Domain::int_ground(int_rngs);
126        }
127        let unresolved_rngs: Vec<Range<IntVal>> = ranges.into_iter().map(Into::into).collect();
128        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Int(
129            unresolved_rngs,
130        ))))
131    }
132
133    /// Create a new ground integer domain with the given ranges
134    pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
135        let rngs = Range::squeeze(&ranges);
136        Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
137    }
138
139    /// Create a new set domain with the given element domain and attributes.
140    /// If the element domain and the attributes are ground, the variant
141    /// will be [GroundDomain::Set]. Otherwise, it will be [UnresolvedDomain::Set].
142    pub fn set<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
143    where
144        T: Into<SetAttr<IntVal>> + TryInto<SetAttr<Int>> + Clone,
145    {
146        if let Domain::Ground(gd) = inner_dom.as_ref()
147            && let Ok(int_attr) = attr.clone().try_into()
148        {
149            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Set(
150                int_attr,
151                gd.clone(),
152            ))));
153        }
154        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Set(
155            attr.into(),
156            inner_dom,
157        ))))
158    }
159
160    /// Create a new multiset domain with the given element domain and attributes
161    pub fn mset<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
162    where
163        T: Into<MSetAttr<IntVal>> + TryInto<MSetAttr<Int>> + Clone,
164    {
165        if let Domain::Ground(gd) = inner_dom.as_ref()
166            && let Ok(int_attr) = attr.clone().try_into()
167        {
168            return Moo::new(Domain::Ground(Moo::new(GroundDomain::MSet(
169                int_attr,
170                gd.clone(),
171            ))));
172        }
173        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::MSet(
174            attr.into(),
175            inner_dom,
176        ))))
177    }
178
179    /// Create a new matrix domain with the given element domain and index domains.
180    /// If the given domains are all ground, the variant will be [GroundDomain::Matrix].
181    /// Otherwise, it will be [UnresolvedDomain::Matrix].
182    pub fn matrix(inner_dom: DomainPtr, idx_doms: Vec<DomainPtr>) -> DomainPtr {
183        if let Domain::Ground(gd) = inner_dom.as_ref()
184            && let Some(idx_gds) = as_grounds(&idx_doms)
185        {
186            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Matrix(
187                gd.clone(),
188                idx_gds,
189            ))));
190        }
191        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(
192            inner_dom, idx_doms,
193        ))))
194    }
195
196    /// Create a new tuple domain with the given element domains.
197    /// If the given domains are all ground, the variant will be [GroundDomain::Tuple].
198    /// Otherwise, it will be [UnresolvedDomain::Tuple].
199    pub fn tuple(inner_doms: Vec<DomainPtr>) -> DomainPtr {
200        if let Some(inner_gds) = as_grounds(&inner_doms) {
201            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Tuple(inner_gds))));
202        }
203        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(
204            inner_doms,
205        ))))
206    }
207
208    /// Create a new tuple domain with the given entries.
209    /// If the entries are all ground, the variant will be [GroundDomain::Record].
210    /// Otherwise, it will be [UnresolvedDomain::Record].
211    pub fn record(entries: Vec<RecordEntry>) -> DomainPtr {
212        if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
213            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Record(entries_gds))));
214        }
215        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Record(
216            entries,
217        ))))
218    }
219
220    /// Create a new [UnresolvedDomain::Reference] domain from a domain letting
221    pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
222        let _ = ptr.as_domain_letting()?;
223        Some(Moo::new(Domain::Unresolved(Moo::new(
224            UnresolvedDomain::Reference(Reference::new(ptr)),
225        ))))
226    }
227
228    /// Create a new function domain
229    pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
230    where
231        T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
232    {
233        if let Ok(attrs_gd) = attrs.clone().try_into()
234            && let Some(dom_gd) = dom.as_ground()
235            && let Some(cdom_gd) = cdom.as_ground()
236        {
237            return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
238                attrs_gd,
239                Moo::new(dom_gd.clone()),
240                Moo::new(cdom_gd.clone()),
241            ))));
242        }
243
244        Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
245            attrs.into(),
246            dom,
247            cdom,
248        ))))
249    }
250
251    /// If this domain is ground, return a [Moo] to the underlying [GroundDomain].
252    /// Otherwise, try to resolve it; Return None if this is not yet possible.
253    /// Domains which contain references to givens cannot be resolved until these
254    /// givens are substituted for their concrete values.
255    pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
256        match self {
257            Domain::Ground(gd) => Some(gd.clone()),
258            Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
259        }
260    }
261
262    /// If this domain is already ground, return a reference to the underlying [GroundDomain].
263    /// Otherwise, return None. This method does NOT perform any resolution.
264    /// See also: [Domain::resolve].
265    pub fn as_ground(&self) -> Option<&GroundDomain> {
266        match self {
267            Domain::Ground(gd) => Some(gd.as_ref()),
268            _ => None,
269        }
270    }
271
272    /// If this domain is already ground, return a mutable reference to the underlying [GroundDomain].
273    /// Otherwise, return None. This method does NOT perform any resolution.
274    pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
275        match self {
276            Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
277            _ => None,
278        }
279    }
280
281    /// If this domain is unresolved, return a reference to the underlying [UnresolvedDomain].
282    pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
283        match self {
284            Domain::Unresolved(ud) => Some(ud.as_ref()),
285            _ => None,
286        }
287    }
288
289    /// If this domain is unresolved, return a mutable reference to the underlying [UnresolvedDomain].
290    pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
291        match self {
292            Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
293            _ => None,
294        }
295    }
296
297    /// If this is [GroundDomain::Empty(ty)], get a reference to the return type `ty`
298    pub fn as_dom_empty(&self) -> Option<&ReturnType> {
299        if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
300            return Some(ty);
301        }
302        None
303    }
304
305    /// If this is [GroundDomain::Empty(ty)], get a mutable reference to the return type `ty`
306    pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
307        if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
308            return Some(ty);
309        }
310        None
311    }
312
313    /// True if this is [GroundDomain::Bool]
314    pub fn is_bool(&self) -> bool {
315        self.return_type() == ReturnType::Bool
316    }
317
318    /// True if this is a [GroundDomain::Int] or an [UnresolvedDomain::Int]
319    pub fn is_int(&self) -> bool {
320        self.return_type() == ReturnType::Int
321    }
322
323    /// If this domain is [GroundDomain::Int] or [UnresolveDomain::Int], get
324    /// its ranges. The ranges are cloned and upcast to Range<IntVal> if necessary.
325    pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
326        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
327            return Some(rngs.iter().cloned().map(|r| r.into()).collect());
328        }
329        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
330            return Some(rngs.clone());
331        }
332        None
333    }
334
335    /// If this is an int domain, get a mutable reference to its ranges.
336    /// The domain always becomes [UnresolvedDomain::Int] after this operation.
337    pub fn as_int_mut(&mut self) -> Option<&mut Vec<Range<IntVal>>> {
338        // We're "upcasting" ground ranges (Range<Int>) to the more general
339        // Range<IntVal>, which may contain references or expressions.
340        // We know that for now they are still ground, but we're giving the user a mutable
341        // reference, so they can overwrite the ranges with values that aren't ground.
342        // So, the entire domain has to become non-ground as well.
343        if let Some(GroundDomain::Int(rngs_gds)) = self.as_ground() {
344            let rngs: Vec<Range<IntVal>> = rngs_gds.iter().cloned().map(|r| r.into()).collect();
345            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Int(rngs)))
346        }
347
348        if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
349            return Some(rngs);
350        }
351        None
352    }
353
354    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
355    pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
356        if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
357            return Some(rngs);
358        }
359        None
360    }
361
362    /// If this is a [GroundDomain::Int(rngs)], get an immutable reference to rngs.
363    pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
364        if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
365            return Some(rngs);
366        }
367        None
368    }
369
370    /// If this is a matrix domain, get pointers to its element domain
371    /// and index domains.
372    pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
373        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
374            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
375            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
376            return Some((inner_dom, idx_doms));
377        }
378        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
379            return Some((inner_dom.clone(), idx_doms.clone()));
380        }
381        None
382    }
383
384    /// If this is a matrix domain, get mutable references to its element
385    /// domain and its vector of index domains.
386    /// The domain always becomes [UnresolvedDomain::Matrix] after this operation.
387    pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
388        // "upcast" the entire domain to UnresolvedDomain
389        // See [Domain::as_dom_int_mut] for an explanation of why this is necessary
390        if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
391            let inner_dom: DomainPtr = inner_dom_gd.clone().into();
392            let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
393            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
394        }
395
396        if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
397            return Some((inner_dom, idx_doms));
398        }
399        None
400    }
401
402    /// If this is a [GroundDomain::Matrix], get immutable references to its element and index domains
403    pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
404        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
405            return Some((inner_dom, idx_doms));
406        }
407        None
408    }
409
410    /// If this is a [GroundDomain::Matrix], get mutable references to its element and index domains
411    pub fn as_matrix_ground_mut(
412        &mut self,
413    ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
414        if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
415            return Some((inner_dom, idx_doms));
416        }
417        None
418    }
419
420    /// If this is a set domain, get its attributes and a pointer to its element domain.
421    pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
422        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
423            return Some((attr.clone().into(), inner_dom.clone().into()));
424        }
425        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
426            return Some((attr.clone(), inner_dom.clone()));
427        }
428        None
429    }
430
431    /// If this is a set domain, get mutable reference to its attributes and element domain.
432    /// The domain always becomes [UnresolvedDomain::Set] after this operation.
433    pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
434        if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
435            let attr: SetAttr<IntVal> = attr_gd.clone().into();
436            let inner_dom = inner_dom_gd.clone().into();
437            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
438        }
439
440        if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
441            return Some((attr, inner_dom));
442        }
443        None
444    }
445
446    /// If this is a [GroundDomain::Set], get immutable references to its attributes and inner domain
447    pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
448        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
449            return Some((attr, inner_dom));
450        }
451        None
452    }
453
454    /// If this is a [GroundDomain::Set], get mutable references to its attributes and inner domain
455    pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
456        if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
457            return Some((attr, inner_dom));
458        }
459        None
460    }
461
462    /// If this is a tuple domain, get pointers to its element domains.
463    pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
464        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
465            return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
466        }
467        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
468            return Some(inner_doms.clone());
469        }
470        None
471    }
472
473    /// If this is a tuple domain, get a mutable reference to its vector of element domains.
474    /// The domain always becomes [UnresolvedDomain::Tuple] after this operation.
475    pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
476        if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
477            let inner_doms: Vec<DomainPtr> =
478                inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
479            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
480        }
481
482        if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
483            return Some(inner_doms);
484        }
485        None
486    }
487
488    /// If this is a [GroundDomain::Tuple], get immutable references to its element domains
489    pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
490        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
491            return Some(inner_doms);
492        }
493        None
494    }
495
496    /// If this is a [GroundDomain::Tuple], get mutable reference to its element domains
497    pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
498        if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
499            return Some(inner_doms);
500        }
501        None
502    }
503
504    /// If this is a record domain, clone and return its entries.
505    pub fn as_record(&self) -> Option<Vec<RecordEntry>> {
506        if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
507            return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
508        }
509        if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
510            return Some(record_entries.clone());
511        }
512        None
513    }
514
515    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
516    pub fn as_record_ground(&self) -> Option<&Vec<RecordEntryGround>> {
517        if let Some(GroundDomain::Record(entries)) = self.as_ground() {
518            return Some(entries);
519        }
520        None
521    }
522
523    /// If this is a record domain, get a mutable reference to its list of entries.
524    /// The domain always becomes [UnresolvedDomain::Record] after this operation.
525    pub fn as_record_mut(&mut self) -> Option<&mut Vec<RecordEntry>> {
526        if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
527            let entries: Vec<RecordEntry> = entries_gds.iter().cloned().map(|r| r.into()).collect();
528            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
529        }
530
531        if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
532            return Some(entries_gds);
533        }
534        None
535    }
536
537    /// If this is a [GroundDomain::Record], get a mutable reference to its entries
538    pub fn as_record_ground_mut(&mut self) -> Option<&mut Vec<RecordEntryGround>> {
539        if let Some(GroundDomain::Record(entries)) = self.as_ground_mut() {
540            return Some(entries);
541        }
542        None
543    }
544
545    /// If this is a function domain, get its (attributes, domain, co-domain)
546    pub fn as_function(&self) -> Option<(FuncAttr<IntVal>, Moo<Domain>, Moo<Domain>)> {
547        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
548            return Some((
549                attrs.clone().into(),
550                dom.clone().into(),
551                codom.clone().into(),
552            ));
553        }
554        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved() {
555            return Some((attrs.clone(), dom.clone(), codom.clone()));
556        }
557        None
558    }
559
560    /// If this is a function domain, convert it to unresolved and get mutable references to
561    /// its (attrs, domain, co-domain).
562    /// The domain always becomes [UnresolvedDomain::Function] after this operation.
563    pub fn as_function_mut(
564        &mut self,
565    ) -> Option<(&mut FuncAttr<IntVal>, &mut Moo<Domain>, &mut Moo<Domain>)> {
566        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
567            *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
568                attrs.clone().into(),
569                dom.clone().into(),
570                codom.clone().into(),
571            )));
572        }
573
574        if let Some(UnresolvedDomain::Function(attrs, dom, codom)) = self.as_unresolved_mut() {
575            return Some((attrs, dom, codom));
576        }
577        None
578    }
579
580    /// If this is a [GroundDomain::Function], get its (attrs, domain, co-domain)
581    pub fn as_function_ground(
582        &self,
583    ) -> Option<(&FuncAttr, &Moo<GroundDomain>, &Moo<GroundDomain>)> {
584        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground() {
585            return Some((attrs, dom, codom));
586        }
587        None
588    }
589
590    /// If this is a [GroundDomain::Function], get mutable references to its (attrs, domain, co-domain)
591    pub fn as_function_ground_mut(
592        &mut self,
593    ) -> Option<(
594        &mut FuncAttr,
595        &mut Moo<GroundDomain>,
596        &mut Moo<GroundDomain>,
597    )> {
598        if let Some(GroundDomain::Function(attrs, dom, codom)) = self.as_ground_mut() {
599            return Some((attrs, dom, codom));
600        }
601        None
602    }
603
604    /// Compute the intersection of two domains
605    pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
606        match (self, other) {
607            (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
608            (Domain::Unresolved(a), Domain::Unresolved(b)) => {
609                Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
610            }
611            (Domain::Unresolved(u), Domain::Ground(g))
612            | (Domain::Ground(g), Domain::Unresolved(u)) => {
613                todo!("Union of unresolved domain {u} and ground domain {g} is not yet implemented")
614            }
615        }
616    }
617
618    /// Compute the intersection of two ground domains
619    pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
620        match (self, other) {
621            (Domain::Ground(a), Domain::Ground(b)) => {
622                a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
623            }
624            _ => Err(DomainOpError::NotGround),
625        }
626    }
627
628    /// If the domain is ground, return an iterator over its values
629    pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
630        if let Some(gd) = self.as_ground() {
631            return gd.values();
632        }
633        Err(DomainOpError::NotGround)
634    }
635
636    /// If the domain is ground, return its size bound
637    pub fn length(&self) -> Result<u64, DomainOpError> {
638        if let Some(gd) = self.as_ground() {
639            return gd.length();
640        }
641        Err(DomainOpError::NotGround)
642    }
643
644    /// Construct a ground domain from a slice of values
645    pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
646        GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
647    }
648
649    /// Returns true if `lit` is a valid value of this domain
650    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
651        if let Some(gd) = self.as_ground() {
652            return gd.contains(lit);
653        }
654        Err(DomainOpError::NotGround)
655    }
656
657    pub fn element_domain(&self) -> Option<DomainPtr> {
658        match self {
659            Domain::Ground(gd) => gd.element_domain().map(DomainPtr::from),
660            Domain::Unresolved(ud) => ud.element_domain(),
661        }
662    }
663}
664
665impl Typeable for Domain {
666    fn return_type(&self) -> ReturnType {
667        match self {
668            Domain::Ground(dom) => dom.return_type(),
669            Domain::Unresolved(dom) => dom.return_type(),
670        }
671    }
672}
673
674impl Display for Domain {
675    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
676        match &self {
677            Domain::Ground(gd) => gd.fmt(f),
678            Domain::Unresolved(ud) => ud.fmt(f),
679        }
680    }
681}
682
683fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
684    doms.iter()
685        .map(|idx| match idx.as_ref() {
686            Domain::Ground(idx_gd) => Some(idx_gd.clone()),
687            _ => None,
688        })
689        .collect()
690}
691
692#[cfg(test)]
693mod tests {
694    use super::*;
695    use crate::ast::Name;
696    use crate::{domain_int, range};
697
698    #[test]
699    fn test_negative_product() {
700        let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
701        let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
702        let res = d1
703            .as_ground()
704            .unwrap()
705            .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
706            .unwrap();
707
708        assert!(matches!(res, GroundDomain::Int(_)));
709        if let GroundDomain::Int(ranges) = res {
710            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
711        }
712    }
713
714    #[test]
715    fn test_negative_div() {
716        let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
717        let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
718        let res = d1
719            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
720            .unwrap();
721
722        assert!(matches!(res, GroundDomain::Int(_)));
723        if let GroundDomain::Int(ranges) = res {
724            assert!(!ranges.contains(&Range::Bounded(-4, 4)));
725        }
726    }
727
728    #[test]
729    fn test_length_basic() {
730        assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
731        assert_eq!(Domain::bool().length(), Ok(2));
732        assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
733        assert_eq!(
734            domain_int!(1..2, 5..).length(),
735            Err(DomainOpError::Unbounded)
736        );
737    }
738    #[test]
739    fn test_length_set_basic() {
740        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
741        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..3));
742        assert_eq!(s.length(), Ok(8));
743
744        // {{1,2}, {1,3}, {2,3}}
745        let s = Domain::set(SetAttr::new_size(2), domain_int!(1..3));
746        assert_eq!(s.length(), Ok(3));
747
748        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
749        let s = Domain::set(SetAttr::new_min_max_size(1, 2), domain_int!(1..3));
750        assert_eq!(s.length(), Ok(6));
751
752        // {{1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
753        let s = Domain::set(SetAttr::new_min_size(1), domain_int!(1..3));
754        assert_eq!(s.length(), Ok(7));
755
756        // {∅, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}}
757        let s = Domain::set(SetAttr::new_max_size(2), domain_int!(1..3));
758        assert_eq!(s.length(), Ok(7));
759    }
760
761    #[test]
762    fn test_length_set_nested() {
763        // {
764        // ∅,                                          -- all size 0
765        // {∅}, {{1}}, {{2}}, {{1, 2}},                -- all size 1
766        // {∅, {1}}, {∅, {2}}, {∅, {1, 2}},            -- all size 2
767        // {{1}, {2}}, {{1}, {1, 2}}, {{2}, {1, 2}}
768        // }
769        let s2 = Domain::set(
770            SetAttr::new_max_size(2),
771            // {∅, {1}, {2}, {1,2}}
772            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
773        );
774        assert_eq!(s2.length(), Ok(11));
775    }
776
777    #[test]
778    fn test_length_set_unbounded_inner() {
779        // leaf domain is unbounded
780        let s2_bad = Domain::set(
781            SetAttr::new_max_size(2),
782            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..)),
783        );
784        assert_eq!(s2_bad.length(), Err(DomainOpError::Unbounded));
785    }
786
787    #[test]
788    fn test_length_set_overflow() {
789        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..20));
790        assert!(s.length().is_ok());
791
792        // current way of calculating the formula overflows for anything larger than this
793        let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..63));
794        assert_eq!(s.length(), Err(DomainOpError::TooLarge));
795    }
796
797    #[test]
798    fn test_length_tuple() {
799        // 3 ways to pick first element, 2 ways to pick second element
800        let t = Domain::tuple(vec![domain_int!(1..3), Domain::bool()]);
801        assert_eq!(t.length(), Ok(6));
802    }
803
804    #[test]
805    fn test_length_record() {
806        // 3 ways to pick rec.a, 2 ways to pick rec.b
807        let t = Domain::record(vec![
808            RecordEntry {
809                name: Name::user("a"),
810                domain: domain_int!(1..3),
811            },
812            RecordEntry {
813                name: Name::user("b"),
814                domain: Domain::bool(),
815            },
816        ]);
817        assert_eq!(t.length(), Ok(6));
818    }
819
820    #[test]
821    fn test_length_matrix_basic() {
822        // 3 booleans -> [T, T, T], [T, T, F], ..., [F, F, F]
823        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..3)]);
824        assert_eq!(m.length(), Ok(8));
825
826        // 2 numbers, each 1..3 -> 3*3 options
827        let m = Domain::matrix(domain_int!(1..3), vec![domain_int!(1..2)]);
828        assert_eq!(m.length(), Ok(9));
829    }
830
831    #[test]
832    fn test_length_matrix_2d() {
833        // 2x3 matrix of booleans -> (2**2)**3 = 64 options
834        let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..2), domain_int!(1..3)]);
835        assert_eq!(m.length(), Ok(64));
836    }
837
838    #[test]
839    fn test_length_matrix_of_sets() {
840        // 3 sets drawn from 1..2; 4**3 = 64 total options
841        let m = Domain::matrix(
842            Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
843            vec![domain_int!(1..3)],
844        );
845        assert_eq!(m.length(), Ok(64));
846    }
847}