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

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

            
26
impl From<RecordEntryGround> for RecordEntry {
27
    fn from(value: RecordEntryGround) -> Self {
28
        RecordEntry {
29
            name: value.name,
30
            domain: value.domain.into(),
31
        }
32
    }
33
}
34

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

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

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

            
71
impl GroundDomain {
72
    pub fn union(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
73
        match (self, other) {
74
            (GroundDomain::Empty(ty), dom) | (dom, GroundDomain::Empty(ty)) => {
75
                if *ty == dom.return_type() {
76
                    Ok(dom.clone())
77
                } else {
78
                    Err(DomainOpError::WrongType)
79
                }
80
            }
81
            (GroundDomain::Bool, GroundDomain::Bool) => Ok(GroundDomain::Bool),
82
            (GroundDomain::Bool, _) | (_, GroundDomain::Bool) => Err(DomainOpError::WrongType),
83
            (GroundDomain::Int(r1), GroundDomain::Int(r2)) => {
84
                let mut rngs = r1.clone();
85
                rngs.extend(r2.clone());
86
                Ok(GroundDomain::Int(Range::squeeze(&rngs)))
87
            }
88
            (GroundDomain::Int(_), _) | (_, GroundDomain::Int(_)) => Err(DomainOpError::WrongType),
89
            (GroundDomain::Set(_, in1), GroundDomain::Set(_, in2)) => Ok(GroundDomain::Set(
90
                SetAttr::default(),
91
                Moo::new(in1.union(in2)?),
92
            )),
93
            (GroundDomain::Set(_, _), _) | (_, GroundDomain::Set(_, _)) => {
94
                Err(DomainOpError::WrongType)
95
            }
96
            (GroundDomain::Matrix(in1, idx1), GroundDomain::Matrix(in2, idx2)) if idx1 == idx2 => {
97
                Ok(GroundDomain::Matrix(
98
                    Moo::new(in1.union(in2)?),
99
                    idx1.clone(),
100
                ))
101
            }
102
            (GroundDomain::Matrix(_, _), _) | (_, GroundDomain::Matrix(_, _)) => {
103
                Err(DomainOpError::WrongType)
104
            }
105
            (GroundDomain::Tuple(in1s), GroundDomain::Tuple(in2s)) if in1s.len() == in2s.len() => {
106
                let mut inners = Vec::new();
107
                for (in1, in2) in zip(in1s, in2s) {
108
                    inners.push(Moo::new(in1.union(in2)?));
109
                }
110
                Ok(GroundDomain::Tuple(inners))
111
            }
112
            (GroundDomain::Tuple(_), _) | (_, GroundDomain::Tuple(_)) => {
113
                Err(DomainOpError::WrongType)
114
            }
115
            // TODO: Eventually we may define semantics for joining record domains. This day is not today.
116
            #[allow(unreachable_patterns)]
117
            // Technically redundant but logically clearer to have both
118
            (GroundDomain::Record(_), _) | (_, GroundDomain::Record(_)) => {
119
                Err(DomainOpError::WrongType)
120
            }
121
            #[allow(unreachable_patterns)]
122
            // Technically redundant but logically clearer to have both
123
            (GroundDomain::Function(_, _, _), _) | (_, GroundDomain::Function(_, _, _)) => {
124
                Err(DomainOpError::WrongType)
125
            }
126
        }
127
    }
128

            
129
    /// Calculates the intersection of two domains.
130
    ///
131
    /// # Errors
132
    ///
133
    ///  - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
134
    ///  - [`DomainOpError::WrongType`] if the input domains are different types, or are not integer or set domains.
135
    pub fn intersect(&self, other: &GroundDomain) -> Result<GroundDomain, DomainOpError> {
136
        // TODO: does not consider unbounded domains yet
137
        // needs to be tested once comprehension rules are written
138

            
139
        match (self, other) {
140
            // one or more arguments is an empty int domain
141
            (d @ GroundDomain::Empty(ReturnType::Int), GroundDomain::Int(_)) => Ok(d.clone()),
142
            (GroundDomain::Int(_), d @ GroundDomain::Empty(ReturnType::Int)) => Ok(d.clone()),
143
            (GroundDomain::Empty(ReturnType::Int), d @ GroundDomain::Empty(ReturnType::Int)) => {
144
                Ok(d.clone())
145
            }
146

            
147
            // one or more arguments is an empty set(int) domain
148
            (GroundDomain::Set(_, inner1), d @ GroundDomain::Empty(ReturnType::Set(inner2)))
149
                if matches!(
150
                    **inner1,
151
                    GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
152
                ) && matches!(**inner2, ReturnType::Int) =>
153
            {
154
                Ok(d.clone())
155
            }
156
            (d @ GroundDomain::Empty(ReturnType::Set(inner1)), GroundDomain::Set(_, inner2))
157
                if matches!(**inner1, ReturnType::Int)
158
                    && matches!(
159
                        **inner2,
160
                        GroundDomain::Int(_) | GroundDomain::Empty(ReturnType::Int)
161
                    ) =>
162
            {
163
                Ok(d.clone())
164
            }
165
            (
166
                d @ GroundDomain::Empty(ReturnType::Set(inner1)),
167
                GroundDomain::Empty(ReturnType::Set(inner2)),
168
            ) if matches!(**inner1, ReturnType::Int) && matches!(**inner2, ReturnType::Int) => {
169
                Ok(d.clone())
170
            }
171

            
172
            // both arguments are non-empy
173
            (GroundDomain::Set(_, x), GroundDomain::Set(_, y)) => Ok(GroundDomain::Set(
174
                SetAttr::default(),
175
                Moo::new((*x).intersect(y)?),
176
            )),
177

            
178
            (GroundDomain::Int(_), GroundDomain::Int(_)) => {
179
                let mut v: BTreeSet<i32> = BTreeSet::new();
180

            
181
                let v1 = self.values_i32()?;
182
                let v2 = other.values_i32()?;
183
                for value1 in v1.iter() {
184
                    if v2.contains(value1) && !v.contains(value1) {
185
                        v.insert(*value1);
186
                    }
187
                }
188
                Ok(GroundDomain::from_set_i32(&v))
189
            }
190
            _ => Err(DomainOpError::WrongType),
191
        }
192
    }
193

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

            
214
    /// Gets the length of this domain.
215
    ///
216
    /// # Errors
217
    ///
218
    /// - [`DomainOpError::Unbounded`] if the input domain is of infinite size.
219
    pub fn length(&self) -> Result<u64, DomainOpError> {
220
        match self {
221
            GroundDomain::Empty(_) => Ok(0),
222
            GroundDomain::Bool => Ok(2),
223
            GroundDomain::Int(ranges) => {
224
                if ranges.is_empty() {
225
                    return Err(DomainOpError::Unbounded);
226
                }
227

            
228
                let mut length = 0u64;
229
                for range in ranges {
230
                    if let Some(range_length) = range.length() {
231
                        length += range_length as u64;
232
                    } else {
233
                        return Err(DomainOpError::Unbounded);
234
                    }
235
                }
236
                Ok(length)
237
            }
238
            GroundDomain::Set(set_attr, inner_domain) => {
239
                let inner_len = inner_domain.length()?;
240
                let (min_sz, max_sz) = match set_attr.size {
241
                    Range::Unbounded => (0, inner_len),
242
                    Range::Single(n) => (n as u64, n as u64),
243
                    Range::UnboundedR(n) => (n as u64, inner_len),
244
                    Range::UnboundedL(n) => (0, n as u64),
245
                    Range::Bounded(min, max) => (min as u64, max as u64),
246
                };
247
                let mut ans = 0u64;
248
                for sz in min_sz..=max_sz {
249
                    let c = count_combinations(inner_len, sz)?;
250
                    ans = ans.checked_add(c).ok_or(DomainOpError::TooLarge)?;
251
                }
252
                Ok(ans)
253
            }
254
            GroundDomain::Tuple(domains) => {
255
                let mut ans = 1u64;
256
                for domain in domains {
257
                    ans = ans
258
                        .checked_mul(domain.length()?)
259
                        .ok_or(DomainOpError::TooLarge)?;
260
                }
261
                Ok(ans)
262
            }
263
            GroundDomain::Record(entries) => {
264
                // A record is just a named tuple
265
                let mut ans = 1u64;
266
                for entry in entries {
267
                    let sz = entry.domain.length()?;
268
                    ans = ans.checked_mul(sz).ok_or(DomainOpError::TooLarge)?;
269
                }
270
                Ok(ans)
271
            }
272
            GroundDomain::Matrix(inner_domain, idx_domains) => {
273
                let inner_sz = inner_domain.length()?;
274
                let exp = idx_domains.iter().try_fold(1u32, |acc, val| {
275
                    let len = val.length()? as u32;
276
                    acc.checked_mul(len).ok_or(DomainOpError::TooLarge)
277
                })?;
278
                inner_sz.checked_pow(exp).ok_or(DomainOpError::TooLarge)
279
            }
280
            GroundDomain::Function(_, _, _) => {
281
                todo!("Length bound of functions is not yet supported")
282
            }
283
        }
284
    }
285

            
286
    pub fn contains(&self, lit: &Literal) -> Result<bool, DomainOpError> {
287
        // not adding a generic wildcard condition for all domains, so that this gives a compile
288
        // error when a domain is added.
289
        match self {
290
            // empty domain can't contain anything
291
            GroundDomain::Empty(_) => Ok(false),
292
            GroundDomain::Bool => match lit {
293
                Literal::Bool(_) => Ok(true),
294
                _ => Ok(false),
295
            },
296
            GroundDomain::Int(ranges) => match lit {
297
                Literal::Int(x) => {
298
                    // unconstrained int domain - contains all integers
299
                    if ranges.is_empty() {
300
                        return Ok(true);
301
                    };
302

            
303
                    Ok(ranges.iter().any(|range| range.contains(x)))
304
                }
305
                _ => Ok(false),
306
            },
307
            GroundDomain::Set(set_attr, inner_domain) => match lit {
308
                Literal::AbstractLiteral(AbstractLiteral::Set(lit_elems)) => {
309
                    // check if the literal's size is allowed by the set attribute
310
                    let sz = lit_elems.len().to_i32().ok_or(DomainOpError::TooLarge)?;
311
                    if !set_attr.size.contains(&sz) {
312
                        return Ok(false);
313
                    }
314

            
315
                    for elem in lit_elems {
316
                        if !inner_domain.contains(elem)? {
317
                            return Ok(false);
318
                        }
319
                    }
320
                    Ok(true)
321
                }
322
                _ => Ok(false),
323
            },
324
            GroundDomain::Matrix(elem_domain, index_domains) => {
325
                match lit {
326
                    Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx_domain)) => {
327
                        // Matrix literals are represented as nested 1d matrices, so the elements of
328
                        // the matrix literal will be the inner dimensions of the matrix.
329

            
330
                        let mut index_domains = index_domains.clone();
331
                        if index_domains
332
                            .pop()
333
                            .expect("a matrix should have at least one index domain")
334
                            != *idx_domain
335
                        {
336
                            return Ok(false);
337
                        };
338

            
339
                        let next_elem_domain = if index_domains.is_empty() {
340
                            // Base case - we have a 1D row. Now check if all elements in the
341
                            // literal are in this row's element domain.
342
                            elem_domain.as_ref().clone()
343
                        } else {
344
                            // Otherwise, go down a dimension (e.g. 2D matrix inside a 3D tensor)
345
                            GroundDomain::Matrix(elem_domain.clone(), index_domains)
346
                        };
347

            
348
                        for elem in elems {
349
                            if !next_elem_domain.contains(elem)? {
350
                                return Ok(false);
351
                            }
352
                        }
353

            
354
                        Ok(true)
355
                    }
356
                    _ => Ok(false),
357
                }
358
            }
359
            GroundDomain::Tuple(elem_domains) => {
360
                match lit {
361
                    Literal::AbstractLiteral(AbstractLiteral::Tuple(literal_elems)) => {
362
                        if elem_domains.len() != literal_elems.len() {
363
                            return Ok(false);
364
                        }
365

            
366
                        // for every element in the tuple literal, check if it is in the corresponding domain
367
                        for (elem_domain, elem) in itertools::izip!(elem_domains, literal_elems) {
368
                            if !elem_domain.contains(elem)? {
369
                                return Ok(false);
370
                            }
371
                        }
372

            
373
                        Ok(true)
374
                    }
375
                    _ => Ok(false),
376
                }
377
            }
378
            GroundDomain::Record(entries) => match lit {
379
                Literal::AbstractLiteral(AbstractLiteral::Record(lit_entries)) => {
380
                    if entries.len() != lit_entries.len() {
381
                        return Ok(false);
382
                    }
383

            
384
                    for (entry, lit_entry) in itertools::izip!(entries, lit_entries) {
385
                        if entry.name != lit_entry.name
386
                            || !(entry.domain.contains(&lit_entry.value)?)
387
                        {
388
                            return Ok(false);
389
                        }
390
                    }
391
                    Ok(true)
392
                }
393
                _ => Ok(false),
394
            },
395
            GroundDomain::Function(func_attr, domain, codomain) => match lit {
396
                Literal::AbstractLiteral(AbstractLiteral::Function(lit_elems)) => {
397
                    let sz = Int::try_from(lit_elems.len()).expect("Should convert");
398
                    if !func_attr.size.contains(&sz) {
399
                        return Ok(false);
400
                    }
401
                    for lit in lit_elems {
402
                        let domain_element = &lit.0;
403
                        let codomain_element = &lit.1;
404
                        if !domain.contains(domain_element)? {
405
                            return Ok(false);
406
                        }
407
                        if !codomain.contains(codomain_element)? {
408
                            return Ok(false);
409
                        }
410
                    }
411
                    Ok(true)
412
                }
413
                _ => Ok(false),
414
            },
415
        }
