1
use crate::ast::domains::attrs::PartitionAttr;
2
use crate::ast::domains::{MSetAttr, SequenceAttr};
3
use crate::ast::pretty::pretty_vec;
4
use crate::ast::{
5
    AbstractLiteral, Domain, DomainOpError, FieldEntry, FuncAttr, HasDomain, Literal, Moo, RelAttr,
6
    SetAttr, Typeable,
7
    domains::{domain::Int, range::Range},
8
};
9
use crate::range;
10
use crate::utils::count_combinations;
11
use conjure_cp_core::ast::{Name, ReturnType};
12
use itertools::{Itertools, izip};
13
use num_traits::ToPrimitive;
14
use polyquine::Quine;
15
use serde::{Deserialize, Serialize};
16
use std::collections::BTreeSet;
17
use std::fmt::{Display, Formatter};
18
use std::iter::zip;
19
use uniplate::Uniplate;
20

            
21
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Uniplate, Quine)]
22
#[path_prefix(conjure_cp::ast)]
23
pub struct FieldEntryGround {
24
    pub name: Name,
25
    pub domain: Moo<GroundDomain>,
26
}
27

            
28
impl From<FieldEntryGround> for FieldEntry {
29
80
    fn from(value: FieldEntryGround) -> Self {
30
80
        FieldEntry {
31
80
            name: value.name,
32
80
            domain: value.domain.into(),
33
80
        }
34
80
    }
35
}
36

            
37
impl TryFrom<FieldEntry> for FieldEntryGround {
38
    type Error = DomainOpError;
39

            
40
802
    fn try_from(value: FieldEntry) -> Result<Self, Self::Error> {
41
802
        match value.domain.as_ref() {
42
722
            Domain::Ground(gd) => Ok(FieldEntryGround {
43
722
                name: value.name,
44
722
                domain: gd.clone(),
45
722
            }),
46
80
            Domain::Unresolved(_) => Err(DomainOpError::NotGround),
47
        }
48
802
    }
49
}
50

            
51
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
52
#[path_prefix(conjure_cp::ast)]
53
pub enum GroundDomain {
54
    /// An empty domain of a given type
55
    Empty(ReturnType),
56
    /// A boolean value (true / false)
57
    Bool,
58
    /// An integer value in the given ranges (e.g. int(1, 3..5))
59
    Int(Vec<Range<Int>>),
60
    /// A set of elements drawn from the inner domain
61
    Set(SetAttr<Int>, Moo<GroundDomain>),
62
    /// A multiset of elements drawn from the inner domain
63
    MSet(MSetAttr<Int>, Moo<GroundDomain>),
64
    /// An N-dimensional matrix of elements drawn from the inner domain,
65
    /// and indices from the n index domains
66
    Matrix(Moo<GroundDomain>, Vec<Moo<GroundDomain>>),
67
    /// A tuple of N elements, each with its own domain
68
    Tuple(Vec<Moo<GroundDomain>>),
69
    /// A record
70
    Record(Vec<FieldEntryGround>),
71
    /// A Partition
72
    Partition(PartitionAttr, Moo<GroundDomain>),
73
    /// A sequence of elements drawn from the inner domain
74
    Sequence(SequenceAttr, Moo<GroundDomain>),
75
    /// A function with a domain and codomain
76
    Function(FuncAttr, Moo<GroundDomain>, Moo<GroundDomain>),
77
    /// A relation as a set of tuples
78
    Relation(RelAttr, Vec<Moo<GroundDomain>>),
79
    /// A variant domain with its domain options (reusing field entries)
80
    Variant(Vec<FieldEntryGround>),
81
}
82

            
83
impl GroundDomain {
84
1361260
    pub fn union(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
85
1361260
        match (self, other) {
86
            (GroundDomain::Empty(ty), dom) | (dom, GroundDomain::Empty(ty)) => {
87
                if *ty == dom.return_type() {
88
                    Ok(dom.clone())
89
                } else {
90
                    Err(DomainOpError::WrongType)
91
                }
92
            }
93
800
            (GroundDomain::Bool, GroundDomain::Bool) => Ok(GroundDomain::Bool),
94
            (GroundDomain::Bool, _) | (_, GroundDomain::Bool) => Err(DomainOpError::WrongType),
95
1318630
            (GroundDomain::Int(r1), GroundDomain::Int(r2)) => {
96
1318630
                let mut rngs = r1.clone();
97
1318630
                rngs.extend(r2.clone());
98
1318630
                Ok(GroundDomain::Int(Range::squeeze(&rngs)))
99
            }
100
            (GroundDomain::Int(_), _) | (_, GroundDomain::Int(_)) => Err(DomainOpError::WrongType),
101
96
            (GroundDomain::Set(_, in1), GroundDomain::Set(_, in2)) => Ok(GroundDomain::Set(
102
96
                SetAttr::default(),
103
96
                Moo::new(in1.union(in2)?),
104
            )),
105
            (GroundDomain::Set(_, _), _) | (_, GroundDomain::Set(_, _)) => {
106
                Err(DomainOpError::WrongType)
107
            }
108
            (GroundDomain::MSet(_, in1), GroundDomain::MSet(_, in2)) => Ok(GroundDomain::MSet(
109
                MSetAttr::default(),
110
                Moo::new(in1.union(in2)?),
111
            )),
112
41734
            (GroundDomain::Matrix(in1, idx1), GroundDomain::Matrix(in2, idx2)) if idx1 == idx2 => {
113
                Ok(GroundDomain::Matrix(
114
41734
                    Moo::new(in1.union(in2)?),
115
41734
                    idx1.clone(),
116
                ))
117
            }
118
            (GroundDomain::Matrix(_, _), _) | (_, GroundDomain::Matrix(_, _)) => {
119
                Err(DomainOpError::WrongType)
120
            }
121
            (GroundDomain::Sequence(_, _), _) | (_, GroundDomain::Sequence(_, _)) => {
122
                Err(DomainOpError::WrongType)
123
            }
124
            (GroundDomain::Tuple(in1s), GroundDomain::Tuple(in2s)) if in1s.len() == in2s.len() => {
125
                let mut inners = Vec::new();
126
                for (in1, in2) in zip(in1s, in2s) {
127
                    inners.push(Moo::new(in1.union(in2)?));
128
                }
129
                Ok(GroundDomain::Tuple(inners))
130
            }
131
            (GroundDomain::Tuple(_), _) | (_, GroundDomain::Tuple(_)) => {
132
                Err(DomainOpError::WrongType)
133
            }
134
            // TODO: Eventually we may define semantics for joining record domains. This day is not today.
135
            #[allow(unreachable_patterns)]
136
            // Technically redundant but logically clearer to have both
137
            (GroundDomain::Record(_), _) | (_, GroundDomain::Record(_)) => {
138
                Err(DomainOpError::WrongType)
139
            }
140
            #[allow(unreachable_patterns)]
141
            // Technically redundant but logically clearer to have both
142
            (GroundDomain::Variant(_), _) | (_, GroundDomain::Variant(_)) => {
143
                Err(DomainOpError::WrongType)
144
            }
145
            #[allow(unreachable_patterns)]
146
            // Technically redundant but logically clearer to have both
147
            (GroundDomain::Function(_, _, _), _) | (_, GroundDomain::Function(_, _, _)) => {
148
                Err(DomainOpError::WrongType)
149
            }
150
            (GroundDomain::Relation(_, in1s), GroundDomain::Relation(_, in2s)) => {
151
                let mut inners = Vec::new();
152
                for (in1, in2) in zip(in1s, in2s) {
153
                    inners.push(Moo::new(in1.union(in2)?));
154
                }
155
                Ok(GroundDomain::Tuple(inners))
156
            }
157
            (GroundDomain::Relation(_, _), _) | (_, GroundDomain::Relation(_, _)) => {
158
                Err(DomainOpError::WrongType)
159
            }
160
            #[allow(unreachable_patterns)]
161
            (GroundDomain::Partition(_, _), _) | (_, GroundDomain::Partition(_, _)) => {
162
                Err(DomainOpError::WrongType)
163
            }
164
        }
165
1361260
    }
166

            
167
    /// Calculates the intersection of two domains.
168
    ///
169
    /// # Errors
170
    ///
171
    ///  - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
172
    ///  - [`DomainOpError::WrongType`] if the input domains are different types, or are not integer or set domains.
173
36594
    pub fn intersect(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
174
        // TODO: does not consider unbounded domains yet
175
        // needs to be tested once comprehension rules are written
176

            
177
36594
        match (self, other) {
178
            // one or more arguments is an empty int domain
179
            (d @ GroundDomain::Empty(ReturnType::Int), GroundDomain::Int(_)) => Ok(d.clone()),
180
            (GroundDomain::Int(_), d @ GroundDomain::Empty(ReturnType::Int)) => Ok(d.clone()),
181
            (GroundDomain::Empty(ReturnType::Int), d @ GroundDomain::Empty(ReturnType::Int)) => {
182
                Ok(d.clone())
183
            }
184

            
185
            // one or more arguments is an empty set(int) domain
186
            (GroundDomain::Set(_, inner1), d @ GroundDomain::Empty(ReturnType::Set(inner2)))
187
                if matches!(
188
                    **inner1,
189
                    GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
190
                ) && matches!(**inner2, ReturnType::Int) =>
191
            {
192
                Ok(d.clone())
193
            }
194
            (d @ GroundDomain::Empty(ReturnType::Set(inner1)), GroundDomain::Set(_, inner2))
195
                if matches!(**inner1, ReturnType::Int)
196
                    && matches!(
197
                        **inner2,
198
                        GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
199
                    ) =>
200
            {
201
                Ok(d.clone())
202
            }
203
            (
204
                d @ GroundDomain::Empty(ReturnType::Set(inner1)),
205
                GroundDomain::Empty(ReturnType::Set(inner2)),
206
            ) if matches!(**inner1, ReturnType::Int) && matches!(**inner2, ReturnType::Int) => {
207
                Ok(d.clone())
208
            }
209

            
210
            // both arguments are non-empy
211
90
            (GroundDomain::Set(_, x), GroundDomain::Set(_, y)) => Ok(GroundDomain::Set(
212
90
                SetAttr::default(),
213
90
                Moo::new((*x).intersect(y)?),
214
            )),
215

            
216
            (GroundDomain::Int(_), GroundDomain::Int(_)) => {
217
35132
                let mut v: BTreeSet<i32> = BTreeSet::new();
218

            
219
35132
                let v1 = self.values_i32()?;
220
35132
                let v2 = other.values_i32()?;
221
165006
                for value1 in v1.iter() {
222
165006
                    if v2.contains(value1) && !v.contains(value1) {
223
99776
                        v.insert(*value1);
224
99776
                    }
225
                }
226
35132
                Ok(GroundDomain::from_set_i32(&v))
227
            }
228
            (GroundDomain::Relation(_, _), GroundDomain::Relation(_, _)) => {
229
                todo!("Relation union not yet supported")
230
            }
231
1372
            _ => Err(DomainOpError::WrongType),
232
        }
233
36594
    }
234

            
235
890142
    pub fn values(&self) -> Result<Box<dyn Iterator<Item = Literal>>, DomainOpError> {
236
890142
        match self {
237
            GroundDomain::Empty(_) => Ok(Box::new(vec![].into_iter())),
238
2660
            GroundDomain::Bool => Ok(Box::new(
239
2660
                vec![Literal::from(true), Literal::from(false)].into_iter(),
240
2660
            )),
241
885872
            GroundDomain::Int(rngs) => {
242
885872
                let rng_iters = rngs
243
885872
                    .iter()
244
885872
                    .map(Range::iter)
245
885872
                    .collect::<Option<Vec<_>>>()
246
885872
                    .ok_or(DomainOpError::Unbounded)?;
247
885872
                Ok(Box::new(
248
886832
                    rng_iters.into_iter().flat_map(|ri| ri.map(Literal::from)),
249
                ))
250
            }
251
1610
            GroundDomain::Matrix(elem_domain, index_domains) => Ok(Box::new(
252
1610
                enumerate_matrix_values(elem_domain.as_ref(), index_domains)?.into_iter(),
253
            )),
254
            _ => todo!("Enumerating nested domains is not yet supported"),
255
        }
256
890142
    }
257

            
258
    /// Gets the length of this domain.
259
    ///
260
    /// # Errors
261
    ///
262
    /// - [`DomainOpError::Unbounded`] if the input domain is of infinite size.
263
5302
    pub fn length(&self) -> Result<u64, DomainOpError> {
264
5302
        match self {
265
1
            GroundDomain::Empty(_) => Ok(0),
266
7
            GroundDomain::Bool => Ok(2),
267
5276
            GroundDomain::Int(ranges) => {
268
5276
                if ranges.is_empty() {
269
                    return Err(DomainOpError::Unbounded);
270
5276
                }
271

            
272
5276
                let mut length = 0u64;
273
5279
                for range in ranges {
274
5279
                    if let Some(range_length) = range.length() {
275
5277
                        length += range_length as u64;
276
5277
                    } else {
277
2
                        return Err(DomainOpError::Unbounded);
278
                    }
279
                }
280
5274
                Ok(length)
281
            }
282
12
            GroundDomain::Set(set_attr, inner_domain) => {
283
12
                let inner_len = inner_domain.length()?;
284
10
                let (min_sz, max_sz) = match set_attr.size {
285
5
                    Range::Unbounded => (0, inner_len),
286
1
                    Range::Single(n) => (n as u64, n as u64),
287
1
                    Range::UnboundedR(n) => (n as u64, inner_len),
288
2
                    Range::UnboundedL(n) => (0, n as u64),
289
1
                    Range::Bounded(min, max) => (min as u64, max as u64),
290
                };
291
10
                let mut ans = 0u64;
292
73
                for sz in min_sz..=max_sz {
293
73
                    let c = count_combinations(inner_len, sz)?;
294
72
                    ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
295
                }
296
9
                Ok(ans)
297
            }
298
            GroundDomain::MSet(mset_attr, inner_domain) => {
299
                let inner_len = inner_domain.length()?;
300
                let (min_sz, max_sz) = match mset_attr.size {
301
                    Range::Unbounded => (0, inner_len),
302
                    Range::Single(n) => (n as u64, n as u64),
303
                    Range::UnboundedR(n) => (n as u64, inner_len),
304
                    Range::UnboundedL(n) => (0, n as u64),
305
                    Range::Bounded(min, max) => (min as u64, max as u64),
306
                };
307
                let mut ans = 0u64;
308
                for sz in min_sz..=max_sz {
309
                    // need  "multichoose", ((n  k)) == (n+k-1  k)
310
                    // Where n=inner_len and k=sz
311
                    let c = count_combinations(inner_len + sz - 1, sz)?;
312
                    ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
313
                }
314
                Ok(ans)
315
            }
316
            GroundDomain::Sequence(_, _) => {
317
                // If jectivity is not set, the sequence can have any permutation.
318
                //
319
                todo!("Length bound currently not supported");
320
            }
321
1
            GroundDomain::Tuple(domains) => {
322
1
                let mut ans = 1u64;
323
2
                for domain in domains {
324
2
                    ans = ans
325
2
                        .checked_mul(domain.length()?)
326
2
                        .ok_or(DomainOpError::TooLarge)?;
327
                }
328
1
                Ok(ans)
329
            }
330
1
            GroundDomain::Record(entries) => {
331
                // A record is just a named tuple
332
1
                let mut ans = 1u64;
333
2
                for entry in entries {
334
2
                    let sz = entry.domain.length()?;
335
2
                    ans = ans.checked_mul(sz).ok_or(DomainOpError::TooLarge)?;
336
                }
337
1
                Ok(ans)
338
            }
339
4
            GroundDomain::Matrix(inner_domain, idx_domains) => {
340
4
                let inner_sz = inner_domain.length()?;
341
5
                let exp = idx_domains.iter().try_fold(1u32, |acc, val| {
342
5
                    let len = val.length()? as u32;
343
5
                    acc.checked_mul(len).ok_or(DomainOpError::TooLarge)
344
5
                })?;
345
4
                inner_sz.checked_pow(exp).ok_or(DomainOpError::TooLarge)
346
            }
347
            GroundDomain::Function(_, _, _) => {
348
                todo!("Length bound of functions is not yet supported")
349
            }
350
            GroundDomain::Variant(entries) => {
351
                let mut ans = 1u64;
352
                for entry in entries {
353
                    let sz = entry.domain.length()?;
354
                    // Only one field can be in the variant at once
355
                    ans = ans.checked_add(sz).ok_or(DomainOpError::TooLarge)?;
356
                }
357
                Ok(ans)
358
            }
359
            GroundDomain::Relation(_, domains) => {
360
                // Cannot currently use attributes to better infer length because of i32 u64 mismatch
361
                let dom_sizes_result: Result<Vec<u64>, DomainOpError> =
362
                    domains.iter().map(|x| x.length()).collect();
363
                let dom_sizes = dom_sizes_result?;
364
                Ok(dom_sizes.iter().product())
365
            }
366
            GroundDomain::Partition(_, _) => {
367
                todo!("Length bound of Partitions is not yet supported")
368
            }
369
        }
370
5302
    }
371

            
372
788878
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
373
        // not adding a generic wildcard condition for all domains, so that this gives a compile
374
        // error when a domain is added.
375
788878
        match self {
376
            // empty domain can't contain anything
377
            GroundDomain::Empty(_) => Ok(false),
378
176968
            GroundDomain::Bool => match lit {
379
176964
                Literal::Bool(_) => Ok(true),
380
4
                _ => Ok(false),
381
            },
382
607186
            GroundDomain::Int(ranges) => match lit {
383
607186
                Literal::Int(x) => {
384
                    // unconstrained int domain - contains all integers
385
607186
                    if ranges.is_empty() {
386
88
                        return Ok(true);
387
607098
                    };
388

            
389
608234
                    Ok(ranges.iter().any(|range| range.contains(x)))
390
                }
391
                _ => Ok(false),
392
            },
393
480
            GroundDomain::Set(set_attr, inner_domain) => match lit {
394
320
                Literal::AbstractLiteral(AbstractLiteral::Set(lit_elems)) => {
395
                    // check if the literal's size is allowed by the set attribute
396
320
                    let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
397
320
                    if !set_attr.size.contains(&sz) {
398
                        return Ok(false);
399
320
                    }
400

            
401
640
                    for elem in lit_elems {
402
640
                        if !inner_domain.contains(elem)? {
403
160
                            return Ok(false);
404
480
                        }
405
                    }
406
160
                    Ok(true)
407
                }
408
160
                _ => Ok(false),
409
            },
410
            GroundDomain::MSet(mset_attr, inner_domain) => match lit {
411
                Literal::AbstractLiteral(AbstractLiteral::MSet(lit_elems)) => {
412
                    // check if the literal's size is allowed by the mset attribute
413
                    let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
414
                    if !mset_attr.size.contains(&sz) {
415
                        return Ok(false);
416
                    }
417

            
418
                    for elem in lit_elems {
419
                        if !inner_domain.contains(elem)? {
420
                            return Ok(false);
421
                        }
422
                    }
423
                    Ok(true)
424
                }
425
                _ => Ok(false),
426
            },
427
            GroundDomain::Sequence(seq_attr, inner_dom) => match lit {
428
                Literal::AbstractLiteral(AbstractLiteral::Sequence(elems)) => {
429
                    let sz = elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
430
                    if !seq_attr.size.contains(&sz) {
431
                        return Ok(false);
432
                    }
433

            
434
                    for elem in elems {
435
                        if !inner_dom.contains(elem)? {
436
                            return Ok(false);
437
                        }
438
                    }
439
                    Ok(true)
440
                }
441
                _ => Ok(false),
442
            },
443
244
            GroundDomain::Matrix(elem_domain, index_domains) => {
444
218
                match lit {
445
218
                    Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx_domain)) => {
446
                        // Matrix literals are represented as nested 1d matrices, so the elements of
447
                        // the matrix literal will be the inner dimensions of the matrix.
448

            
449
218
                        let Some((current_index_domain, remaining_index_domains)) =
450
218
                            index_domains.split_first()
451
                        else {
452
                            panic!("a matrix should have at least one index domain");
453
                        };
454

            
455
218
                        if *current_index_domain != *idx_domain {
456
                            return Ok(false);
457
218
                        };
458

            
459
218
                        let next_elem_domain = if remaining_index_domains.is_empty() {
460
                            // Base case - we have a 1D row. Now check if all elements in the
461
                            // literal are in this row's element domain.
462
198
                            elem_domain.as_ref().clone()
463
                        } else {
464
                            // Otherwise, go down a dimension (e.g. 2D matrix inside a 3D tensor)
465
20
                            GroundDomain::Matrix(
466
20
                                elem_domain.clone(),
467
20
                                remaining_index_domains.to_vec(),
468
20
                            )
469
                        };
470

            
471
1046
                        for elem in elems {
472
1046
                            if !next_elem_domain.contains(elem)? {
473
                                return Ok(false);
474
1046
                            }
475
                        }
476

            
477
218
                        Ok(true)
478
                    }
479
26
                    _ => Ok(false),
480
                }
481
            }
