1use super::attrs::SetAttr;
2use super::ground::GroundDomain;
3use super::range::Range;
4use super::unresolved::{IntVal, UnresolvedDomain};
5use crate::ast::{
6 DeclarationPtr, DomainOpError, Expression, FuncAttr, Literal, Moo, RecordEntry,
7 RecordEntryGround, Reference, ReturnType, Typeable,
8};
9use itertools::Itertools;
10use polyquine::Quine;
11use serde::{Deserialize, Serialize};
12use std::fmt::{Display, Formatter};
13use std::thread_local;
14use uniplate::Uniplate;
15
16pub type Int = i32;
18
19pub type DomainPtr = Moo<Domain>;
20
21impl DomainPtr {
22 pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
23 self.as_ref().resolve()
24 }
25
26 pub fn union(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
29 self.as_ref().union(other.as_ref()).map(DomainPtr::new)
30 }
31
32 pub fn intersect(&self, other: &DomainPtr) -> Result<DomainPtr, DomainOpError> {
35 self.as_ref().intersect(other.as_ref()).map(DomainPtr::new)
36 }
37}
38
39impl From<Moo<GroundDomain>> for DomainPtr {
40 fn from(value: Moo<GroundDomain>) -> Self {
41 Moo::new(Domain::Ground(value))
42 }
43}
44
45impl From<Moo<UnresolvedDomain>> for DomainPtr {
46 fn from(value: Moo<UnresolvedDomain>) -> Self {
47 Moo::new(Domain::Unresolved(value))
48 }
49}
50
51impl From<GroundDomain> for DomainPtr {
52 fn from(value: GroundDomain) -> Self {
53 Moo::new(Domain::Ground(Moo::new(value)))
54 }
55}
56
57impl From<UnresolvedDomain> for DomainPtr {
58 fn from(value: UnresolvedDomain) -> Self {
59 Moo::new(Domain::Unresolved(Moo::new(value)))
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
64#[biplate(to=DomainPtr)]
65#[biplate(to=GroundDomain)]
66#[biplate(to=UnresolvedDomain)]
67#[biplate(to=Expression)]
68#[biplate(to=Reference)]
69#[biplate(to=RecordEntry)]
70#[biplate(to=IntVal)]
71#[path_prefix(conjure_cp::ast)]
72pub enum Domain {
73 Ground(Moo<GroundDomain>),
75 Unresolved(Moo<UnresolvedDomain>),
77}
78
79pub trait HasDomain {
81 fn domain_of(&self) -> DomainPtr;
83}
84
85impl<T: HasDomain> Typeable for T {
86 fn return_type(&self) -> ReturnType {
87 self.domain_of().return_type()
88 }
89}
90
91thread_local! {
94 static BOOL_DOMAIN: DomainPtr =
95 Moo::new(Domain::Ground(Moo::new(GroundDomain::Bool)));
96}
97
98impl Domain {
99 pub fn bool() -> DomainPtr {
102 BOOL_DOMAIN.with(Clone::clone)
103 }
104
105 pub fn empty(ty: ReturnType) -> DomainPtr {
108 Moo::new(Domain::Ground(Moo::new(GroundDomain::Empty(ty))))
109 }
110
111 pub fn int<T>(ranges: Vec<T>) -> DomainPtr
115 where
116 T: Into<Range<IntVal>> + TryInto<Range<Int>> + Clone,
117 {
118 if let Ok(int_rngs) = ranges
119 .iter()
120 .cloned()
121 .map(TryInto::try_into)
122 .collect::<Result<Vec<_>, _>>()
123 {
124 return Domain::int_ground(int_rngs);
125 }
126 let unresolved_rngs: Vec<Range<IntVal>> = ranges.into_iter().map(Into::into).collect();
127 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Int(
128 unresolved_rngs,
129 ))))
130 }
131
132 pub fn int_ground(ranges: Vec<Range<Int>>) -> DomainPtr {
134 let rngs = Range::squeeze(&ranges);
135 Moo::new(Domain::Ground(Moo::new(GroundDomain::Int(rngs))))
136 }
137
138 pub fn set<T>(attr: T, inner_dom: DomainPtr) -> DomainPtr
142 where
143 T: Into<SetAttr<IntVal>> + TryInto<SetAttr<Int>> + Clone,
144 {
145 if let Domain::Ground(gd) = inner_dom.as_ref()
146 && let Ok(int_attr) = attr.clone().try_into()
147 {
148 return Moo::new(Domain::Ground(Moo::new(GroundDomain::Set(
149 int_attr,
150 gd.clone(),
151 ))));
152 }
153 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Set(
154 attr.into(),
155 inner_dom,
156 ))))
157 }
158
159 pub fn matrix(inner_dom: DomainPtr, idx_doms: Vec<DomainPtr>) -> DomainPtr {
163 if let Domain::Ground(gd) = inner_dom.as_ref()
164 && let Some(idx_gds) = as_grounds(&idx_doms)
165 {
166 return Moo::new(Domain::Ground(Moo::new(GroundDomain::Matrix(
167 gd.clone(),
168 idx_gds,
169 ))));
170 }
171 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(
172 inner_dom, idx_doms,
173 ))))
174 }
175
176 pub fn tuple(inner_doms: Vec<DomainPtr>) -> DomainPtr {
180 if let Some(inner_gds) = as_grounds(&inner_doms) {
181 return Moo::new(Domain::Ground(Moo::new(GroundDomain::Tuple(inner_gds))));
182 }
183 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(
184 inner_doms,
185 ))))
186 }
187
188 pub fn record(entries: Vec<RecordEntry>) -> DomainPtr {
192 if let Ok(entries_gds) = entries.iter().cloned().map(TryInto::try_into).try_collect() {
193 return Moo::new(Domain::Ground(Moo::new(GroundDomain::Record(entries_gds))));
194 }
195 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Record(
196 entries,
197 ))))
198 }
199
200 pub fn reference(ptr: DeclarationPtr) -> Option<DomainPtr> {
202 ptr.as_domain_letting()?;
203 Some(Moo::new(Domain::Unresolved(Moo::new(
204 UnresolvedDomain::Reference(Reference::new(ptr)),
205 ))))
206 }
207
208 pub fn function<T>(attrs: T, dom: DomainPtr, cdom: DomainPtr) -> DomainPtr
210 where
211 T: Into<FuncAttr<IntVal>> + TryInto<FuncAttr<Int>> + Clone,
212 {
213 if let Ok(attrs_gd) = attrs.clone().try_into()
214 && let Some(dom_gd) = dom.as_ground()
215 && let Some(cdom_gd) = cdom.as_ground()
216 {
217 return Moo::new(Domain::Ground(Moo::new(GroundDomain::Function(
218 attrs_gd,
219 Moo::new(dom_gd.clone()),
220 Moo::new(cdom_gd.clone()),
221 ))));
222 }
223
224 Moo::new(Domain::Unresolved(Moo::new(UnresolvedDomain::Function(
225 attrs.into(),
226 dom,
227 cdom,
228 ))))
229 }
230
231 pub fn resolve(&self) -> Option<Moo<GroundDomain>> {
236 match self {
237 Domain::Ground(gd) => Some(gd.clone()),
238 Domain::Unresolved(ud) => ud.resolve().map(Moo::new),
239 }
240 }
241
242 pub fn as_ground(&self) -> Option<&GroundDomain> {
246 match self {
247 Domain::Ground(gd) => Some(gd.as_ref()),
248 _ => None,
249 }
250 }
251
252 pub fn as_ground_mut(&mut self) -> Option<&mut GroundDomain> {
255 match self {
256 Domain::Ground(gd) => Some(Moo::<GroundDomain>::make_mut(gd)),
257 _ => None,
258 }
259 }
260
261 pub fn as_unresolved(&self) -> Option<&UnresolvedDomain> {
263 match self {
264 Domain::Unresolved(ud) => Some(ud.as_ref()),
265 _ => None,
266 }
267 }
268
269 pub fn as_unresolved_mut(&mut self) -> Option<&mut UnresolvedDomain> {
271 match self {
272 Domain::Unresolved(ud) => Some(Moo::<UnresolvedDomain>::make_mut(ud)),
273 _ => None,
274 }
275 }
276
277 pub fn as_dom_empty(&self) -> Option<&ReturnType> {
279 if let Some(GroundDomain::Empty(ty)) = self.as_ground() {
280 return Some(ty);
281 }
282 None
283 }
284
285 pub fn as_dom_empty_mut(&mut self) -> Option<&mut ReturnType> {
287 if let Some(GroundDomain::Empty(ty)) = self.as_ground_mut() {
288 return Some(ty);
289 }
290 None
291 }
292
293 pub fn is_bool(&self) -> bool {
295 self.return_type() == ReturnType::Bool
296 }
297
298 pub fn is_int(&self) -> bool {
300 self.return_type() == ReturnType::Int
301 }
302
303 pub fn as_int(&self) -> Option<Vec<Range<IntVal>>> {
306 if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
307 return Some(rngs.iter().cloned().map(|r| r.into()).collect());
308 }
309 if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved() {
310 return Some(rngs.clone());
311 }
312 None
313 }
314
315 pub fn as_int_mut(&mut self) -> Option<&mut Vec<Range<IntVal>>> {
318 if let Some(GroundDomain::Int(rngs_gds)) = self.as_ground() {
324 let rngs: Vec<Range<IntVal>> = rngs_gds.iter().cloned().map(|r| r.into()).collect();
325 *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Int(rngs)))
326 }
327
328 if let Some(UnresolvedDomain::Int(rngs)) = self.as_unresolved_mut() {
329 return Some(rngs);
330 }
331 None
332 }
333
334 pub fn as_int_ground(&self) -> Option<&Vec<Range<Int>>> {
336 if let Some(GroundDomain::Int(rngs)) = self.as_ground() {
337 return Some(rngs);
338 }
339 None
340 }
341
342 pub fn as_int_ground_mut(&mut self) -> Option<&mut Vec<Range<Int>>> {
344 if let Some(GroundDomain::Int(rngs)) = self.as_ground_mut() {
345 return Some(rngs);
346 }
347 None
348 }
349
350 pub fn as_matrix(&self) -> Option<(DomainPtr, Vec<DomainPtr>)> {
353 if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
354 let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
355 let inner_dom: DomainPtr = inner_dom_gd.clone().into();
356 return Some((inner_dom, idx_doms));
357 }
358 if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved() {
359 return Some((inner_dom.clone(), idx_doms.clone()));
360 }
361 None
362 }
363
364 pub fn as_matrix_mut(&mut self) -> Option<(&mut DomainPtr, &mut Vec<DomainPtr>)> {
368 if let Some(GroundDomain::Matrix(inner_dom_gd, idx_doms_gds)) = self.as_ground() {
371 let inner_dom: DomainPtr = inner_dom_gd.clone().into();
372 let idx_doms: Vec<DomainPtr> = idx_doms_gds.iter().cloned().map(|d| d.into()).collect();
373 *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Matrix(inner_dom, idx_doms)));
374 }
375
376 if let Some(UnresolvedDomain::Matrix(inner_dom, idx_doms)) = self.as_unresolved_mut() {
377 return Some((inner_dom, idx_doms));
378 }
379 None
380 }
381
382 pub fn as_matrix_ground(&self) -> Option<(&Moo<GroundDomain>, &Vec<Moo<GroundDomain>>)> {
384 if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground() {
385 return Some((inner_dom, idx_doms));
386 }
387 None
388 }
389
390 pub fn as_matrix_ground_mut(
392 &mut self,
393 ) -> Option<(&mut Moo<GroundDomain>, &mut Vec<Moo<GroundDomain>>)> {
394 if let Some(GroundDomain::Matrix(inner_dom, idx_doms)) = self.as_ground_mut() {
395 return Some((inner_dom, idx_doms));
396 }
397 None
398 }
399
400 pub fn as_set(&self) -> Option<(SetAttr<IntVal>, DomainPtr)> {
402 if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
403 return Some((attr.clone().into(), inner_dom.clone().into()));
404 }
405 if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved() {
406 return Some((attr.clone(), inner_dom.clone()));
407 }
408 None
409 }
410
411 pub fn as_set_mut(&mut self) -> Option<(&mut SetAttr<IntVal>, &mut DomainPtr)> {
414 if let Some(GroundDomain::Set(attr_gd, inner_dom_gd)) = self.as_ground() {
415 let attr: SetAttr<IntVal> = attr_gd.clone().into();
416 let inner_dom = inner_dom_gd.clone().into();
417 *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Set(attr, inner_dom)));
418 }
419
420 if let Some(UnresolvedDomain::Set(attr, inner_dom)) = self.as_unresolved_mut() {
421 return Some((attr, inner_dom));
422 }
423 None
424 }
425
426 pub fn as_set_ground(&self) -> Option<(&SetAttr<Int>, &Moo<GroundDomain>)> {
428 if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground() {
429 return Some((attr, inner_dom));
430 }
431 None
432 }
433
434 pub fn as_set_ground_mut(&mut self) -> Option<(&mut SetAttr<Int>, &mut Moo<GroundDomain>)> {
436 if let Some(GroundDomain::Set(attr, inner_dom)) = self.as_ground_mut() {
437 return Some((attr, inner_dom));
438 }
439 None
440 }
441
442 pub fn as_tuple(&self) -> Option<Vec<DomainPtr>> {
444 if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
445 return Some(inner_doms.iter().cloned().map(|d| d.into()).collect());
446 }
447 if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved() {
448 return Some(inner_doms.clone());
449 }
450 None
451 }
452
453 pub fn as_tuple_mut(&mut self) -> Option<&mut Vec<DomainPtr>> {
456 if let Some(GroundDomain::Tuple(inner_doms_gds)) = self.as_ground() {
457 let inner_doms: Vec<DomainPtr> =
458 inner_doms_gds.iter().cloned().map(|d| d.into()).collect();
459 *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Tuple(inner_doms)));
460 }
461
462 if let Some(UnresolvedDomain::Tuple(inner_doms)) = self.as_unresolved_mut() {
463 return Some(inner_doms);
464 }
465 None
466 }
467
468 pub fn as_tuple_ground(&self) -> Option<&Vec<Moo<GroundDomain>>> {
470 if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground() {
471 return Some(inner_doms);
472 }
473 None
474 }
475
476 pub fn as_tuple_ground_mut(&mut self) -> Option<&mut Vec<Moo<GroundDomain>>> {
478 if let Some(GroundDomain::Tuple(inner_doms)) = self.as_ground_mut() {
479 return Some(inner_doms);
480 }
481 None
482 }
483
484 pub fn as_record(&self) -> Option<Vec<RecordEntry>> {
486 if let Some(GroundDomain::Record(record_entries)) = self.as_ground() {
487 return Some(record_entries.iter().cloned().map(|r| r.into()).collect());
488 }
489 if let Some(UnresolvedDomain::Record(record_entries)) = self.as_unresolved() {
490 return Some(record_entries.clone());
491 }
492 None
493 }
494
495 pub fn as_record_ground(&self) -> Option<&Vec<RecordEntryGround>> {
497 if let Some(GroundDomain::Record(entries)) = self.as_ground() {
498 return Some(entries);
499 }
500 None
501 }
502
503 pub fn as_record_mut(&mut self) -> Option<&mut Vec<RecordEntry>> {
506 if let Some(GroundDomain::Record(entries_gds)) = self.as_ground() {
507 let entries: Vec<RecordEntry> = entries_gds.iter().cloned().map(|r| r.into()).collect();
508 *self = Domain::Unresolved(Moo::new(UnresolvedDomain::Record(entries)));
509 }
510
511 if let Some(UnresolvedDomain::Record(entries_gds)) = self.as_unresolved_mut() {
512 return Some(entries_gds);
513 }
514 None
515 }
516
517 pub fn as_record_ground_mut(&mut self) -> Option<&mut Vec<RecordEntryGround>> {
519 if let Some(GroundDomain::Record(entries)) = self.as_ground_mut() {
520 return Some(entries);
521 }
522 None
523 }
524
525 pub fn union(&self, other: &Domain) -> Result<Domain, DomainOpError> {
527 match (self, other) {
528 (Domain::Ground(a), Domain::Ground(b)) => Ok(Domain::Ground(Moo::new(a.union(b)?))),
529 (Domain::Unresolved(a), Domain::Unresolved(b)) => {
530 Ok(Domain::Unresolved(Moo::new(a.union_unresolved(b)?)))
531 }
532 (Domain::Unresolved(u), Domain::Ground(g))
533 | (Domain::Ground(g), Domain::Unresolved(u)) => {
534 todo!("Union of unresolved domain {u} and ground domain {g} is not yet implemented")
535 }
536 }
537 }
538
539 pub fn intersect(&self, other: &Domain) -> Result<Domain, DomainOpError> {
541 match (self, other) {
542 (Domain::Ground(a), Domain::Ground(b)) => {
543 a.intersect(b).map(|res| Domain::Ground(Moo::new(res)))
544 }
545 _ => Err(DomainOpError::NotGround),
546 }
547 }
548
549 pub fn values(&self) -> Result<impl Iterator<Item = Literal>, DomainOpError> {
551 if let Some(gd) = self.as_ground() {
552 return gd.values();
553 }
554 Err(DomainOpError::NotGround)
555 }
556
557 pub fn length(&self) -> Result<u64, DomainOpError> {
559 if let Some(gd) = self.as_ground() {
560 return gd.length();
561 }
562 Err(DomainOpError::NotGround)
563 }
564
565 pub fn from_literal_vec(vals: &[Literal]) -> Result<DomainPtr, DomainOpError> {
567 GroundDomain::from_literal_vec(vals).map(DomainPtr::from)
568 }
569
570 pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
572 if let Some(gd) = self.as_ground() {
573 return gd.contains(lit);
574 }
575 Err(DomainOpError::NotGround)
576 }
577}
578
579impl Typeable for Domain {
580 fn return_type(&self) -> ReturnType {
581 match self {
582 Domain::Ground(dom) => dom.return_type(),
583 Domain::Unresolved(dom) => dom.return_type(),
584 }
585 }
586}
587
588impl Display for Domain {
589 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
590 match &self {
591 Domain::Ground(gd) => gd.fmt(f),
592 Domain::Unresolved(ud) => ud.fmt(f),
593 }
594 }
595}
596
597fn as_grounds(doms: &[DomainPtr]) -> Option<Vec<Moo<GroundDomain>>> {
598 doms.iter()
599 .map(|idx| match idx.as_ref() {
600 Domain::Ground(idx_gd) => Some(idx_gd.clone()),
601 _ => None,
602 })
603 .collect()
604}
605
606#[cfg(test)]
607mod tests {
608 use super::*;
609 use crate::ast::Name;
610 use crate::{domain_int, range};
611
612 #[test]
613 fn test_negative_product() {
614 let d1 = Domain::int(vec![Range::Bounded(-2, 1)]);
615 let d2 = Domain::int(vec![Range::Bounded(-2, 1)]);
616 let res = d1
617 .as_ground()
618 .unwrap()
619 .apply_i32(|a, b| Some(a * b), d2.as_ground().unwrap())
620 .unwrap();
621
622 assert!(matches!(res, GroundDomain::Int(_)));
623 if let GroundDomain::Int(ranges) = res {
624 assert!(!ranges.contains(&Range::Bounded(-4, 4)));
625 }
626 }
627
628 #[test]
629 fn test_negative_div() {
630 let d1 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
631 let d2 = GroundDomain::Int(vec![Range::Bounded(-2, 1)]);
632 let res = d1
633 .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
634 .unwrap();
635
636 assert!(matches!(res, GroundDomain::Int(_)));
637 if let GroundDomain::Int(ranges) = res {
638 assert!(!ranges.contains(&Range::Bounded(-4, 4)));
639 }
640 }
641
642 #[test]
643 fn test_length_basic() {
644 assert_eq!(Domain::empty(ReturnType::Int).length(), Ok(0));
645 assert_eq!(Domain::bool().length(), Ok(2));
646 assert_eq!(domain_int!(1..3, 5, 7..9).length(), Ok(7));
647 assert_eq!(
648 domain_int!(1..2, 5..).length(),
649 Err(DomainOpError::Unbounded)
650 );
651 }
652 #[test]
653 fn test_length_set_basic() {
654 let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..3));
656 assert_eq!(s.length(), Ok(8));
657
658 let s = Domain::set(SetAttr::new_size(2), domain_int!(1..3));
660 assert_eq!(s.length(), Ok(3));
661
662 let s = Domain::set(SetAttr::new_min_max_size(1, 2), domain_int!(1..3));
664 assert_eq!(s.length(), Ok(6));
665
666 let s = Domain::set(SetAttr::new_min_size(1), domain_int!(1..3));
668 assert_eq!(s.length(), Ok(7));
669
670 let s = Domain::set(SetAttr::new_max_size(2), domain_int!(1..3));
672 assert_eq!(s.length(), Ok(7));
673 }
674
675 #[test]
676 fn test_length_set_nested() {
677 let s2 = Domain::set(
684 SetAttr::new_max_size(2),
685 Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
687 );
688 assert_eq!(s2.length(), Ok(11));
689 }
690
691 #[test]
692 fn test_length_set_unbounded_inner() {
693 let s2_bad = Domain::set(
695 SetAttr::new_max_size(2),
696 Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..)),
697 );
698 assert_eq!(s2_bad.length(), Err(DomainOpError::Unbounded));
699 }
700
701 #[test]
702 fn test_length_set_overflow() {
703 let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..20));
704 assert!(s.length().is_ok());
705
706 let s = Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..63));
708 assert_eq!(s.length(), Err(DomainOpError::TooLarge));
709 }
710
711 #[test]
712 fn test_length_tuple() {
713 let t = Domain::tuple(vec![domain_int!(1..3), Domain::bool()]);
715 assert_eq!(t.length(), Ok(6));
716 }
717
718 #[test]
719 fn test_length_record() {
720 let t = Domain::record(vec![
722 RecordEntry {
723 name: Name::user("a"),
724 domain: domain_int!(1..3),
725 },
726 RecordEntry {
727 name: Name::user("b"),
728 domain: Domain::bool(),
729 },
730 ]);
731 assert_eq!(t.length(), Ok(6));
732 }
733
734 #[test]
735 fn test_length_matrix_basic() {
736 let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..3)]);
738 assert_eq!(m.length(), Ok(8));
739
740 let m = Domain::matrix(domain_int!(1..3), vec![domain_int!(1..2)]);
742 assert_eq!(m.length(), Ok(9));
743 }
744
745 #[test]
746 fn test_length_matrix_2d() {
747 let m = Domain::matrix(Domain::bool(), vec![domain_int!(1..2), domain_int!(1..3)]);
749 assert_eq!(m.length(), Ok(64));
750 }
751
752 #[test]
753 fn test_length_matrix_of_sets() {
754 let m = Domain::matrix(
756 Domain::set(SetAttr::<IntVal>::default(), domain_int!(1..2)),
757 vec![domain_int!(1..3)],
758 );
759 assert_eq!(m.length(), Ok(64));
760 }
761}