416
    }
417

            
418
    /// Returns a list of all possible values in an integer domain.
419
    ///
420
    /// # Errors
421
    ///
422
    /// - [`DomainOpError::NotInteger`] if the domain is not an integer domain.
423
    /// - [`DomainOpError::Unbounded`] if the domain is unbounded.
424
    pub fn values_i32(&self) -> Result<Vec<i32>, DomainOpError> {
425
        if let GroundDomain::Empty(ReturnType::Int) = self {
426
            return Ok(vec![]);
427
        }
428
        let GroundDomain::Int(ranges) = self else {
429
            return Err(DomainOpError::NotInteger(self.return_type()));
430
        };
431

            
432
        if ranges.is_empty() {
433
            return Err(DomainOpError::Unbounded);
434
        }
435

            
436
        let mut values = vec![];
437
        for range in ranges {
438
            match range {
439
                Range::Single(i) => {
440
                    values.push(*i);
441
                }
442
                Range::Bounded(i, j) => {
443
                    values.extend(*i..=*j);
444
                }
445
                Range::UnboundedR(_) | Range::UnboundedL(_) | Range::Unbounded => {
446
                    return Err(DomainOpError::Unbounded);
447
                }
448
            }
449
        }
450

            
451
        Ok(values)
452
    }
453

            
454
    /// Creates an [`Domain::Int`] containing the given integers.