482
1120
            GroundDomain::Tuple(elem_domains) => {
483
1120
                match lit {
484
1120
                    Literal::AbstractLiteral(AbstractLiteral::Tuple(literal_elems)) => {
485
1120
                        if elem_domains.len() != literal_elems.len() {
486
                            return Ok(false);
487
1120
                        }
488

            
489
                        // for every element in the tuple literal, check if it is in the corresponding domain
490
2240
                        for (elem_domain, elem) in itertools::izip!(elem_domains, literal_elems) {
491
2240
                            if !elem_domain.contains(elem)? {
492
                                return Ok(false);
493
2240
                            }
494
                        }
495

            
496
1120
                        Ok(true)
497
                    }
498
                    _ => Ok(false),
499
                }
500
            }
501
2880
            GroundDomain::Record(entries) => match lit {
502
2880
                Literal::AbstractLiteral(AbstractLiteral::Record(lit_entries)) => {
503
2880
                    if entries.len() != lit_entries.len() {
504
                        return Ok(false);
505
2880
                    }
506

            
507
5760
                    for (entry, lit_entry) in itertools::izip!(entries, lit_entries) {
508
5760
                        if entry.name != lit_entry.name
509
5760
                            || !(entry.domain.contains(&lit_entry.value)?)
510
                        {
511
                            return Ok(false);
512
5760
                        }
513
                    }
514
2880
                    Ok(true)
515
                }
516
                _ => Ok(false),
517
            },
