1use crate::ast::domains::MSetAttr;
2use crate::ast::pretty::pretty_vec;
3use crate::ast::{
4 AbstractLiteral, Domain, DomainOpError, FuncAttr, HasDomain, Literal, Moo, RecordEntry,
5 SetAttr, Typeable,
6 domains::{domain::Int, range::Range},
7};
8use crate::range;
9use crate::utils::count_combinations;
10use conjure_cp_core::ast::{Name, ReturnType};
11use itertools::{Itertools, izip};
12use num_traits::ToPrimitive;
13use polyquine::Quine;
14use serde::{Deserialize, Serialize};
15use std::collections::BTreeSet;
16use std::fmt::{Display, Formatter};
17use std::iter::zip;
18use uniplate::Uniplate;
19
20#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Uniplate, Quine)]
21#[path_prefix(conjure_cp::ast)]
22pub struct RecordEntryGround {
23 pub name: Name,
24 pub domain: Moo<GroundDomain>,
25}
26
27impl From<RecordEntryGround> for RecordEntry {
28 fn from(value: RecordEntryGround) -> Self {
29 RecordEntry {
30 name: value.name,
31 domain: value.domain.into(),
32 }
33 }
34}
35
36impl TryFrom<RecordEntry> for RecordEntryGround {
37 type Error = DomainOpError;
38
39 fn try_from(value: RecordEntry) -> Result<Self, Self::Error> {
40 match value.domain.as_ref() {
41 Domain::Ground(gd) => Ok(RecordEntryGround {
42 name: value.name,
43 domain: gd.clone(),
44 }),
45 Domain::Unresolved(_) => Err(DomainOpError::NotGround),
46 }
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
51#[path_prefix(conjure_cp::ast)]
52pub enum GroundDomain {
53 Empty(ReturnType),
55 Bool,
57 Int(Vec<Range<Int>>),
59 Set(SetAttr<Int>, Moo<GroundDomain>),
61 MSet(MSetAttr<Int>, Moo<GroundDomain>),
63 Matrix(Moo<GroundDomain>, Vec<Moo<GroundDomain>>),
66 Tuple(Vec<Moo<GroundDomain>>),
68 Record(Vec<RecordEntryGround>),
70 Function(FuncAttr, Moo<GroundDomain>, Moo<GroundDomain>),
72}
73
74impl GroundDomain {
75 pub fn union(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
76 match (self, other) {
77 (GroundDomain::Empty(ty), dom) | (dom, GroundDomain::Empty(ty)) => {
78 if *ty == dom.return_type() {
79 Ok(dom.clone())
80 } else {
81 Err(DomainOpError::WrongType)
82 }
83 }
84 (GroundDomain::Bool, GroundDomain::Bool) => Ok(GroundDomain::Bool),
85 (GroundDomain::Bool, _) | (_, GroundDomain::Bool) => Err(DomainOpError::WrongType),
86 (GroundDomain::Int(r1), GroundDomain::Int(r2)) => {
87 let mut rngs = r1.clone();
88 rngs.extend(r2.clone());
89 Ok(GroundDomain::Int(Range::squeeze(&rngs)))
90 }
91 (GroundDomain::Int(_), _) | (_, GroundDomain::Int(_)) => Err(DomainOpError::WrongType),
92 (GroundDomain::Set(_, in1), GroundDomain::Set(_, in2)) => Ok(GroundDomain::Set(
93 SetAttr::default(),
94 Moo::new(in1.union(in2)?),
95 )),
96 (GroundDomain::Set(_, _), _) | (_, GroundDomain::Set(_, _)) => {
97 Err(DomainOpError::WrongType)
98 }
99 (GroundDomain::MSet(_, in1), GroundDomain::MSet(_, in2)) => Ok(GroundDomain::MSet(
100 MSetAttr::default(),
101 Moo::new(in1.union(in2)?),
102 )),
103 (GroundDomain::Matrix(in1, idx1), GroundDomain::Matrix(in2, idx2)) if idx1 == idx2 => {
104 Ok(GroundDomain::Matrix(
105 Moo::new(in1.union(in2)?),
106 idx1.clone(),
107 ))
108 }
109 (GroundDomain::Matrix(_, _), _) | (_, GroundDomain::Matrix(_, _)) => {
110 Err(DomainOpError::WrongType)
111 }
112 (GroundDomain::Tuple(in1s), GroundDomain::Tuple(in2s)) if in1s.len() == in2s.len() => {
113 let mut inners = Vec::new();
114 for (in1, in2) in zip(in1s, in2s) {
115 inners.push(Moo::new(in1.union(in2)?));
116 }
117 Ok(GroundDomain::Tuple(inners))
118 }
119 (GroundDomain::Tuple(_), _) | (_, GroundDomain::Tuple(_)) => {
120 Err(DomainOpError::WrongType)
121 }
122 #[allow(unreachable_patterns)]
124 (GroundDomain::Record(_), _) | (_, GroundDomain::Record(_)) => {
126 Err(DomainOpError::WrongType)
127 }
128 #[allow(unreachable_patterns)]
129 (GroundDomain::Function(_, _, _), _) | (_, GroundDomain::Function(_, _, _)) => {
131 Err(DomainOpError::WrongType)
132 }
133 }
134 }
135
136 pub fn intersect(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
143 match (self, other) {
147 (d @ GroundDomain::Empty(ReturnType::Int), GroundDomain::Int(_)) => Ok(d.clone()),
149 (GroundDomain::Int(_), d @ GroundDomain::Empty(ReturnType::Int)) => Ok(d.clone()),
150 (GroundDomain::Empty(ReturnType::Int), d @ GroundDomain::Empty(ReturnType::Int)) => {
151 Ok(d.clone())
152 }
153
154 (GroundDomain::Set(_, inner1), d @ GroundDomain::Empty(ReturnType::Set(inner2)))
156 if matches!(
157 **inner1,
158 GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
159 ) && matches!(**inner2, ReturnType::Int) =>
160 {
161 Ok(d.clone())
162 }
163 (d @ GroundDomain::Empty(ReturnType::Set(inner1)), GroundDomain::Set(_, inner2))
164 if matches!(**inner1, ReturnType::Int)
165 && matches!(
166 **inner2,
167 GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
168 ) =>
169 {
170 Ok(d.clone())
171 }
172 (
173 d @ GroundDomain::Empty(ReturnType::Set(inner1)),
174 GroundDomain::Empty(ReturnType::Set(inner2)),
175 ) if matches!(**inner1, ReturnType::Int) && matches!(**inner2, ReturnType::Int) => {
176 Ok(d.clone())
177 }
178
179 (GroundDomain::Set(_, x), GroundDomain::Set(_, y)) => Ok(GroundDomain::Set(
181 SetAttr::default(),
182 Moo::new((*x).intersect(y)?),
183 )),
184
185 (GroundDomain::Int(_), GroundDomain::Int(_)) => {
186 let mut v: BTreeSet<i32> = BTreeSet::new();
187
188 let v1 = self.values_i32()?;
189 let v2 = other.values_i32()?;
190 for value1 in v1.iter() {
191 if v2.contains(value1) && !v.contains(value1) {
192 v.insert(*value1);
193 }
194 }
195 Ok(GroundDomain::from_set_i32(&v))
196 }
197 _ => Err(DomainOpError::WrongType),
198 }
199 }
200
201 pub fn values(&self) -> Result<Box<dyn Iterator<Item = Literal>>, DomainOpError> {
202 match self {
203 GroundDomain::Empty(_) => Ok(Box::new(vec![].into_iter())),
204 GroundDomain::Bool => Ok(Box::new(
205 vec![Literal::from(true), Literal::from(false)].into_iter(),
206 )),
207 GroundDomain::Int(rngs) => {
208 let rng_iters = rngs
209 .iter()
210 .map(Range::iter)
211 .collect::<Option<Vec<_>>>()
212 .ok_or(DomainOpError::Unbounded)?;
213 Ok(Box::new(
214 rng_iters.into_iter().flat_map(|ri| ri.map(Literal::from)),
215 ))
216 }
217 _ => todo!("Enumerating nested domains is not yet supported"),
218 }
219 }
220
221 pub fn length(&self) -> Result<u64, DomainOpError> {
227 match self {
228 GroundDomain::Empty(_) => Ok(0),
229 GroundDomain::Bool => Ok(2),
230 GroundDomain::Int(ranges) => {
231 if ranges.is_empty() {
232 return Err(DomainOpError::Unbounded);
233 }
234
235 let mut length = 0u64;
236 for range in ranges {
237 if let Some(range_length) = range.length() {
238 length += range_length as u64;
239 } else {
240 return Err(DomainOpError::Unbounded);
241 }
242 }
243 Ok(length)
244 }
245 GroundDomain::Set(set_attr, inner_domain) => {
246 let inner_len = inner_domain.length()?;
247 let (min_sz, max_sz) = match set_attr.size {
248 Range::Unbounded => (0, inner_len),
249 Range::Single(n) => (n as u64, n as u64),
250 Range::UnboundedR(n) => (n as u64, inner_len),
251 Range::UnboundedL(n) => (0, n as u64),
252 Range::Bounded(min, max) => (min as u64, max as u64),
253 };
254 let mut ans = 0u64;
255 for sz in min_sz..=max_sz {
256 let c = count_combinations(inner_len, sz)?;
257 ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
258 }
259 Ok(ans)
260 }
261 GroundDomain::MSet(mset_attr, inner_domain) => {
262 let inner_len = inner_domain.length()?;
263 let (min_sz, max_sz) = match mset_attr.size {
264 Range::Unbounded => (0, inner_len),
265 Range::Single(n) => (n as u64, n as u64),
266 Range::UnboundedR(n) => (n as u64, inner_len),
267 Range::UnboundedL(n) => (0, n as u64),
268 Range::Bounded(min, max) => (min as u64, max as u64),
269 };
270 let mut ans = 0u64;
271 for sz in min_sz..=max_sz {
272 let c = count_combinations(inner_len + sz - 1, sz)?;
275 ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
276 }
277 Ok(ans)
278 }
279 GroundDomain::Tuple(domains) => {
280 let mut ans = 1u64;
281 for domain in domains {
282 ans = ans
283 .checked_mul(domain.length()?)
284 .ok_or(DomainOpError::TooLarge)?;
285 }
286 Ok(ans)
287 }
288 GroundDomain::Record(entries) => {
289 let mut ans = 1u64;
291 for entry in entries {
292 let sz = entry.domain.length()?;
293 ans = ans.checked_mul(sz).ok_or(DomainOpError::TooLarge)?;
294 }
295 Ok(ans)
296 }
297 GroundDomain::Matrix(inner_domain, idx_domains) => {
298 let inner_sz = inner_domain.length()?;
299 let exp = idx_domains.iter().try_fold(1u32, |acc, val| {
300 let len = val.length()? as u32;
301 acc.checked_mul(len).ok_or(DomainOpError::TooLarge)
302 })?;
303 inner_sz.checked_pow(exp).ok_or(DomainOpError::TooLarge)
304 }
305 GroundDomain::Function(_, _, _) => {
306 todo!("Length bound of functions is not yet supported")
307 }
308 }
309 }
310
311 pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
312 match self {
315 GroundDomain::Empty(_) => Ok(false),
317 GroundDomain::Bool => match lit {
318 Literal::Bool(_) => Ok(true),
319 _ => Ok(false),
320 },
321 GroundDomain::Int(ranges) => match lit {
322 Literal::Int(x) => {
323 if ranges.is_empty() {
325 return Ok(true);
326 };
327
328 Ok(ranges.iter().any(|range| range.contains(x)))
329 }
330 _ => Ok(false),
331 },
332 GroundDomain::Set(set_attr, inner_domain) => match lit {
333 Literal::AbstractLiteral(AbstractLiteral::Set(lit_elems)) => {
334 let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
336 if !set_attr.size.contains(&sz) {
337 return Ok(false);
338 }
339
340 for elem in lit_elems {
341 if !inner_domain.contains(elem)? {
342 return Ok(false);
343 }
344 }
345 Ok(true)
346 }
347 _ => Ok(false),
348 },
349 GroundDomain::MSet(mset_attr, inner_domain) => match lit {
350 Literal::AbstractLiteral(AbstractLiteral::MSet(lit_elems)) => {
351 let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
353 if !mset_attr.size.contains(&sz) {
354 return Ok(false);
355 }
356
357 for elem in lit_elems {
358 if !inner_domain.contains(elem)? {
359 return Ok(false);
360 }
361 }
362 Ok(true)
363 }
364 _ => Ok(false),
365 },
366 GroundDomain::Matrix(elem_domain, index_domains) => {
367 match lit {
368 Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx_domain)) => {
369 let mut index_domains = index_domains.clone();
373 if index_domains
374 .pop()
375 .expect("a matrix should have at least one index domain")
376 != *idx_domain
377 {
378 return Ok(false);
379 };
380
381 let next_elem_domain = if index_domains.is_empty() {
382 elem_domain.as_ref().clone()
385 } else {
386 GroundDomain::Matrix(elem_domain.clone(), index_domains)
388 };
389
390 for elem in elems {
391 if !next_elem_domain.contains(elem)? {
392 return Ok(false);
393 }
394 }
395
396 Ok(true)
397 }
398 _ => Ok(false),
399 }
400 }
401 GroundDomain::Tuple(elem_domains) => {
402 match lit {
403 Literal::AbstractLiteral(AbstractLiteral::Tuple(literal_elems)) => {
404 if elem_domains.len() != literal_elems.len() {
405 return Ok(false);
406 }
407
408 for (elem_domain, elem) in itertools::izip!(elem_domains, literal_elems) {
410 if !elem_domain.contains(elem)? {
411 return Ok(false);
412 }
413 }
414
415 Ok(true)
416 }
417 _ => Ok(false),
418 }
419 }
420 GroundDomain::Record(entries) => match lit {
421 Literal::AbstractLiteral(AbstractLiteral::Record(lit_entries)) => {
422 if entries.len() != lit_entries.len() {
423 return Ok(false);
424 }
425
426 for (entry, lit_entry) in itertools::izip!(entries, lit_entries) {
427 if entry.name != lit_entry.name
428 || !(entry.domain.contains(&lit_entry.value)?)
429 {
430 return Ok(false);
431 }
432 }
433 Ok(true)
434 }
435 _ => Ok(false),
436 },
437 GroundDomain::Function(func_attr, domain, codomain) => match lit {
438 Literal::AbstractLiteral(AbstractLiteral::Function(lit_elems)) => {
439 let sz = Int::try_from(lit_elems.len()).expect("Should convert");
440 if !func_attr.size.contains(&sz) {
441 return Ok(false);
442 }
443 for lit in lit_elems {
444 let domain_element = &lit.0;
445 let codomain_element = &lit.1;
446 if !domain.contains(domain_element)? {
447 return Ok(false);
448 }
449 if !codomain.contains(codomain_element)? {
450 return Ok(false);
451 }
452 }
453 Ok(true)
454 }
455 _ => Ok(false),
456 },
457 }
458 }
459
460 pub fn values_i32(&self) -> Result<Vec<i32>, DomainOpError> {
467 if let GroundDomain::Empty(ReturnType::Int) = self {
468 return Ok(vec![]);
469 }
470 let GroundDomain::Int(ranges) = self else {
471 return Err(DomainOpError::NotInteger(self.return_type()));
472 };
473
474 if ranges.is_empty() {
475 return Err(DomainOpError::Unbounded);
476 }
477
478 let mut values = vec![];
479 for range in ranges {
480 match range {
481 Range::Single(i) => {
482 values.push(*i);
483 }
484 Range::Bounded(i, j) => {
485 values.extend(*i..=*j);
486 }
487 Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => {
488 return Err(DomainOpError::Unbounded);
489 }
490 }
491 }
492
493 Ok(values)
494 }
495
496 pub fn from_set_i32(elements: &BTreeSet<i32>) -> GroundDomain {
535 if elements.is_empty() {
536 return GroundDomain::Empty(ReturnType::Int);
537 }
538 if elements.len() == 1 {
539 return GroundDomain::Int(vec![Range::Single(*elements.first().unwrap())]);
540 }
541
542 let mut elems_iter = elements.iter().copied();
543
544 let mut ranges: Vec<Range<i32>> = vec![];
545
546 let mut lower = elems_iter
551 .next()
552 .expect("if we get here, elements should have => 2 elements");
553 let mut upper = lower;
554
555 for current in elems_iter {
556 if current == upper + 1 {
559 upper = current;
562 } else {
563 if lower == upper {
568 ranges.push(range!(lower));
569 } else {
570 ranges.push(range!(lower..upper));
571 }
572
573 lower = current;
574 upper = current;
575 }
576 }
577
578 if lower == upper {
580 ranges.push(range!(lower));
581 } else {
582 ranges.push(range!(lower..upper));
583 }
584
585 ranges = Range::squeeze(&ranges);
586 GroundDomain::Int(ranges)
587 }
588
589 pub fn apply_i32(
599 &self,
600 op: fn(i32, i32) -> Option<i32>,
601 other: &GroundDomain,
602 ) -> Result<GroundDomain, DomainOpError> {
603 let vs1 = self.values_i32()?;
604 let vs2 = other.values_i32()?;
605
606 let mut set = BTreeSet::new();
607 for (v1, v2) in itertools::iproduct!(vs1, vs2) {
608 if let Some(v) = op(v1, v2) {
609 set.insert(v);
610 }
611 }
612
613 Ok(GroundDomain::from_set_i32(&set))
614 }
615
616 pub fn is_finite(&self) -> bool {
618 for domain in self.universe() {
619 if let GroundDomain::Int(ranges) = domain {
620 if ranges.is_empty() {
621 return false;
622 }
623
624 if ranges.iter().any(|range| {
625 matches!(
626 range,
627 Range::UnboundedL(_) | Range::UnboundedR(_) | Range::Unbounded
628 )
629 }) {
630 return false;
631 }
632 }
633 }
634 true
635 }
636
637 pub fn from_literal_vec(literals: &[Literal]) -> Result<GroundDomain, DomainOpError> {
718 if literals.is_empty() {
721 return Ok(GroundDomain::Empty(ReturnType::Unknown));
722 }
723
724 let first_literal = literals.first().unwrap();
725
726 match first_literal {
727 Literal::Int(_) => {
728 let mut ints = BTreeSet::new();
730 for lit in literals {
731 let Literal::Int(i) = lit else {
732 return Err(DomainOpError::WrongType);
733 };
734
735 ints.insert(*i);
736 }
737
738 Ok(GroundDomain::from_set_i32(&ints))
739 }
740 Literal::Bool(_) => {
741 if literals.iter().any(|x| !matches!(x, Literal::Bool(_))) {
743 Err(DomainOpError::WrongType)
744 } else {
745 Ok(GroundDomain::Bool)
746 }
747 }
748 Literal::AbstractLiteral(AbstractLiteral::Set(_)) => {
749 let mut all_elems = vec![];
750
751 for lit in literals {
752 let Literal::AbstractLiteral(AbstractLiteral::Set(elems)) = lit else {
753 return Err(DomainOpError::WrongType);
754 };
755
756 all_elems.extend(elems.clone());
757 }
758 let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
759
760 Ok(GroundDomain::Set(SetAttr::default(), Moo::new(elem_domain)))
761 }
762 Literal::AbstractLiteral(AbstractLiteral::MSet(_)) => {
763 let mut all_elems = vec![];
764
765 for lit in literals {
766 let Literal::AbstractLiteral(AbstractLiteral::MSet(elems)) = lit else {
767 return Err(DomainOpError::WrongType);
768 };
769
770 all_elems.extend(elems.clone());
771 }
772 let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
773
774 Ok(GroundDomain::MSet(
775 MSetAttr::default(),
776 Moo::new(elem_domain),
777 ))
778 }
779 l @ Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _)) => {
780 let mut first_index_domain = vec![];
781 let mut l = l.clone();
783 while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
784 assert!(
785 !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
786 "n-dimensional matrix literals should be represented as a matrix inside a matrix"
787 );
788 first_index_domain.push(idx);
789 l = elems[0].clone();
790 }
791
792 let mut all_elems: Vec<Literal> = vec![];
793
794 for lit in literals {
796 let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = lit else {
797 return Err(DomainOpError::NotGround);
798 };
799
800 all_elems.extend(elems.clone());
801
802 let mut index_domain = vec![idx.clone()];
803 let mut l = elems[0].clone();
804 while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
805 assert!(
806 !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
807 "n-dimensional matrix literals should be represented as a matrix inside a matrix"
808 );
809 index_domain.push(idx);
810 l = elems[0].clone();
811 }
812
813 if index_domain != first_index_domain {
814 return Err(DomainOpError::WrongType);
815 }
816 }
817
818 let mut terminal_elements: Vec<Literal> = vec![];
820 while let Some(elem) = all_elems.pop() {
821 if let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, _)) = elem {
822 all_elems.extend(elems);
823 } else {
824 terminal_elements.push(elem);
825 }
826 }
827
828 let element_domain = GroundDomain::from_literal_vec(&terminal_elements)?;
829
830 Ok(GroundDomain::Matrix(
831 Moo::new(element_domain),
832 first_index_domain,
833 ))
834 }
835
836 Literal::AbstractLiteral(AbstractLiteral::Tuple(first_elems)) => {
837 let n_fields = first_elems.len();
838
839 let mut elem_domains = vec![];
841
842 for i in 0..n_fields {
843 let mut all_elems = vec![];
844 for lit in literals {
845 let Literal::AbstractLiteral(AbstractLiteral::Tuple(elems)) = lit else {
846 return Err(DomainOpError::NotGround);
847 };
848
849 if elems.len() != n_fields {
850 return Err(DomainOpError::NotGround);
851 }
852
853 all_elems.push(elems[i].clone());
854 }
855
856 elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
857 }
858
859 Ok(GroundDomain::Tuple(elem_domains))
860 }
861
862 Literal::AbstractLiteral(AbstractLiteral::Record(first_elems)) => {
863 let n_fields = first_elems.len();
864 let field_names = first_elems.iter().map(|x| x.name.clone()).collect_vec();
865
866 let mut elem_domains = vec![];
868
869 for i in 0..n_fields {
870 let mut all_elems = vec![];
871 for lit in literals {
872 let Literal::AbstractLiteral(AbstractLiteral::Record(elems)) = lit else {
873 return Err(DomainOpError::NotGround);
874 };
875
876 if elems.len() != n_fields {
877 return Err(DomainOpError::NotGround);
878 }
879
880 let elem = elems[i].clone();
881 if elem.name != field_names[i] {
882 return Err(DomainOpError::NotGround);
883 }
884
885 all_elems.push(elem.value);
886 }
887
888 elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
889 }
890
891 Ok(GroundDomain::Record(
892 izip!(field_names, elem_domains)
893 .map(|(name, domain)| RecordEntryGround { name, domain })
894 .collect(),
895 ))
896 }
897 Literal::AbstractLiteral(AbstractLiteral::Function(items)) => {
898 if items.is_empty() {
899 return Err(DomainOpError::NotGround);
900 }
901
902 let (x1, y1) = &items[0];
903 let d1 = x1.domain_of();
904 let d1 = d1.as_ground().ok_or(DomainOpError::NotGround)?;
905 let d2 = y1.domain_of();
906 let d2 = d2.as_ground().ok_or(DomainOpError::NotGround)?;
907
908 for (x, y) in items {
910 let dx = x.domain_of();
911 let dx = dx.as_ground().ok_or(DomainOpError::NotGround)?;
912
913 let dy = y.domain_of();
914 let dy = dy.as_ground().ok_or(DomainOpError::NotGround)?;
915
916 if (dx != d1) || (dy != d2) {
917 return Err(DomainOpError::WrongType);
918 }
919 }
920
921 todo!();
922 }
923 }
924 }
925
926 pub fn element_domain(&self) -> Option<Moo<GroundDomain>> {
927 match self {
928 GroundDomain::Set(_, inner) => Some(inner.clone()),
929 GroundDomain::MSet(_, inner) => Some(inner.clone()),
930 GroundDomain::Matrix(_, _) => todo!("Unwrap one dimension of the domain"),
931 _ => None,
932 }
933 }
934}
935
936impl Typeable for GroundDomain {
937 fn return_type(&self) -> ReturnType {
938 match self {
939 GroundDomain::Empty(ty) => ty.clone(),
940 GroundDomain::Bool => ReturnType::Bool,
941 GroundDomain::Int(_) => ReturnType::Int,
942 GroundDomain::Set(_attr, inner) => ReturnType::Set(Box::new(inner.return_type())),
943 GroundDomain::MSet(_attr, inner) => ReturnType::MSet(Box::new(inner.return_type())),
944 GroundDomain::Matrix(inner, _idx) => ReturnType::Matrix(Box::new(inner.return_type())),
945 GroundDomain::Tuple(inners) => {
946 let mut inner_types = Vec::new();
947 for inner in inners {
948 inner_types.push(inner.return_type());
949 }
950 ReturnType::Tuple(inner_types)
951 }
952 GroundDomain::Record(entries) => {
953 let mut entry_types = Vec::new();
954 for entry in entries {
955 entry_types.push(entry.domain.return_type());
956 }
957 ReturnType::Record(entry_types)
958 }
959 GroundDomain::Function(_, dom, cdom) => {
960 ReturnType::Function(Box::new(dom.return_type()), Box::new(cdom.return_type()))
961 }
962 }
963 }
964}
965
966impl Display for GroundDomain {
967 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
968 match &self {
969 GroundDomain::Empty(ty) => write!(f, "empty({ty:?})"),
970 GroundDomain::Bool => write!(f, "bool"),
971 GroundDomain::Int(ranges) => {
972 if ranges.iter().all(Range::is_lower_or_upper_bounded) {
973 let rngs: String = ranges.iter().map(|r| format!("{r}")).join(", ");
974 write!(f, "int({})", rngs)
975 } else {
976 write!(f, "int")
977 }
978 }
979 GroundDomain::Set(attrs, inner_dom) => write!(f, "set {attrs} of {inner_dom}"),
980 GroundDomain::MSet(attrs, inner_dom) => write!(f, "mset {attrs} of {inner_dom}"),
981 GroundDomain::Matrix(value_domain, index_domains) => {
982 write!(
983 f,
984 "matrix indexed by [{}] of {value_domain}",
985 pretty_vec(&index_domains.iter().collect_vec())
986 )
987 }
988 GroundDomain::Tuple(domains) => {
989 write!(
990 f,
991 "tuple of ({})",
992 pretty_vec(&domains.iter().collect_vec())
993 )
994 }
995 GroundDomain::Record(entries) => {
996 write!(
997 f,
998 "record of ({})",
999 pretty_vec(
1000 &entries
1001 .iter()
1002 .map(|entry| format!("{}: {}", entry.name, entry.domain))
1003 .collect_vec()
1004 )
1005 )
1006 }
1007 GroundDomain::Function(attribute, domain, codomain) => {
1008 write!(f, "function {} {} --> {} ", attribute, domain, codomain)
1009 }
1010 }
1011 }
1012}