455
    ///
456
    /// # Examples
457
    ///
458
    /// ```
459
    /// use conjure_cp_core::ast::{GroundDomain, Range};
460
    /// use conjure_cp_core::{domain_int_ground,range};
461
    /// use std::collections::BTreeSet;
462
    ///
463
    /// let elements = BTreeSet::from([1,2,3,4,5]);
464
    ///
465
    /// let domain = GroundDomain::from_set_i32(&elements);
466
    ///
467
    /// assert_eq!(domain,domain_int_ground!(1..5));
468
    /// ```
469
    ///
470
    /// ```
471
    /// use conjure_cp_core::ast::{GroundDomain,Range};
472
    /// use conjure_cp_core::{domain_int_ground,range};
473
    /// use std::collections::BTreeSet;
474
    ///
475
    /// let elements = BTreeSet::from([1,2,4,5,7,8,9,10]);
476
    ///
477
    /// let domain = GroundDomain::from_set_i32(&elements);
478
    ///
479
    /// assert_eq!(domain,domain_int_ground!(1..2,4..5,7..10));
480
    /// ```
481
    ///
482
    /// ```
483
    /// use conjure_cp_core::ast::{GroundDomain,Range,ReturnType};
484
    /// use std::collections::BTreeSet;
485
    ///
486
    /// let elements = BTreeSet::from([]);