518
            GroundDomain::Function(func_attr, domain, codomain) => match lit {
519
                Literal::AbstractLiteral(AbstractLiteral::Function(lit_elems)) => {
520
                    let sz = Int::try_from(lit_elems.len()).expect("Should convert");
521
                    if !func_attr.size.contains(&sz) {
522
                        return Ok(false);
523
                    }
524
                    for lit in lit_elems {
525
                        let domain_element = &lit.0;
526
                        let codomain_element = &lit.1;
527
                        if !domain.contains(domain_element)? {
528
                            return Ok(false);
529
                        }
530
                        if !codomain.contains(codomain_element)? {
531
                            return Ok(false);
532
                        }
533
                    }
534
                    Ok(true)
535
                }
536
                _ => Ok(false),
537
            },
538
            GroundDomain::Variant(entries) => match lit {
539
                Literal::AbstractLiteral(AbstractLiteral::Variant(lit_entry)) => {
540
                    for entry in entries {
541
                        if entry.name == lit_entry.name
542
                            && !(entry.domain.contains(&lit_entry.value)?)
543
                        {
544
                            return Ok(true);
545
                        }
546
                    }
547
                    Ok(false)
548
                }
549
                _ => Ok(false),
550
            },
551
            GroundDomain::Relation(rel_attr, inner_domains) => match lit {
552
                Literal::AbstractLiteral(AbstractLiteral::Relation(lit_elems)) => {
553
                    // check if the literal's size is allowed by the attributes
554
                    let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
555
                    if !rel_attr.size.contains(&sz) {
556
                        return Ok(false);
557
                    }
558

            
559
                    for elem_tuple in lit_elems {
560
                        if elem_tuple.len() == inner_domains.len() {
561
                            for (elem, inner_dom) in elem_tuple.iter().zip(inner_domains.iter()) {
562
                                if !inner_dom.contains(elem)? {
563
                                    return Ok(false);
564
                                }
565
                            }
566
                        } else {
567
                            return Ok(false);
568
                        }
569
                    }
570
                    Ok(true)
571
                }
572
                _ => Ok(false),
573
            },
