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

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

            
27
impl 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

            
36
impl TryFrom<RecordEntry> for RecordEntryGround {
37
    type Error = DomainOpError;
38

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

            
50
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine, Uniplate)]
51
#[path_prefix(conjure_cp::ast)]
52
pub enum GroundDomain {
53
    /// An empty domain of a given type
54
    Empty(ReturnType),
55
    /// A boolean value (true / false)
56
    Bool,
57
    /// An integer value in the given ranges (e.g. int(1, 3..5))
58
    Int(Vec<Range<Int>>),
59
    /// A set of elements drawn from the inner domain
60
    Set(SetAttr<Int>, Moo<GroundDomain>),
61
    /// A multiset of elements drawn from the inner domain
62
    MSet(MSetAttr<Int>, Moo<GroundDomain>),
63
    /// An N-dimensional matrix of elements drawn from the inner domain,
64
    /// and indices from the n index domains
65
    Matrix(Moo<GroundDomain>, Vec<Moo<GroundDomain>>),
66
    /// A tuple of N elements, each with its own domain
67
    Tuple(Vec<Moo<GroundDomain>>),
68
    /// A record
69
    Record(Vec<RecordEntryGround>),
70
    /// A function with a domain and range
71
    Function(FuncAttr, Moo<GroundDomain>, Moo<GroundDomain>),
72
}
73

            
74
impl GroundDomain {
75
760
    pub fn union(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
76
760
        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
700
            (GroundDomain::Int(r1), GroundDomain::Int(r2)) => {
87
700
                let mut rngs = r1.clone();
88
700
                rngs.extend(r2.clone());
89
700
                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
60
            (GroundDomain::Matrix(in1, idx1), GroundDomain::Matrix(in2, idx2)) if idx1 == idx2 => {
104
                Ok(GroundDomain::Matrix(
105
60
                    Moo::new(in1.union(in2)?),
106
60
                    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
            // TODO: Eventually we may define semantics for joining record domains. This day is not today.
123
            #[allow(unreachable_patterns)]
124
            // Technically redundant but logically clearer to have both
125
            (GroundDomain::Record(_), _) | (_, GroundDomain::Record(_)) => {
126
                Err(DomainOpError::WrongType)
127
            }
128
            #[allow(unreachable_patterns)]
129
            // Technically redundant but logically clearer to have both
130
            (GroundDomain::Function(_, _, _), _) | (_, GroundDomain::Function(_, _, _)) => {
131
                Err(DomainOpError::WrongType)
132
            }
133
        }
134
760
    }
135

            
136
    /// Calculates the intersection of two domains.
137
    ///
138
    /// # Errors
139
    ///
140
    ///  - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
141
    ///  - [`DomainOpError::WrongType`] if the input domains are different types, or are not integer or set domains.
142
1960
    pub fn intersect(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
143
        // TODO: does not consider unbounded domains yet
144
        // needs to be tested once comprehension rules are written
145

            
146
1960
        match (self, other) {
147
            // one or more arguments is an empty int domain
148
            (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
            // one or more arguments is an empty set(int) domain
155
            (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
            // both arguments are non-empy
180
            (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
1960
                let mut v: BTreeSet<i32> = BTreeSet::new();
187

            
188
1960
                let v1 = self.values_i32()?;
189
1960
                let v2 = other.values_i32()?;
190
7820
                for value1 in v1.iter() {
191
7820
                    if v2.contains(value1) && !v.contains(value1) {
192
6660
                        v.insert(*value1);
193
6660
                    }
194
                }
195
1960
                Ok(GroundDomain::from_set_i32(&v))
196
            }
197
            _ => Err(DomainOpError::WrongType),
198
        }
199
1960
    }
200

            
201
61740
    pub fn values(&self) -> Result<Box<dyn Iterator<Item = Literal>>, DomainOpError> {
202
61740
        match self {
203
            GroundDomain::Empty(_) => Ok(Box::new(vec![].into_iter())),
204
680
            GroundDomain::Bool => Ok(Box::new(
205
680
                vec![Literal::from(true), Literal::from(false)].into_iter(),
206
680
            )),
207
61060
            GroundDomain::Int(rngs) => {
208
61060
                let rng_iters = rngs
209
61060
                    .iter()
210
61060
                    .map(Range::iter)
211
61060
                    .collect::<Option<Vec<_>>>()
212
61060
                    .ok_or(DomainOpError::Unbounded)?;
213
61060
                Ok(Box::new(
214
61420
                    rng_iters.into_iter().flat_map(|ri| ri.map(Literal::from)),
215
                ))
216
            }
217
            _ => todo!("Enumerating nested domains is not yet supported"),
218
        }
219
61740
    }
220

            
221
    /// Gets the length of this domain.
222
    ///
223
    /// # Errors
224
    ///
225
    /// - [`DomainOpError::Unbounded`] if the input domain is of infinite size.
226
604
    pub fn length(&self) -> Result<u64, DomainOpError> {
227
604
        match self {
228
1
            GroundDomain::Empty(_) => Ok(0),
229
5
            GroundDomain::Bool => Ok(2),
230
580
            GroundDomain::Int(ranges) => {
231
580
                if ranges.is_empty() {
232
                    return Err(DomainOpError::Unbounded);
233
580
                }
234

            
235
580
                let mut length = 0u64;
236
583
                for range in ranges {
237
583
                    if let Some(range_length) = range.length() {
238
581
                        length += range_length as u64;
239
581
                    } else {
240
2
                        return Err(DomainOpError::Unbounded);
241
                    }
242
                }
243
578
                Ok(length)
244
            }
245
12
            GroundDomain::Set(set_attr, inner_domain) => {
246
12
                let inner_len = inner_domain.length()?;
247
10
                let (min_sz, max_sz) = match set_attr.size {
248
5
                    Range::Unbounded => (0, inner_len),
249
1
                    Range::Single(n) => (n as u64, n as u64),
250
1
                    Range::UnboundedR(n) => (n as u64, inner_len),
251
2
                    Range::UnboundedL(n) => (0, n as u64),
252
1
                    Range::Bounded(min, max) => (min as u64, max as u64),
253
                };
254
10
                let mut ans = 0u64;
255
73
                for sz in min_sz..=max_sz {
256
73
                    let c = count_combinations(inner_len, sz)?;
257
72
                    ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
258
                }
259
9
                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
                    // need  "multichoose", ((n  k)) == (n+k-1  k)
273
                    // Where n=inner_len and k=sz
274
                    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
1
            GroundDomain::Tuple(domains) => {
280
1
                let mut ans = 1u64;
281
2
                for domain in domains {
282
2
                    ans = ans
283
2
                        .checked_mul(domain.length()?)
284
2
                        .ok_or(DomainOpError::TooLarge)?;
285
                }
286
1
                Ok(ans)
287
            }
288
1
            GroundDomain::Record(entries) => {
289
                // A record is just a named tuple
290
1
                let mut ans = 1u64;
291
2
                for entry in entries {
292
2
                    let sz = entry.domain.length()?;
293
2
                    ans = ans.checked_mul(sz).ok_or(DomainOpError::TooLarge)?;
294
                }
295
1
                Ok(ans)
296
            }
297
4
            GroundDomain::Matrix(inner_domain, idx_domains) => {
298
4
                let inner_sz = inner_domain.length()?;
299
5
                let exp = idx_domains.iter().try_fold(1u32, |acc, val| {
300
5
                    let len = val.length()? as u32;
301
5
                    acc.checked_mul(len).ok_or(DomainOpError::TooLarge)
302
5
                })?;
303
4
                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
604
    }
310

            
311
3360
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
312
        // not adding a generic wildcard condition for all domains, so that this gives a compile
313
        // error when a domain is added.
314
3360
        match self {
315
            // empty domain can't contain anything
316
            GroundDomain::Empty(_) => Ok(false),
317
40
            GroundDomain::Bool => match lit {
318
40
                Literal::Bool(_) => Ok(true),
319
                _ => Ok(false),
320
            },
321
3320
            GroundDomain::Int(ranges) => match lit {
322
3320
                Literal::Int(x) => {
323
                    // unconstrained int domain - contains all integers
324
3320
                    if ranges.is_empty() {
325
                        return Ok(true);
326
3320
                    };
327

            
328
3340
                    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
                    // check if the literal's size is allowed by the set attribute
335
                    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
                    // check if the literal's size is allowed by the mset attribute
352
                    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
                        // Matrix literals are represented as nested 1d matrices, so the elements of
370
                        // the matrix literal will be the inner dimensions of the matrix.
371

            
372
                        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
                            // Base case - we have a 1D row. Now check if all elements in the
383
                            // literal are in this row's element domain.
384
                            elem_domain.as_ref().clone()
385
                        } else {
386
                            // Otherwise, go down a dimension (e.g. 2D matrix inside a 3D tensor)
387
                            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 every element in the tuple literal, check if it is in the corresponding domain
409
                        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
3360
    }
459

            
460
    /// Returns a list of all possible values in an integer domain.
461
    ///
462
    /// # Errors
463
    ///
464
    /// - [`DomainOpError::NotInteger`] if the domain is not an integer domain.
465
    /// - [`DomainOpError::Unbounded`] if the domain is unbounded.
466
10044
    pub fn values_i32(&self) -> Result<Vec<i32>, DomainOpError> {
467
        if let GroundDomain::Empty(ReturnType::Int) = self {
468
            return Ok(vec![]);
469
10044
        }
470
10044
        let GroundDomain::Int(ranges) = self else {
471
            return Err(DomainOpError::NotInteger(self.return_type()));
472
        };
473

            
474
10044
        if ranges.is_empty() {
475
            return Err(DomainOpError::Unbounded);
476
10044
        }
477

            
478
10044
        let mut values = vec![];
479
10044
        for range in ranges {
480
10044
            match range {
481
800
                Range::Single(i) => {
482
800
                    values.push(*i);
483
800
                }
484
9244
                Range::Bounded(i, j) => {
485
9244
                    values.extend(*i..=*j);
486
9244
                }
487
                Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => {
488
                    return Err(DomainOpError::Unbounded);
489
                }
490
            }
491
        }
492

            
493
10044
        Ok(values)
494
10044
    }
495

            
496
    /// Creates an [`Domain::Int`] containing the given integers.
497
    ///
498
    /// # Examples
499
    ///
500
    /// ```
501
    /// use conjure_cp_core::ast::{GroundDomain, Range};
502
    /// use conjure_cp_core::{domain_int_ground,range};
503
    /// use std::collections::BTreeSet;
504
    ///
505
    /// let elements = BTreeSet::from([1,2,3,4,5]);
506
    ///
507
    /// let domain = GroundDomain::from_set_i32(&elements);
508
    ///
509
    /// assert_eq!(domain,domain_int_ground!(1..5));
510
    /// ```
511
    ///
512
    /// ```
513
    /// use conjure_cp_core::ast::{GroundDomain,Range};
514
    /// use conjure_cp_core::{domain_int_ground,range};
515
    /// use std::collections::BTreeSet;
516
    ///
517
    /// let elements = BTreeSet::from([1,2,4,5,7,8,9,10]);
518
    ///
519
    /// let domain = GroundDomain::from_set_i32(&elements);
520
    ///
521
    /// assert_eq!(domain,domain_int_ground!(1..2,4..5,7..10));
522
    /// ```
523
    ///
524
    /// ```
525
    /// use conjure_cp_core::ast::{GroundDomain,Range,ReturnType};
526
    /// use std::collections::BTreeSet;
527
    ///
528
    /// let elements = BTreeSet::from([]);
529
    ///
530
    /// let domain = GroundDomain::from_set_i32(&elements);
531
    ///
532
    /// assert!(matches!(domain,GroundDomain::Empty(ReturnType::Int)))
533
    /// ```
534
6262
    pub fn from_set_i32(elements: &BTreeSet<i32>) -> GroundDomain {
535
6262
        if elements.is_empty() {
536
20
            return GroundDomain::Empty(ReturnType::Int);
537
6242
        }
538
6242
        if elements.len() == 1 {
539
400
            return GroundDomain::Int(vec![Range::Single(*elements.first().unwrap())]);
540
5842
        }
541

            
542
5842
        let mut elems_iter = elements.iter().copied();
543

            
544
5842
        let mut ranges: Vec<Range<i32>> = vec![];
545

            
546
        // Loop over the elements in ascending order, turning all sequential runs of
547
        // numbers into ranges.
548

            
549
        // the bounds of the current run of numbers.
550
5842
        let mut lower = elems_iter
551
5842
            .next()
552
5842
            .expect("if we get here, elements should have => 2 elements");
553
5842
        let mut upper = lower;
554

            
555
29309
        for current in elems_iter {
556
            // As elements is a BTreeSet, current is always strictly larger than lower.
557

            
558
29309
            if current == upper + 1 {
559
                // current is part of the current run - we now have the run lower..current
560
                //
561
19488
                upper = current;
562
19488
            } else {
563
                // the run lower..upper has ended.
564
                //
565
                // Add the run lower..upper to the domain, and start a new run.
566

            
567
9821
                if lower == upper {
568
9540
                    ranges.push(range!(lower));
569
9541
                } else {
570
281
                    ranges.push(range!(lower..upper));
571
281
                }
572

            
573
9821
                lower = current;
574
9821
                upper = current;
575
            }
576
        }
577

            
578
        // add the final run to the domain
579
5842
        if lower == upper {
580
601
            ranges.push(range!(lower));
581
5241
        } else {
582
5241
            ranges.push(range!(lower..upper));
583
5241
        }
584

            
585
5842
        ranges = Range::squeeze(&ranges);
586
5842
        GroundDomain::Int(ranges)
587
6262
    }
588

            
589
    /// Returns the domain that is the result of applying a binary operation to two integer domains.
590
    ///
591
    /// The given operator may return `None` if the operation is not defined for its arguments.
592
    /// Undefined values will not be included in the resulting domain.
593
    ///
594
    /// # Errors
595
    ///
596
    /// - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
597
    /// - [`DomainOpError::NotInteger`] if either of the input domains are not integers.
598
2702
    pub fn apply_i32(
599
2702
        &self,
600
2702
        op: fn(i32, i32) -> Option<i32>,
601
2702
        other: &GroundDomain,
602
2702
    ) -> Result<GroundDomain, DomainOpError> {
603
2702
        let vs1 = self.values_i32()?;
604
2702
        let vs2 = other.values_i32()?;
605

            
606
2702
        let mut set = BTreeSet::new();
607
165972
        for (v1, v2) in itertools::iproduct!(vs1, vs2) {
608
165972
            if let Some(v) = op(v1, v2) {
609
159768
                set.insert(v);
610
159768
            }
611
        }
612

            
613
2702
        Ok(GroundDomain::from_set_i32(&set))
614
2702
    }
615

            
616
    /// Returns true if the domain is finite.
617
940
    pub fn is_finite(&self) -> bool {
618
2840
        for domain in self.universe() {
619
2840
            if let GroundDomain::Int(ranges) = domain {
620
1860
                if ranges.is_empty() {
621
                    return false;
622
1860
                }
623

            
624
1860
                if ranges.iter().any(|range| {
625
1860
                    matches!(
626
1860
                        range,
627
                        Range::UnboundedL(_) | Range::UnboundedR(_) | Range::Unbounded
628
                    )
629
1860
                }) {
630
                    return false;
631
1860
                }
632
980
            }
633
        }
634
940
        true
635
940
    }
636

            
637
    /// For a vector of literals, creates a domain that contains all the elements.
638
    ///
639
    /// The literals must all be of the same type.
640
    ///
641
    /// For abstract literals, this method merges the element domains of the literals, but not the
642
    /// index domains. Thus, for fixed-sized abstract literals (matrices, tuples, records, etc.),
643
    /// all literals in the vector must also have the same size / index domain:
644
    ///
645
    /// + Matrices: all literals must have the same index domain.
646
    /// + Tuples: all literals must have the same number of elements.
647
    /// + Records: all literals must have the same fields.
648
    ///
649
    /// # Errors
650
    ///
651
    /// - [DomainOpError::WrongType] if the input literals are of a different type to
652
    ///   each-other, as described above.
653
    ///
654
    /// # Examples
655
    ///
656
    /// ```
657
    /// use conjure_cp_core::ast::{Range, Literal, ReturnType, GroundDomain};
658
    ///
659
    /// let domain = GroundDomain::from_literal_vec(&vec![]);
660
    /// assert_eq!(domain,Ok(GroundDomain::Empty(ReturnType::Unknown)));
661
    /// ```
662
    ///
663
    /// ```
664
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
665
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
666
    ///
667
    /// // `[1,2;int(2..3)], [4,5; int(2..3)]` has domain
668
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
669
    ///
670
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
671
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(2..3)]);
672
    ///
673
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
674
    ///
675
    /// let expected_domain = Ok(GroundDomain::Matrix(
676
    ///     domain_int_ground!(1..2,4..5),vec![domain_int_ground!(2..3)]));
677
    ///
678
    /// assert_eq!(domain,expected_domain);
679
    /// ```
680
    ///
681
    /// ```
682
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral,DomainOpError};
683
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
684
    ///
685
    /// // `[1,2;int(2..3)], [4,5; int(1..2)]` cannot be combined
686
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
687
    ///
688
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
689
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]);
690
    ///
691
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
692
    ///
693
    /// assert_eq!(domain,Err(DomainOpError::WrongType));
694
    /// ```
695
    ///
696
    /// ```
697
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
698
    /// use conjure_cp_core::{domain_int_ground,range, matrix};
699
    ///
700
    /// // `[[1,2; int(1..2)];int(2)], [[4,5; int(1..2)]; int(2)]` has domain
701
    /// // `matrix indexed by [int(2),int(1..2)] of int(1..2,4..5)`
702
    ///
703
    ///
704
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
705
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
706
    ///
707
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
708
    ///
709
    /// let expected_domain = Ok(GroundDomain::Matrix(
710
    ///     domain_int_ground!(1..2,4..5),
711
    ///     vec![domain_int_ground!(2),domain_int_ground!(1..2)]));
712
    ///
713
    /// assert_eq!(domain,expected_domain);
714
    /// ```
715
    ///
716
    ///
717
3140
    pub fn from_literal_vec(literals: &[Literal]) -> Result<GroundDomain, DomainOpError> {
718
        // TODO: use proptest to test this better?
719

            
720
3140
        if literals.is_empty() {
721
20
            return Ok(GroundDomain::Empty(ReturnType::Unknown));
722
3120
        }
723

            
724
3120
        let first_literal = literals.first().unwrap();
725

            
726
1520
        match first_literal {
727
            Literal::Int(_) => {
728
                // check all literals are ints, then pass this to Domain::from_set_i32.
729
1540
                let mut ints = BTreeSet::new();
730
5660
                for lit in literals {
731
5660
                    let Literal::Int(i) = lit else {
732
                        return Err(DomainOpError::WrongType);
733
                    };
734

            
735
5660
                    ints.insert(*i);
736
                }
737

            
738
1540
                Ok(GroundDomain::from_set_i32(&ints))
739
            }
740
            Literal::Bool(_) => {
741
                // check all literals are bools
742
60
                if literals.iter().any(|x| !matches!(x, Literal::Bool(_))) {
743
                    Err(DomainOpError::WrongType)
744
                } else {
745
60
                    Ok(GroundDomain::Bool)
746
                }
747
            }
748
            Literal::AbstractLiteral(AbstractLiteral::Set(_)) => {
749
240
                let mut all_elems = vec![];
750

            
751
240
                for lit in literals {
752
240
                    let Literal::AbstractLiteral(AbstractLiteral::Set(elems)) = lit else {
753
                        return Err(DomainOpError::WrongType);
754
                    };
755

            
756
240
                    all_elems.extend(elems.clone());
757
                }
758
240
                let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
759

            
760
240
                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
1180
            l @ Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _)) => {
780
1180
                let mut first_index_domain = vec![];
781
                // flatten index domains of n-d matrix into list
782
1180
                let mut l = l.clone();
783
1320
                while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
784
1320
                    assert!(
785
1320
                        !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
786
                        "n-dimensional matrix literals should be represented as a matrix inside a matrix"
787
                    );
788
1320
                    first_index_domain.push(idx);
789
1320
                    l = elems[0].clone();
790
                }
791

            
792
1180
                let mut all_elems: Vec<Literal> = vec![];
793

            
794
                // check types and index domains
795
1240
                for lit in literals {
796
1240
                    let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = lit else {
797
                        return Err(DomainOpError::NotGround);
798
                    };
799

            
800
1240
                    all_elems.extend(elems.clone());
801

            
802
1240
                    let mut index_domain = vec![idx.clone()];
803
1240
                    let mut l = elems[0].clone();
804
160
                    while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
805
160
                        assert!(
806
160
                            !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
807
                            "n-dimensional matrix literals should be represented as a matrix inside a matrix"
808
                        );
809
160
                        index_domain.push(idx);
810
160
                        l = elems[0].clone();
811
                    }
812

            
813
1240
                    if index_domain != first_index_domain {
814
20
                        return Err(DomainOpError::WrongType);
815
1220
                    }
816
                }
817

            
818
                // extract all the terminal elements (those that are not nested matrix literals) from the matrix literal.
819
1160
                let mut terminal_elements: Vec<Literal> = vec![];
820
6520
                while let Some(elem) = all_elems.pop() {
821
400
                    if let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, _)) = elem {
822
400
                        all_elems.extend(elems);
823
4960
                    } else {
824
4960
                        terminal_elements.push(elem);
825
4960
                    }
826
                }
827

            
828
1160
                let element_domain = GroundDomain::from_literal_vec(&terminal_elements)?;
829

            
830
1160
                Ok(GroundDomain::Matrix(
831
1160
                    Moo::new(element_domain),
832
1160
                    first_index_domain,
833
1160
                ))
834
            }
835

            
836
40
            Literal::AbstractLiteral(AbstractLiteral::Tuple(first_elems)) => {
837
40
                let n_fields = first_elems.len();
838

            
839
                // for each field, calculate the element domain and add it to this list
840
40
                let mut elem_domains = vec![];
841

            
842
80
                for i in 0..n_fields {
843
80
                    let mut all_elems = vec![];
844
80
                    for lit in literals {
845
80
                        let Literal::AbstractLiteral(AbstractLiteral::Tuple(elems)) = lit else {
846
                            return Err(DomainOpError::NotGround);
847
                        };
848

            
849
80
                        if elems.len() != n_fields {
850
                            return Err(DomainOpError::NotGround);
851
80
                        }
852

            
853
80
                        all_elems.push(elems[i].clone());
854
                    }
855

            
856
80
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
857
                }
858

            
859
40
                Ok(GroundDomain::Tuple(elem_domains))
860
            }
861

            
862
60
            Literal::AbstractLiteral(AbstractLiteral::Record(first_elems)) => {
863
60
                let n_fields = first_elems.len();
864
120
                let field_names = first_elems.iter().map(|x| x.name.clone()).collect_vec();
865

            
866
                // for each field, calculate the element domain and add it to this list
867
60
                let mut elem_domains = vec![];
868

            
869
120
                for i in 0..n_fields {
870
120
                    let mut all_elems = vec![];
871
120
                    for lit in literals {
872
120
                        let Literal::AbstractLiteral(AbstractLiteral::Record(elems)) = lit else {
873
                            return Err(DomainOpError::NotGround);
874
                        };
875

            
876
120
                        if elems.len() != n_fields {
877
                            return Err(DomainOpError::NotGround);
878
120
                        }
879

            
880
120
                        let elem = elems[i].clone();
881
120
                        if elem.name != field_names[i] {
882
                            return Err(DomainOpError::NotGround);
883
120
                        }
884

            
885
120
                        all_elems.push(elem.value);
886
                    }
887

            
888
120
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
889
                }
890

            
891
                Ok(GroundDomain::Record(
892
60
                    izip!(field_names, elem_domains)
893
120
                        .map(|(name, domain)| RecordEntryGround { name, domain })
894
60
                        .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
                // Check that all items have the same domains
909
                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
3140
    }
925

            
926
20
    pub fn element_domain(&self) -> Option<Moo<GroundDomain>> {
927
20
        match self {
928
20
            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
20
    }
934
}
935

            
936
impl Typeable for GroundDomain {
937
117280
    fn return_type(&self) -> ReturnType {
938
117280
        match self {
939
            GroundDomain::Empty(ty) => ty.clone(),
940
8720
            GroundDomain::Bool => ReturnType::Bool,
941
84020
            GroundDomain::Int(_) => ReturnType::Int,
942
240
            GroundDomain::Set(_attr, inner) => ReturnType::Set(Box::new(inner.return_type())),
943
            GroundDomain::MSet(_attr, inner) => ReturnType::MSet(Box::new(inner.return_type())),
944
21780
            GroundDomain::Matrix(inner, _idx) => ReturnType::Matrix(Box::new(inner.return_type())),
945
1120
            GroundDomain::Tuple(inners) => {
946
1120
                let mut inner_types = Vec::new();
947
2240
                for inner in inners {
948
2240
                    inner_types.push(inner.return_type());
949
2240
                }
950
1120
                ReturnType::Tuple(inner_types)
951
            }
952
1400
            GroundDomain::Record(entries) => {
953
1400
                let mut entry_types = Vec::new();
954
2800
                for entry in entries {
955
2800
                    entry_types.push(entry.domain.return_type());
956
2800
                }
957
1400
                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
117280
    }
964
}
965

            
966
impl Display for GroundDomain {
967
194828
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
968
194828
        match &self {
969
            GroundDomain::Empty(ty) => write!(f, "empty({ty:?})"),
970
9088
            GroundDomain::Bool => write!(f, "bool"),
971
181660
            GroundDomain::Int(ranges) => {
972
181660
                if ranges.iter().all(Range::is_lower_or_upper_bounded) {
973
189860
                    let rngs: String = ranges.iter().map(|r| format!("{r}")).join(", ");
974
181660
                    write!(f, "int({})", rngs)
975
                } else {
976
                    write!(f, "int")
977
                }
978
            }
979
320
            GroundDomain::Set(attrs, inner_dom) => write!(f, "set {attrs} of {inner_dom}"),
980
360
            GroundDomain::MSet(attrs, inner_dom) => write!(f, "mset {attrs} of {inner_dom}"),
981
2560
            GroundDomain::Matrix(value_domain, index_domains) => {
982
2560
                write!(
983
2560
                    f,
984
                    "matrix indexed by [{}] of {value_domain}",
985
2560
                    pretty_vec(&index_domains.iter().collect_vec())
986
                )
987
            }
988
280
            GroundDomain::Tuple(domains) => {
989
280
                write!(
990
280
                    f,
991
                    "tuple of ({})",
992
280
                    pretty_vec(&domains.iter().collect_vec())
993
                )
994
            }
995
40
            GroundDomain::Record(entries) => {
996
40
                write!(
997
40
                    f,
998
                    "record of ({})",
999
40
                    pretty_vec(
40
                        &entries
40
                            .iter()
80
                            .map(|entry| format!("{}: {}", entry.name, entry.domain))
40
                            .collect_vec()
                    )
                )
            }
520
            GroundDomain::Function(attribute, domain, codomain) => {
520
                write!(f, "function {} {} --> {} ", attribute, domain, codomain)
            }
        }
194828
    }
}