487
    ///
488
    /// let domain = GroundDomain::from_set_i32(&elements);
489
    ///
490
    /// assert!(matches!(domain,GroundDomain::Empty(ReturnType::Int)))
491
    /// ```
492
    pub fn from_set_i32(elements: &BTreeSet<i32>) -> GroundDomain {
493
        if elements.is_empty() {
494
            return GroundDomain::Empty(ReturnType::Int);
495
        }
496
        if elements.len() == 1 {
497
            return GroundDomain::Int(vec![Range::Single(*elements.first().unwrap())]);
498
        }
499

            
500
        let mut elems_iter = elements.iter().copied();
501

            
502
        let mut ranges: Vec<Range<i32>> = vec![];
503

            
504
        // Loop over the elements in ascending order, turning all sequential runs of
505
        // numbers into ranges.
506

            
507
        // the bounds of the current run of numbers.
508
        let mut lower = elems_iter
509
            .next()
510
            .expect("if we get here, elements should have => 2 elements");
511
        let mut upper = lower;
512

            
513
        for current in elems_iter {
514
            // As elements is a BTreeSet, current is always strictly larger than lower.
515

            
516
            if current == upper + 1 {
517
                // current is part of the current run - we now have the run lower..current
518
                //
519
                upper = current;
520
            } else {
521
                // the run lower..upper has ended.
522
                //
523
                // Add the run lower..upper to the domain, and start a new run.
524

            
525
                if lower == upper {
526
                    ranges.push(range!(lower));
527
                } else {
528
                    ranges.push(range!(lower..upper));
529
                }
530

            
531
                lower = current;
532
                upper = current;
533
            }
534
        }
535

            
536
        // add the final run to the domain
537
        if lower == upper {
538
            ranges.push(range!(lower));
539
        } else {
540
            ranges.push(range!(lower..upper));
541
        }
542

            
543
        ranges = Range::squeeze(&ranges);
544
        GroundDomain::Int(ranges)
545
    }