574
            GroundDomain::Partition(attr, dom) => match lit {
575
                Literal::AbstractLiteral(AbstractLiteral::Partition(lit_elems)) => {
576
                    // let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
577
                    let sz: i32 = lit_elems
578
                        .iter()
579
                        .flatten()
580
                        .count()
581
                        .to_i32()
582
                        .ok_or(DomainOpError::TooLarge)?;
583

            
584
                    let min: Option<i32> = match (attr.num_parts.low(), attr.part_len.low()) {
585
                        (Some(x), Some(y)) => Some(x * y),
586
                        _ => None,
587
                    };
588

            
589
                    let max: Option<i32> = match (attr.num_parts.high(), attr.part_len.high()) {
590
                        (Some(x), Some(y)) => Some(x * y),
591
                        _ => None,
592
                    };
593

            
594
                    let rng = Range::new(min, max);
595
                    if rng.contains(&sz) {
596
                        return Ok(false);
597
                    }
598

            
599
                    for elem in lit_elems.iter().flatten() {
600
                        if !dom.contains(elem)? {
601
                            return Ok(false);
602
                        }
603
                    }
604
                    Ok(true)
605
                }
606
                _ => Ok(false),
607
            },
608
        }
609
788878
    }
610

            
611
    /// Returns a list of all possible values in an integer domain.
612
    ///
613
    /// # Errors
614
    ///
615
    /// - [`DomainOpError::NotInteger`] if the domain is not an integer domain.
616
    /// - [`DomainOpError::Unbounded`] if the domain is unbounded.
617
931974
    pub fn values_i32(&self) -> Result<Vec<i32>, DomainOpError> {
618
        if let GroundDomain::Empty(ReturnType::Int) = self {
619
            return Ok(vec![]);
620
931974
        }
621
931974
        let GroundDomain::Int(ranges) = self else {
622
            return Err(DomainOpError::NotInteger(self.return_type()));
623
        };
624

            
625
931974
        if ranges.is_empty() {
626
            return Err(DomainOpError::Unbounded);
627
931974
        }
628

            
629
931974
        let mut values = vec![];
630
942444
        for range in ranges {
631
942444
            match range {
632
397902
                Range::Single(i) => {
633
397902
                    values.push(*i);
634
397902
                }
635
544542
                Range::Bounded(i, j) => {
636
544542
                    values.extend(*i..=*j);
637
544542
                }
638
                Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => {
639
                    return Err(DomainOpError::Unbounded);
640
                }
641
            }
642
        }
643

            
644
931974
        Ok(values)
645
931974
    }
646

            
647
    /// Creates an [`Domain::Int`] containing the given integers.
648
    ///
649
    /// # Examples
650
    ///
651
    /// ```
652
    /// use conjure_cp_core::ast::{GroundDomain, Range};
653
    /// use conjure_cp_core::{domain_int_ground,range};
654
    /// use std::collections::BTreeSet;
655
    ///
656
    /// let elements = BTreeSet::from([1,2,3,4,5]);
657
    ///
658
    /// let domain = GroundDomain::from_set_i32(&elements);
659
    ///
660
    /// assert_eq!(domain,domain_int_ground!(1..5));
661
    /// ```
662
    ///
663
    /// ```
664
    /// use conjure_cp_core::ast::{GroundDomain,Range};
665
    /// use conjure_cp_core::{domain_int_ground,range};
666
    /// use std::collections::BTreeSet;
667
    ///
668
    /// let elements = BTreeSet::from([1,2,4,5,7,8,9,10]);
669
    ///
670
    /// let domain = GroundDomain::from_set_i32(&elements);
671
    ///
672
    /// assert_eq!(domain,domain_int_ground!(1..2,4..5,7..10));
673
    /// ```
674
    ///
675
    /// ```
676
    /// use conjure_cp_core::ast::{GroundDomain,Range,ReturnType};
677
    /// use std::collections::BTreeSet;
678
    ///
679
    /// let elements = BTreeSet::from([]);
680
    ///
681
    /// let domain = GroundDomain::from_set_i32(&elements);
682
    ///
683
    /// assert!(matches!(domain,GroundDomain::Empty(ReturnType::Int)))
684
    /// ```
685
471054
    pub fn from_set_i32(elements: &BTreeSet<i32>) -> GroundDomain {
686
471054
        if elements.is_empty() {
687
20
            return GroundDomain::Empty(ReturnType::Int);
688
471034
        }
689
471034
        if elements.len() == 1 {
690
3046
            return GroundDomain::Int(vec![Range::Single(*elements.first().unwrap())]);
691
467988
        }
692

            
693
467988
        let mut elems_iter = elements.iter().copied();
694

            
695
467988
        let mut ranges: Vec<Range<i32>> = vec![];
696

            
697
        // Loop over the elements in ascending order, turning all sequential runs of
698
        // numbers into ranges.
699

            
700
        // the bounds of the current run of numbers.
701
467988
        let mut lower = elems_iter
702
467988
            .next()
703
467988
            .expect("if we get here, elements should have => 2 elements");
704
467988
        let mut upper = lower;
705

            
706
1077293
        for current in elems_iter {
707
            // As elements is a BTreeSet, current is always strictly larger than lower.
708

            
709
1077293
            if current == upper + 1 {
710
                // current is part of the current run - we now have the run lower..current
711
                //
712
980440
                upper = current;
713
980440
            } else {
714
                // the run lower..upper has ended.
715
                //
716
                // Add the run lower..upper to the domain, and start a new run.
717

            
718
96853
                if lower == upper {
719
86140
                    ranges.push(range!(lower));
720
86141
                } else {
721
10713
                    ranges.push(range!(lower..upper));
722
10713
                }
723

            
724
96853
                lower = current;
725
96853
                upper = current;
726
            }
727
        }
728

            
729
        // add the final run to the domain
730
467988
        if lower == upper {
731
12785
            ranges.push(range!(lower));
732
455203
        } else {
733
455203
            ranges.push(range!(lower..upper));
734
455203
        }
735

            
736
467988
        ranges = Range::squeeze(&ranges);
737
467988
        GroundDomain::Int(ranges)
738
471054
    }