546

            
547
    /// Returns the domain that is the result of applying a binary operation to two integer domains.
548
    ///
549
    /// The given operator may return `None` if the operation is not defined for its arguments.
550
    /// Undefined values will not be included in the resulting domain.
551
    ///
552
    /// # Errors
553
    ///
554
    /// - [`DomainOpError::Unbounded`] if either of the input domains are unbounded.
555
    /// - [`DomainOpError::NotInteger`] if either of the input domains are not integers.
556
    pub fn apply_i32(
557
        &self,
558
        op: fn(i32, i32) -> Option<i32>,
559
        other: &GroundDomain,
560
    ) -> Result<GroundDomain, DomainOpError> {
561
        let vs1 = self.values_i32()?;
562
        let vs2 = other.values_i32()?;
563

            
564
        let mut set = BTreeSet::new();
565
        for (v1, v2) in itertools::iproduct!(vs1, vs2) {
566
            if let Some(v) = op(v1, v2) {
567
                set.insert(v);
568
            }
569
        }
570

            
571
        Ok(GroundDomain::from_set_i32(&set))
572
    }
573

            
574
    /// Returns true if the domain is finite.
575
    pub fn is_finite(&self) -> bool {
576
        for domain in self.universe() {
577
            if let GroundDomain::Int(ranges) = domain {
578
                if ranges.is_empty() {
579
                    return false;
580
                }
581

            
582
                if ranges.iter().any(|range| {
583
                    matches!(
584
                        range,
585
                        Range::UnboundedL(_) | Range::UnboundedR(_) | Range::Unbounded
586
                    )
587
                }) {
588
                    return false;
589
                }
590
            }
591
        }
592
        true
593
    }
594

            
595
    /// For a vector of literals, creates a domain that contains all the elements.
596
    ///
597
    /// The literals must all be of the same type.
598
    ///
599
    /// For abstract literals, this method merges the element domains of the literals, but not the
600
    /// index domains. Thus, for fixed-sized abstract literals (matrices, tuples, records, etc.),
601
    /// all literals in the vector must also have the same size / index domain:
602
    ///
603
    /// + Matrices: all literals must have the same index domain.
604
    /// + Tuples: all literals must have the same number of elements.
605
    /// + Records: all literals must have the same fields.
606
    ///
607
    /// # Errors
608
    ///
609
    /// - [DomainOpError::WrongType] if the input literals are of a different type to
610
    ///   each-other, as described above.
611
    ///
612
    /// # Examples
613
    ///
614
    /// ```
615
    /// use conjure_cp_core::ast::{Range, Literal, ReturnType, GroundDomain};
616
    ///
617
    /// let domain = GroundDomain::from_literal_vec(&vec![]);
618
    /// assert_eq!(domain,Ok(GroundDomain::Empty(ReturnType::Unknown)));
619
    /// ```
620
    ///
621
    /// ```
622
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
623
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
624
    ///
625
    /// // `[1,2;int(2..3)], [4,5; int(2..3)]` has domain
626
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
627
    ///
628
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
629
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(2..3)]);
630
    ///
631
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
632
    ///
633
    /// let expected_domain = Ok(GroundDomain::Matrix(
634
    ///     domain_int_ground!(1..2,4..5),vec![domain_int_ground!(2..3)]));
635
    ///
636
    /// assert_eq!(domain,expected_domain);
637
    /// ```
638
    ///
639
    /// ```
640
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral,DomainOpError};
641
    /// use conjure_cp_core::{domain_int_ground, range, matrix};
642
    ///
643
    /// // `[1,2;int(2..3)], [4,5; int(1..2)]` cannot be combined
644
    /// // `matrix indexed by [int(2..3)] of int(1..2,4..5)`
645
    ///
646
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(2..3)]);
647
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]);
648
    ///
649
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
650
    ///
651
    /// assert_eq!(domain,Err(DomainOpError::WrongType));
652
    /// ```
653
    ///
654
    /// ```
655
    /// use conjure_cp_core::ast::{GroundDomain,Range,Literal, AbstractLiteral};
656
    /// use conjure_cp_core::{domain_int_ground,range, matrix};
657
    ///
658
    /// // `[[1,2; int(1..2)];int(2)], [[4,5; int(1..2)]; int(2)]` has domain
659
    /// // `matrix indexed by [int(2),int(1..2)] of int(1..2,4..5)`
660
    ///
661
    ///
662
    /// let matrix_1 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(1),Literal::Int(2);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
663
    /// let matrix_2 = Literal::AbstractLiteral(matrix![Literal::AbstractLiteral(matrix![Literal::Int(4),Literal::Int(5);domain_int_ground!(1..2)]); domain_int_ground!(2)]);
664
    ///
665
    /// let domain = GroundDomain::from_literal_vec(&vec![matrix_1,matrix_2]);
666
    ///
667
    /// let expected_domain = Ok(GroundDomain::Matrix(
668
    ///     domain_int_ground!(1..2,4..5),
669
    ///     vec![domain_int_ground!(2),domain_int_ground!(1..2)]));
670
    ///
671
    /// assert_eq!(domain,expected_domain);
672
    /// ```
673
    ///
674
    ///
675
    pub fn from_literal_vec(literals: &[Literal]) -> Result<GroundDomain, DomainOpError> {
676
        // TODO: use proptest to test this better?
677

            
678
        if literals.is_empty() {
679
            return Ok(GroundDomain::Empty(ReturnType::Unknown));
680
        }
681

            
682
        let first_literal = literals.first().unwrap();
683

            
684
        match first_literal {
685
            Literal::Int(_) => {
686
                // check all literals are ints, then pass this to Domain::from_set_i32.
687
                let mut ints = BTreeSet::new();
688
                for lit in literals {
689
                    let Literal::Int(i) = lit else {
690
                        return Err(DomainOpError::WrongType);
691
                    };
692

            
693
                    ints.insert(*i);
694
                }
695

            
696
                Ok(GroundDomain::from_set_i32(&ints))
697
            }
698
            Literal::Bool(_) => {
699
                // check all literals are bools
700
                if literals.iter().any(|x| !matches!(x, Literal::Bool(_))) {
701
                    Err(DomainOpError::WrongType)
702
                } else {
703
                    Ok(GroundDomain::Bool)
704
                }
705
            }
706
            Literal::AbstractLiteral(AbstractLiteral::Set(_)) => {
707
                let mut all_elems = vec![];
708

            
709
                for lit in literals {
710
                    let Literal::AbstractLiteral(AbstractLiteral::Set(elems)) = lit else {
711
                        return Err(DomainOpError::WrongType);
712
                    };
713

            
714
                    all_elems.extend(elems.clone());
715
                }
716
                let elem_domain = GroundDomain::from_literal_vec(&all_elems)?;
717

            
718
                Ok(GroundDomain::Set(SetAttr::default(), Moo::new(elem_domain)))
719
            }
720

            
721
            l @ Literal::AbstractLiteral(AbstractLiteral::Matrix(_, _)) => {
722
                let mut first_index_domain = vec![];
723
                // flatten index domains of n-d matrix into list
724
                let mut l = l.clone();
725
                while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
726
                    assert!(
727
                        !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
728
                        "n-dimensional matrix literals should be represented as a matrix inside a matrix"
729
                    );
730
                    first_index_domain.push(idx);
731
                    l = elems[0].clone();
732
                }
733

            
734
                let mut all_elems: Vec<Literal> = vec![];
735

            
736
                // check types and index domains
737
                for lit in literals {
738
                    let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = lit else {
739
                        return Err(DomainOpError::NotGround);
740
                    };
741

            
742
                    all_elems.extend(elems.clone());
743

            
744
                    let mut index_domain = vec![idx.clone()];
745
                    let mut l = elems[0].clone();
746
                    while let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, idx)) = l {
747
                        assert!(
748
                            !matches!(idx.as_ref(), GroundDomain::Matrix(_, _)),
749
                            "n-dimensional matrix literals should be represented as a matrix inside a matrix"
750
                        );
751
                        index_domain.push(idx);
752
                        l = elems[0].clone();
753
                    }
754

            
755
                    if index_domain != first_index_domain {
756
                        return Err(DomainOpError::WrongType);
757
                    }
758
                }