739

            
740
    /// Returns the domain that is the result of applying a binary operation to two integer domains.
741
    ///
742
    /// The given operator may return `None` if the operation is not defined for its arguments.
743
    /// Undefined values will not be included in the resulting domain.
744
    ///
745
    /// # Errors
746
    ///
747
    /// - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
748
    /// - [`DomainOpError::NotInteger`] if either of the input domains are not integers.
749
418444
    pub fn apply_i32(
750
418444
        &self,
751
418444
        op: fn(i32, i32) -> Option<i32>,
752
418444
        other: &GroundDomain,
753
418444
    ) -> Result<GroundDomain, DomainOpError> {
754
418444
        let vs1 = self.values_i32()?;
755
418444
        let vs2 = other.values_i32()?;
756

            
757
418444
        let mut set = BTreeSet::new();
758
3535982
        for (v1, v2) in itertools::iproduct!(vs1, vs2) {
759
3535982
            if let Some(v) = op(v1, v2) {
760
3354338
                set.insert(v);
761
3354338
            }
762
        }
763

            
764
418444
        Ok(GroundDomain::from_set_i32(&set))
765
418444
    }
766

            
767
    /// Returns true if the domain is finite.
768
32202
    pub fn is_finite(&self) -> bool {
769
53466
        for domain in self.universe() {
770
53466
            if let GroundDomain::Int(ranges) = domain {
771
44042
                if ranges.is_empty() {
772
                    return false;
773
44042
                }
774

            
775
45842
                if ranges.iter().any(|range| {
776
45842
                    matches!(
777
45842
                        range,
778
                        Range::UnboundedL(_) | Range::UnboundedR(_) | Range::Unbounded
779
                    )
780
45842
                }) {
781
                    return false;
782
44042
                }
783
9424
            }
784
        }
785
32202
        true
786
32202
    }
787

            
788
    /// For a vector of literals, creates a domain that contains all the elements.
789
    ///
790
    /// The literals must all be of the same type.
791
    ///
792
    /// For abstract literals, this method merges the element domains of the literals, but not the
793
    /// index domains. Thus, for fixed-sized abstract literals (matrices, tuples, records, etc.),
794
    /// all literals in the vector must also have the same size / index domain:
795
    ///
796
    /// + Matrices: all literals must have the same index domain.
797
    /// + Tuples: all literals must have the same number of elements.
798
    /// + Records: all literals must have the same fields.
799
    ///
800
    /// # Errors
801
    ///
802
    /// - [DomainOpError::WrongType] if the input literals are of a different type to
803
    ///   each-other, as described above.
804
    ///
805
    /// # Examples
806
    ///
807
    /// ```
808
    /// use conjure_cp_core::ast::{Range, Literal, ReturnType, GroundDomain};
809
    ///
810
    /// let domain = GroundDomain::from_literal_vec(&vec![]);
811
    /// assert_eq!(domain,Ok(GroundDomain::Empty(ReturnType::Unknown)));
812
    /// ```
813
    ///
814
    /// ```
815
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
816
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
817
    ///
818
    /// // `[1,2;int(2..3)], [4,5; int(2..3)]` has domain
819
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
820
    ///
821
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
822
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(2..3)]);
823
    ///
824
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
825
    ///
826
    /// let expected_domain = Ok(GroundDomain::Matrix(
827
    ///     domain_int_ground!(1..2,4..5),vec![domain_int_ground!(2..3)]));
828
    ///
829
    /// assert_eq!(domain,expected_domain);
830
    /// ```
831
    ///
832
    /// ```
833
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral,DomainOpError};
834
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
835
    ///
836
    /// // `[1,2;int(2..3)], [4,5; int(1..2)]` cannot be combined
837
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
838
    ///
839
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
840
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]);
841
    ///
842
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
843
    ///
844
    /// assert_eq!(domain,Err(DomainOpError::WrongType));
845
    /// ```
846
    ///
847
    /// ```
848
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
849
    /// use conjure_cp_core::{domain_int_ground,range, matrix};
850
    ///
851
    /// // `[[1,2; int(1..2)];int(2)], [[4,5; int(1..2)]; int(2)]` has domain
852
    /// // `matrix indexed by [int(2),int(1..2)] of int(1..2,4..5)`
853
    ///
854
    ///
855
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
856
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
857
    ///
858
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
859
    ///
860
    /// let expected_domain = Ok(GroundDomain::Matrix(
861
    ///     domain_int_ground!(1..2,4..5),
862
    ///     vec![domain_int_ground!(2),domain_int_ground!(1..2)]));
863
    ///
864
    /// assert_eq!(domain,expected_domain);
865
    /// ```
866
    ///
867
    ///
868
34956
    pub fn from_literal_vec(literals: &[Literal]) -> Result<GroundDomain, DomainOpError> {
869
        // TODO: use proptest to test this better?
870

            
871
34956
        if literals.is_empty() {
872
20
            return Ok(GroundDomain::Empty(ReturnType::Unknown));
873
34936
        }
874

            
875
34936
        let first_literal = literals.first().unwrap();
876

            
877
17278
        match first_literal {
878
            Literal::Int(_) => {
879
                // check all literals are ints, then pass this to Domain::from_set_i32.
880
17418
                let mut ints = BTreeSet::new();
881
62256
                for lit in literals {
882
62256
                    let Literal::Int(i) = lit else {
883
                        return Err(DomainOpError::WrongType);
884
                    };
885

            
886
62256
                    ints.insert(*i);
887
                }
888

            
889
17418
                Ok(GroundDomain::from_set_i32(&ints))
890
            }
891
            Literal::Bool(_) => {
892
                // check all literals are bools
893
240
                if literals.iter().any(|x| !matches!(x, Literal::Bool(_))) {
894
                    Err(DomainOpError::WrongType)
895
                } else {
896
240
                    Ok(GroundDomain::Bool)
897
                }
898
            }
899
            Literal::AbstractLiteral(AbstractLiteral::Set(_)) => {
900
6
                let mut all_elems = vec![];
901

            
902
6
                for lit in literals {
903
6
                    let Literal::AbstractLiteral(AbstractLiteral::Set(elems)) = lit else {
904
                        return Err(DomainOpError::WrongType);
905
                    };
906

            
907
6
                    all_elems.extend(elems.clone());
908
                }
909
6
                let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
910

            
911
6
                Ok(GroundDomain::Set(SetAttr::default(), Moo::new(elem_domain)))
912
            }
913
            Literal::AbstractLiteral(AbstractLiteral::MSet(_)) => {
914
                let mut all_elems = vec![];
915

            
916
                for lit in literals {
917
                    let Literal::AbstractLiteral(AbstractLiteral::MSet(elems)) = lit else {
918
                        return Err(DomainOpError::WrongType);
919
                    };
920

            
921
                    all_elems.extend(elems.clone());
922
                }
923
                let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
924

            
925
                Ok(GroundDomain::MSet(
926
                    MSetAttr::default(),
927
                    Moo::new(elem_domain),
928
                ))
929
            }
930
            Literal::AbstractLiteral(AbstractLiteral::Partition(_)) => {
931
                todo!("Need to figure out how this is going to work")
932
            }
933
16872
            l @ Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _)) => {
934
16872
                let mut first_index_domain = vec![];
935
                // flatten index domains of n-d matrix into list
936
16872
                let mut l = l.clone();
937
16892
                while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
938
16892
                    assert!(
939
16892
                        !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
940
                        "n-dimensional matrix literals should be represented as a matrix inside a matrix"
941
                    );
942
16892
                    first_index_domain.push(idx);
943
16892
                    l = elems[0].clone();
944
                }