759

            
760
                // extract all the terminal elements (those that are not nested matrix literals) from the matrix literal.
761
                let mut terminal_elements: Vec<Literal> = vec![];
762
                while let Some(elem) = all_elems.pop() {
763
                    if let Literal::AbstractLiteral(AbstractLiteral::Matrix(elems, _)) = elem {
764
                        all_elems.extend(elems);
765
                    } else {
766
                        terminal_elements.push(elem);
767
                    }
768
                }
769

            
770
                let element_domain = GroundDomain::from_literal_vec(&terminal_elements)?;
771

            
772
                Ok(GroundDomain::Matrix(
773
                    Moo::new(element_domain),
774
                    first_index_domain,
775
                ))
776
            }
777

            
778
            Literal::AbstractLiteral(AbstractLiteral::Tuple(first_elems)) => {
779
                let n_fields = first_elems.len();
780

            
781
                // for each field, calculate the element domain and add it to this list
782
                let mut elem_domains = vec![];
783

            
784
                for i in 0..n_fields {
785
                    let mut all_elems = vec![];
786
                    for lit in literals {
787
                        let Literal::AbstractLiteral(AbstractLiteral::Tuple(elems)) = lit else {
788
                            return Err(DomainOpError::NotGround);
789
                        };
790

            
791
                        if elems.len() != n_fields {
792
                            return Err(DomainOpError::NotGround);
793
                        }
794

            
795
                        all_elems.push(elems[i].clone());
796
                    }
797

            
798
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
799
                }
800

            
801
                Ok(GroundDomain::Tuple(elem_domains))
802
            }
803

            
804
            Literal::AbstractLiteral(AbstractLiteral::Record(first_elems)) => {
805
                let n_fields = first_elems.len();
806
                let field_names = first_elems.iter().map(|x| x.name.clone()).collect_vec();
807

            
808
                // for each field, calculate the element domain and add it to this list
809
                let mut elem_domains = vec![];
810

            
811
                for i in 0..n_fields {
812
                    let mut all_elems = vec![];
813
                    for lit in literals {
814
                        let Literal::AbstractLiteral(AbstractLiteral::Record(elems)) = lit else {
815
                            return Err(DomainOpError::NotGround);
816
                        };
817

            
818
                        if elems.len() != n_fields {
819
                            return Err(DomainOpError::NotGround);
820
                        }
821

            
822
                        let elem = elems[i].clone();
823
                        if elem.name != field_names[i] {
824
                            return Err(DomainOpError::NotGround);
825
                        }
826

            
827
                        all_elems.push(elem.value);
828
                    }
829

            
830
                    elem_domains.push(Moo::new(GroundDomain::from_literal_vec(&all_elems)?));
831
                }
832

            
833
                Ok(GroundDomain::Record(
834
                    izip!(field_names, elem_domains)
835
                        .map(|(name, domain)| RecordEntryGround { name, domain })
836
                        .collect(),
837
                ))
838
            }