945

            
946
16872
                let mut all_elems: Vec<Literal> = vec![];
947

            
948
                // check types and index domains
949
16932
                for lit in literals {
950
16932
                    let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = lit else {
951
                        return Err(DomainOpError::NotGround);
952
                    };
953

            
954
16932
                    all_elems.extend(elems.clone());
955

            
956
16932
                    let mut index_domain = vec![idx.clone()];
957
16932
                    let mut l = elems[0].clone();
958
40
                    while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
959
40
                        assert!(
960
40
                            !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
961
                            "n-dimensional matrix literals should be represented as a matrix inside a matrix"
962
                        );
963
40
                        index_domain.push(idx);
964
40
                        l = elems[0].clone();
965
                    }
966

            
967
16932
                    if index_domain != first_index_domain {
968
20
                        return Err(DomainOpError::WrongType);
969
16912
                    }
970
                }
971

            
972
                // extract all the terminal elements (those that are not nested matrix literals) from the matrix literal.
973
16852
                let mut terminal_elements: Vec<Literal> = vec![];
974
78580
                while let Some(elem) = all_elems.pop() {
975
40
                    if let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, _)) = elem {
976
40
                        all_elems.extend(elems);
977
61688
                    } else {
978
61688
                        terminal_elements.push(elem);
979
61688
                    }
980
                }
981

            
982
16852
                let element_domain = GroundDomain::from_literal_vec(&terminal_elements)?;
983

            
984
16852
                Ok(GroundDomain::Matrix(
985
16852
                    Moo::new(element_domain),
986
16852
                    first_index_domain,
987
16852
                ))
988
            }
989

            
990
160
            Literal::AbstractLiteral(AbstractLiteral::Tuple(first_elems)) => {
991
160
                let n_fields = first_elems.len();
992

            
993
                // for each field, calculate the element domain and add it to this list
994
160
                let mut elem_domains = vec![];
995

            
996
320
                for i in 0..n_fields {
997
320
                    let mut all_elems = vec![];
998
320
                    for lit in literals {
999
320
                        let Literal::AbstractLiteral(AbstractLiteral::Tuple(elems)) = lit else {
                            return Err(DomainOpError::NotGround);
                        };
320
                        if elems.len() != n_fields {
                            return Err(DomainOpError::NotGround);
320
                        }
320
                        all_elems.push(elems[i].clone());
                    }
320
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
                }
160
                Ok(GroundDomain::Tuple(elem_domains))
            }
            Literal::AbstractLiteral(AbstractLiteral::Sequence(_)) => {
                let mut all_elems = vec![];
                for lit in literals {
                    let Literal::AbstractLiteral(AbstractLiteral::Sequence(elems)) = lit else {
                        return Err(DomainOpError::WrongType);
                    };
                    all_elems.extend(elems.clone());
                }
                let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
                Ok(GroundDomain::Sequence(
                    SequenceAttr::default(),
                    Moo::new(elem_domain),
                ))
            }
240
            Literal::AbstractLiteral(AbstractLiteral::Record(first_elems)) => {
240
                let n_fields = first_elems.len();
480
                let field_names = first_elems.iter().map(|x| x.name.clone()).collect_vec();
                // for each field, calculate the element domain and add it to this list
240
                let mut elem_domains = vec![];
480
                for i in 0..n_fields {
480
                    let mut all_elems = vec![];
480
                    for lit in literals {
480
                        let Literal::AbstractLiteral(AbstractLiteral::Record(elems)) = lit else {
                            return Err(DomainOpError::NotGround);
                        };
480
                        if elems.len() != n_fields {
                            return Err(DomainOpError::NotGround);
480
                        }
480
                        let elem = elems[i].clone();
480
                        if elem.name != field_names[i] {
                            return Err(DomainOpError::NotGround);
480
                        }
480
                        all_elems.push(elem.value);
                    }
480
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
                }
                Ok(GroundDomain::Record(
240
                    izip!(field_names, elem_domains)
480
                        .map(|(name, domain)| FieldEntryGround { name, domain })
240
                        .collect(),
                ))
            }
            Literal::AbstractLiteral(AbstractLiteral::Function(items)) => {
                if items.is_empty() {
                    return Err(DomainOpError::NotGround);
                }
                let (x1, y1) = &items[0];
                let d1 = x1.domain_of();
                let d1 = d1.as_ground().ok_or(DomainOpError::NotGround)?;
                let d2 = y1.domain_of();
                let d2 = d2.as_ground().ok_or(DomainOpError::NotGround)?;
                // Check that all items have the same domains
                for (x, y) in items {
                    let dx = x.domain_of();
                    let dx = dx.as_ground().ok_or(DomainOpError::NotGround)?;
                    let dy = y.domain_of();
                    let dy = dy.as_ground().ok_or(DomainOpError::NotGround)?;
                    if (dx != d1) || (dy != d2) {
                        return Err(DomainOpError::WrongType);
                    }
                }
                todo!();
            }
            Literal::AbstractLiteral(AbstractLiteral::Variant(_)) => {
                todo!();
            }
            Literal::AbstractLiteral(AbstractLiteral::Relation(_)) => {
                todo!();
            }
        }