839
            Literal::AbstractLiteral(AbstractLiteral::Function(items)) => {
840
                if items.is_empty() {
841
                    return Err(DomainOpError::NotGround);
842
                }
843

            
844
                let (x1, y1) = &items[0];
845
                let d1 = x1.domain_of();
846
                let d1 = d1.as_ground().ok_or(DomainOpError::NotGround)?;
847
                let d2 = y1.domain_of();
848
                let d2 = d2.as_ground().ok_or(DomainOpError::NotGround)?;
849

            
850
                // Check that all items have the same domains
851
                for (x, y) in items {
852
                    let dx = x.domain_of();
853
                    let dx = dx.as_ground().ok_or(DomainOpError::NotGround)?;
854

            
855
                    let dy = y.domain_of();
856
                    let dy = dy.as_ground().ok_or(DomainOpError::NotGround)?;
857

            
858
                    if (dx != d1) || (dy != d2) {
859
                        return Err(DomainOpError::WrongType);
860
                    }
861
                }
862

            
863
                todo!();
864
            }
865
        }
866
    }
867
}
868

            
869
impl Typeable for GroundDomain {
870
    fn return_type(&self) -> ReturnType {
871
        match self {
872
            GroundDomain::Empty(ty) => ty.clone(),
873
            GroundDomain::Bool => ReturnType::Bool,
874
            GroundDomain::Int(_) => ReturnType::Int,
875
            GroundDomain::Set(_attr, inner) => ReturnType::Set(Box::new(inner.return_type())),
876
            GroundDomain::Matrix(inner, _idx) => ReturnType::Matrix(Box::new(inner.return_type())),
877
            GroundDomain::Tuple(inners) => {
878
                let mut inner_types = Vec::new();
879
                for inner in inners {
880
                    inner_types.push(inner.return_type());
881
                }
882
                ReturnType::Tuple(inner_types)
883
            }
884
            GroundDomain::Record(entries) => {
885
                let mut entry_types = Vec::new();
886
                for entry in entries {
887
                    entry_types.push(entry.domain.return_type());
888
                }
889
                ReturnType::Record(entry_types)
890
            }
891
            GroundDomain::Function(_, dom, cdom) => {
892
                ReturnType::Function(Box::new(dom.return_type()), Box::new(cdom.return_type()))
893
            }
894
        }
895
    }
896
}
897

            
898
impl Display for GroundDomain {
899
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
900
        match &self {
901
            GroundDomain::Empty(ty) => write!(f, "empty({ty:?})"),
902
            GroundDomain::Bool => write!(f, "bool"),
903
            GroundDomain::Int(ranges) => {
904
                if ranges.iter().all(Range::is_lower_or_upper_bounded) {
905
                    let rngs: String = ranges.iter().map(|r| format!("{r}")).join(", ");
906
                    write!(f, "int({})", rngs)
907
                } else {
908
                    write!(f, "int")
909
                }
910
            }
911
            GroundDomain::Set(attrs, inner_dom) => write!(f, "set {attrs} of {inner_dom}"),
912
            GroundDomain::Matrix(value_domain, index_domains) => {
913
                write!(
914
                    f,
915
                    "matrix indexed by [{}] of {value_domain}",
916
                    pretty_vec(&index_domains.iter().collect_vec())
917
                )
918
            }
919
            GroundDomain::Tuple(domains) => {
920
                write!(
921
                    f,
922
                    "tuple of ({})",
923
                    pretty_vec(&domains.iter().collect_vec())
924
                )
925
            }
926
            GroundDomain::Record(entries) => {
927
                write!(
928
                    f,
929
                    "record of ({})",
930
                    pretty_vec(
931
                        &entries
932
                            .iter()
933
                            .map(|entry| format!("{}: {}", entry.name, entry.domain))
934
                            .collect_vec()
935
                    )
936
                )
937
            }
938
            GroundDomain::Function(attribute, domain, codomain) => {
939
                write!(f, "function {} {} --> {} ", attribute, domain, codomain)
940
            }
941
        }
942
    }
943
}