34956
    }
40
    pub fn element_domain(&self) -> Option<Moo<GroundDomain>> {
40
        match self {
40
            GroundDomain::Set(_, inner) => Some(inner.clone()),
            GroundDomain::MSet(_, inner) => Some(inner.clone()),
            GroundDomain::Matrix(_, _) => todo!("Unwrap one dimension of the domain"),
            _ => None,
        }
40
    }
}
impl Typeable for GroundDomain {
1781812
    fn return_type(&self) -> ReturnType {
1781812
        match self {
            GroundDomain::Empty(ty) => ty.clone(),
164180
            GroundDomain::Bool => ReturnType::Bool,
1502320
            GroundDomain::Int(_) => ReturnType::Int,
58
            GroundDomain::Set(_attr, inner) => ReturnType::Set(Box::new(inner.return_type())),
2
            GroundDomain::MSet(_attr, inner) => ReturnType::MSet(Box::new(inner.return_type())),
            GroundDomain::Sequence(_attr, inner) => {
                ReturnType::Sequence(Box::new(inner.return_type()))
            }
101448
            GroundDomain::Matrix(inner, _idx) => ReturnType::Matrix(Box::new(inner.return_type())),
6800
            GroundDomain::Tuple(inners) => {
6800
                let mut inner_types = Vec::new();
13600
                for inner in inners {
13600
                    inner_types.push(inner.return_type());
13600
                }
6800
                ReturnType::Tuple(inner_types)
            }
6880
            GroundDomain::Record(entries) => {
6880
                let mut entry_types = Vec::new();
13760
                for entry in entries {
13760
                    entry_types.push(entry.domain.return_type());
13760
                }
6880
                ReturnType::Record(entry_types)
            }
2
            GroundDomain::Function(_, dom, cdom) => {
2
                ReturnType::Function(Box::new(dom.return_type()), Box::new(cdom.return_type()))
            }
            GroundDomain::Variant(entries) => {
                let mut entry_types = Vec::new();
                for entry in entries {
                    entry_types.push(entry.domain.return_type());
                }
                ReturnType::Record(entry_types)
            }
2
            GroundDomain::Relation(_, inners) => {
2
                let mut inner_types = Vec::new();
6
                for inner in inners {
6
                    inner_types.push(inner.return_type());
6
                }
2
                ReturnType::Relation(inner_types)
            }
120
            GroundDomain::Partition(_, inner) => ReturnType::Set(Box::new(inner.return_type())),
        }
1781812
    }
}
impl Display for GroundDomain {
1205176928
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1205176928
        match &self {
6
            GroundDomain::Empty(ty) => write!(f, "empty({ty})"),
1074902696
            GroundDomain::Bool => write!(f, "bool"),
124179296
            GroundDomain::Int(ranges) => {
124179296
                if ranges.iter().all(Range::is_lower_or_upper_bounded) {
127958784
                    let rngs: String = ranges.iter().map(|r| format!("{r}")).join(", ");
124179296
                    write!(f, "int({})", rngs)
                } else {
                    write!(f, "int")
                }
            }
24874
            GroundDomain::Set(attrs, inner_dom) => write!(f, "set {attrs} of {inner_dom}"),
482
            GroundDomain::MSet(attrs, inner_dom) => write!(f, "mset {attrs} of {inner_dom}"),
320
            GroundDomain::Sequence(attrs, inner_dom) => {
320
                write!(f, "sequence {attrs} of {inner_dom}")
            }
5908232
            GroundDomain::Matrix(value_domain, index_domains) => {
5908232
                write!(
5908232
                    f,
                    "matrix indexed by {} of {value_domain}",
5908232
                    pretty_vec(&index_domains.iter().collect_vec())
                )
            }
159080
            GroundDomain::Tuple(domains) => {
159080
                write!(f, "tuple ({})", &domains.iter().join(", "))
            }
240
            GroundDomain::Record(entries) => {
240
                write!(
240
                    f,
                    "record {{{}}}",
240
                    entries
240
                        .iter()
480
                        .map(|entry| format!("{}: {}", entry.name, entry.domain))
240
                        .join(", ")
                )
            }
724
            GroundDomain::Function(attribute, domain, codomain) => {
724
                write!(f, "function {} {} --> {} ", attribute, domain, codomain)
            }
160
            GroundDomain::Variant(entries) => {
160
                write!(
160
                    f,
                    "variant {{{}}}",
160
                    entries
160
                        .iter()
400
                        .map(|entry| format!("{}: {}", entry.name, entry.domain))
160
                        .join(", ")
                )
            }
448
            GroundDomain::Relation(attrs, domains) => {
448
                write!(f, "relation {} of ({})", attrs, domains.iter().join(" * "))
            }
370
            GroundDomain::Partition(attrs, inner) => {
370
                write!(f, "partition {attrs} from {inner}")
            }
        }
1205176928
    }
}
1610
fn enumerate_matrix_values(
1610
    elem_domain: &GroundDomain,
1610
    index_domains: &[Moo<GroundDomain>],
1610
) -> Result<Vec<Literal>, DomainOpError> {
1610
    let Some((current_index_domain, remaining_index_domains)) = index_domains.split_first() else {
        panic!("a matrix should have at least one index domain");
    };
1610
    let current_dimension_len =
1610
        usize::try_from(current_index_domain.length()?).map_err(|_| DomainOpError::TooLarge)?;
1610
    let entry_values = if remaining_index_domains.is_empty() {
1610
        elem_domain.values()?.collect_vec()
    } else {
        enumerate_matrix_values(elem_domain, remaining_index_domains)?
    };
1610
    Ok((0..current_dimension_len)
1610
        .map(|_| entry_values.iter().cloned())
1610
        .multi_cartesian_product()
3220
        .map(|elems| {
3220
            Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, current_index_domain.clone()))
3220
        })
1610
        .collect())
1610
}