Skip to main content

conjure_cp_core/ast/
literals.rs

1use itertools::Itertools;
2use serde::{Deserialize, Serialize};
3use std::fmt::{Display, Formatter};
4use std::hash::Hash;
5use ustr::Ustr;
6
7use super::{
8    Atom, Domain, DomainPtr, Expression, GroundDomain, Metadata, Moo, Range, ReturnType, SetAttr,
9    Typeable, domains::HasDomain, domains::Int, records::RecordValue,
10};
11use crate::ast::domains::MSetAttr;
12use crate::ast::pretty::pretty_vec;
13use crate::bug;
14use polyquine::Quine;
15use uniplate::{Biplate, Tree, Uniplate};
16
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate, Hash, Quine)]
18#[uniplate(walk_into=[AbstractLiteral<Literal>])]
19#[biplate(to=Atom)]
20#[biplate(to=AbstractLiteral<Literal>)]
21#[biplate(to=AbstractLiteral<Expression>)]
22#[biplate(to=RecordValue<Literal>)]
23#[biplate(to=RecordValue<Expression>)]
24#[biplate(to=Expression)]
25#[path_prefix(conjure_cp::ast)]
26/// A literal value, equivalent to constants in Conjure.
27pub enum Literal {
28    Int(i32),
29    Bool(bool),
30    //abstract literal variant ends in Literal, but that's ok
31    #[allow(clippy::enum_variant_names)]
32    AbstractLiteral(AbstractLiteral<Literal>),
33}
34
35impl HasDomain for Literal {
36    fn domain_of(&self) -> DomainPtr {
37        match self {
38            Literal::Int(i) => Domain::int(vec![Range::Single(*i)]),
39            Literal::Bool(_) => Domain::bool(),
40            Literal::AbstractLiteral(abstract_literal) => abstract_literal.domain_of(),
41        }
42    }
43}
44
45// make possible values of an AbstractLiteral a closed world to make the trait bounds more sane (particularly in Uniplate instances!!)
46pub trait AbstractLiteralValue:
47    Clone + Eq + PartialEq + Display + Uniplate + Biplate<RecordValue<Self>> + 'static
48{
49    type Dom: Clone + Eq + PartialEq + Display + Quine + From<GroundDomain> + Into<DomainPtr>;
50}
51impl AbstractLiteralValue for Expression {
52    type Dom = DomainPtr;
53}
54impl AbstractLiteralValue for Literal {
55    type Dom = Moo<GroundDomain>;
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Quine)]
59#[path_prefix(conjure_cp::ast)]
60pub enum AbstractLiteral<T: AbstractLiteralValue> {
61    Set(Vec<T>),
62
63    MSet(Vec<T>),
64
65    /// A 1 dimensional matrix slice with an index domain.
66    Matrix(Vec<T>, T::Dom),
67
68    // a tuple of literals
69    Tuple(Vec<T>),
70
71    Record(Vec<RecordValue<T>>),
72
73    Function(Vec<(T, T)>),
74}
75
76// TODO: use HasDomain instead once Expression::domain_of returns Domain not Option<Domain>
77impl AbstractLiteral<Expression> {
78    pub fn domain_of(&self) -> Option<DomainPtr> {
79        match self {
80            AbstractLiteral::Set(items) => {
81                // ensure that all items have a domain, or return None
82                let item_domains: Vec<DomainPtr> = items
83                    .iter()
84                    .map(|x| x.domain_of())
85                    .collect::<Option<Vec<DomainPtr>>>()?;
86
87                // union all item domains together
88                let mut item_domain_iter = item_domains.iter().cloned();
89                let first_item = item_domain_iter.next()?;
90                let item_domain = item_domains
91                    .iter()
92                    .try_fold(first_item, |x, y| x.union(y))
93                    .expect("taking the union of all item domains of a set literal should succeed");
94
95                Some(Domain::set(SetAttr::<Int>::default(), item_domain))
96            }
97
98            AbstractLiteral::MSet(items) => {
99                // ensure that all items have a domain, or return None
100                let item_domains: Vec<DomainPtr> = items
101                    .iter()
102                    .map(|x| x.domain_of())
103                    .collect::<Option<Vec<DomainPtr>>>()?;
104
105                // union all item domains together
106                let mut item_domain_iter = item_domains.iter().cloned();
107                let first_item = item_domain_iter.next()?;
108                let item_domain = item_domains
109                    .iter()
110                    .try_fold(first_item, |x, y| x.union(y))
111                    .expect("taking the union of all item domains of a set literal should succeed");
112
113                Some(Domain::mset(MSetAttr::<Int>::default(), item_domain))
114            }
115
116            AbstractLiteral::Matrix(items, _) => {
117                // ensure that all items have a domain, or return None
118                let item_domains = items
119                    .iter()
120                    .map(|x| x.domain_of())
121                    .collect::<Option<Vec<DomainPtr>>>()?;
122
123                // union all item domains together
124                let mut item_domain_iter = item_domains.iter().cloned();
125
126                let first_item = item_domain_iter.next()?;
127
128                let item_domain = item_domains
129                    .iter()
130                    .try_fold(first_item, |x, y| x.union(y))
131                    .expect(
132                        "taking the union of all item domains of a matrix literal should succeed",
133                    );
134
135                let mut new_index_domain = vec![];
136
137                // flatten index domains of n-d matrix into list
138                let mut e = Expression::AbstractLiteral(Metadata::new(), self.clone());
139                while let Expression::AbstractLiteral(_, AbstractLiteral::Matrix(elems, idx)) = e {
140                    assert!(
141                        idx.as_matrix().is_none(),
142                        "n-dimensional matrix literals should be represented as a matrix inside a matrix, got {idx}"
143                    );
144                    new_index_domain.push(idx);
145                    e = elems[0].clone();
146                }
147                Some(Domain::matrix(item_domain, new_index_domain))
148            }
149            AbstractLiteral::Tuple(_) => None,
150            AbstractLiteral::Record(_) => None,
151            AbstractLiteral::Function(_) => None,
152        }
153    }
154}
155
156impl HasDomain for AbstractLiteral<Literal> {
157    fn domain_of(&self) -> DomainPtr {
158        Domain::from_literal_vec(&[Literal::AbstractLiteral(self.clone())])
159            .expect("abstract literals should be correctly typed")
160    }
161}
162
163impl Typeable for AbstractLiteral<Expression> {
164    fn return_type(&self) -> ReturnType {
165        match self {
166            AbstractLiteral::Set(items) if items.is_empty() => {
167                ReturnType::Set(Box::new(ReturnType::Unknown))
168            }
169            AbstractLiteral::Set(items) => {
170                let item_type = items[0].return_type();
171
172                // if any items do not have a type, return none.
173                let item_types: Vec<ReturnType> = items.iter().map(|x| x.return_type()).collect();
174
175                assert!(
176                    item_types.iter().all(|x| x == &item_type),
177                    "all items in a set should have the same type"
178                );
179
180                ReturnType::Set(Box::new(item_type))
181            }
182            AbstractLiteral::MSet(items) if items.is_empty() => {
183                ReturnType::MSet(Box::new(ReturnType::Unknown))
184            }
185            AbstractLiteral::MSet(items) => {
186                let item_type = items[0].return_type();
187
188                // if any items do not have a type, return none.
189                let item_types: Vec<ReturnType> = items.iter().map(|x| x.return_type()).collect();
190
191                assert!(
192                    item_types.iter().all(|x| x == &item_type),
193                    "all items in a set should have the same type"
194                );
195
196                ReturnType::MSet(Box::new(item_type))
197            }
198            AbstractLiteral::Matrix(items, _) if items.is_empty() => {
199                ReturnType::Matrix(Box::new(ReturnType::Unknown))
200            }
201            AbstractLiteral::Matrix(items, _) => {
202                let item_type = items[0].return_type();
203
204                // if any items do not have a type, return none.
205                let item_types: Vec<ReturnType> = items.iter().map(|x| x.return_type()).collect();
206
207                assert!(
208                    item_types.iter().all(|x| x == &item_type),
209                    "all items in a matrix should have the same type. items: {items} types: {types:#?}",
210                    items = pretty_vec(items),
211                    types = items
212                        .iter()
213                        .map(|x| x.return_type())
214                        .collect::<Vec<ReturnType>>()
215                );
216
217                ReturnType::Matrix(Box::new(item_type))
218            }
219            AbstractLiteral::Tuple(items) => {
220                let mut item_types = vec![];
221                for item in items {
222                    item_types.push(item.return_type());
223                }
224                ReturnType::Tuple(item_types)
225            }
226            AbstractLiteral::Record(items) => {
227                let mut item_types = vec![];
228                for item in items {
229                    item_types.push(item.value.return_type());
230                }
231                ReturnType::Record(item_types)
232            }
233            AbstractLiteral::Function(items) => {
234                if items.is_empty() {
235                    return ReturnType::Function(
236                        Box::new(ReturnType::Unknown),
237                        Box::new(ReturnType::Unknown),
238                    );
239                }
240
241                // Check that all items have the same return type
242                let (x1, y1) = &items[0];
243                let (t1, t2) = (x1.return_type(), y1.return_type());
244                for (x, y) in items {
245                    let (tx, ty) = (x.return_type(), y.return_type());
246                    if tx != t1 {
247                        bug!("Expected {t1}, got {x}: {tx}");
248                    }
249                    if ty != t2 {
250                        bug!("Expected {t2}, got {y}: {ty}");
251                    }
252                }
253
254                ReturnType::Function(Box::new(t1), Box::new(t2))
255            }
256        }
257    }
258}
259
260impl<T> AbstractLiteral<T>
261where
262    T: AbstractLiteralValue,
263{
264    /// Creates a matrix with elements `elems`, with domain `int(1..)`.
265    ///
266    /// This acts as a variable sized list.
267    pub fn matrix_implied_indices(elems: Vec<T>) -> Self {
268        AbstractLiteral::Matrix(elems, GroundDomain::Int(vec![Range::UnboundedR(1)]).into())
269    }
270
271    /// If the AbstractLiteral is a list, returns its elements.
272    ///
273    /// A list is any a matrix with the domain `int(1..)`. This includes matrix literals without
274    /// any explicitly specified domain.
275    pub fn unwrap_list(&self) -> Option<&Vec<T>> {
276        let AbstractLiteral::Matrix(elems, domain) = self else {
277            return None;
278        };
279
280        let domain: DomainPtr = domain.clone().into();
281        let Some(GroundDomain::Int(ranges)) = domain.as_ground() else {
282            return None;
283        };
284
285        let [Range::UnboundedR(1)] = ranges[..] else {
286            return None;
287        };
288
289        Some(elems)
290    }
291}
292
293impl<T> Display for AbstractLiteral<T>
294where
295    T: AbstractLiteralValue,
296{
297    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
298        match self {
299            AbstractLiteral::Set(elems) => {
300                let elems_str: String = elems.iter().map(|x| format!("{x}")).join(",");
301                write!(f, "{{{elems_str}}}")
302            }
303            AbstractLiteral::MSet(elems) => {
304                let elems_str: String = elems.iter().map(|x| format!("{x}")).join(",");
305                write!(f, "mset({elems_str})")
306            }
307            AbstractLiteral::Matrix(elems, index_domain) => {
308                let elems_str: String = elems.iter().map(|x| format!("{x}")).join(",");
309                write!(f, "[{elems_str};{index_domain}]")
310            }
311            AbstractLiteral::Tuple(elems) => {
312                let elems_str: String = elems.iter().map(|x| format!("{x}")).join(",");
313                write!(f, "({elems_str})")
314            }
315            AbstractLiteral::Record(entries) => {
316                let entries_str: String = entries
317                    .iter()
318                    .map(|entry| format!("{}: {}", entry.name, entry.value))
319                    .join(",");
320                write!(f, "{{{entries_str}}}")
321            }
322            AbstractLiteral::Function(entries) => {
323                let entries_str: String = entries
324                    .iter()
325                    .map(|entry| format!("{} --> {}", entry.0, entry.1))
326                    .join(",");
327                write!(f, "function({entries_str})")
328            }
329        }
330    }
331}
332
333impl<T> Uniplate for AbstractLiteral<T>
334where
335    T: AbstractLiteralValue + Biplate<AbstractLiteral<T>>,
336{
337    fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
338        // walking into T
339        match self {
340            AbstractLiteral::Set(vec) => {
341                let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(vec);
342                (f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
343            }
344            AbstractLiteral::MSet(vec) => {
345                let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(vec);
346                (f1_tree, Box::new(move |x| AbstractLiteral::MSet(f1_ctx(x))))
347            }
348            AbstractLiteral::Matrix(elems, index_domain) => {
349                let index_domain = index_domain.clone();
350                let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(elems);
351                (
352                    f1_tree,
353                    Box::new(move |x| AbstractLiteral::Matrix(f1_ctx(x), index_domain.clone())),
354                )
355            }
356            AbstractLiteral::Tuple(elems) => {
357                let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(elems);
358                (
359                    f1_tree,
360                    Box::new(move |x| AbstractLiteral::Tuple(f1_ctx(x))),
361                )
362            }
363            AbstractLiteral::Record(entries) => {
364                let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(entries);
365                (
366                    f1_tree,
367                    Box::new(move |x| AbstractLiteral::Record(f1_ctx(x))),
368                )
369            }
370            AbstractLiteral::Function(entries) => {
371                let entry_count = entries.len();
372                let flattened: Vec<T> = entries
373                    .iter()
374                    .flat_map(|(lhs, rhs)| [lhs.clone(), rhs.clone()])
375                    .collect();
376
377                let (f1_tree, f1_ctx) =
378                    <Vec<T> as Biplate<AbstractLiteral<T>>>::biplate(&flattened);
379                (
380                    f1_tree,
381                    Box::new(move |x| {
382                        let rebuilt = f1_ctx(x);
383                        assert_eq!(
384                            rebuilt.len(),
385                            entry_count * 2,
386                            "number of function literal children should remain unchanged"
387                        );
388
389                        let mut iter = rebuilt.into_iter();
390                        let mut pairs = Vec::with_capacity(entry_count);
391                        while let (Some(lhs), Some(rhs)) = (iter.next(), iter.next()) {
392                            pairs.push((lhs, rhs));
393                        }
394
395                        AbstractLiteral::Function(pairs)
396                    }),
397                )
398            }
399        }
400    }
401}
402
403impl<U, To> Biplate<To> for AbstractLiteral<U>
404where
405    To: Uniplate,
406    U: AbstractLiteralValue + Biplate<AbstractLiteral<U>> + Biplate<To>,
407    RecordValue<U>: Biplate<AbstractLiteral<U>> + Biplate<To>,
408{
409    fn biplate(&self) -> (Tree<To>, Box<dyn Fn(Tree<To>) -> Self>) {
410        if std::any::TypeId::of::<To>() == std::any::TypeId::of::<AbstractLiteral<U>>() {
411            // To ==From => return One(self)
412
413            unsafe {
414                // SAFETY: asserted the type equality above
415                let self_to = std::mem::transmute::<&AbstractLiteral<U>, &To>(self).clone();
416                let tree = Tree::One(self_to);
417                let ctx = Box::new(move |x| {
418                    let Tree::One(x) = x else {
419                        panic!();
420                    };
421
422                    std::mem::transmute::<&To, &AbstractLiteral<U>>(&x).clone()
423                });
424
425                (tree, ctx)
426            }
427        } else {
428            // walking into T
429            match self {
430                AbstractLiteral::Set(vec) => {
431                    let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(vec);
432                    (f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
433                }
434                AbstractLiteral::MSet(vec) => {
435                    let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(vec);
436                    (f1_tree, Box::new(move |x| AbstractLiteral::MSet(f1_ctx(x))))
437                }
438                AbstractLiteral::Matrix(elems, index_domain) => {
439                    let index_domain = index_domain.clone();
440                    let (f1_tree, f1_ctx) = <Vec<U> as Biplate<To>>::biplate(elems);
441                    (
442                        f1_tree,
443                        Box::new(move |x| AbstractLiteral::Matrix(f1_ctx(x), index_domain.clone())),
444                    )
445                }
446                AbstractLiteral::Tuple(elems) => {
447                    let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(elems);
448                    (
449                        f1_tree,
450                        Box::new(move |x| AbstractLiteral::Tuple(f1_ctx(x))),
451                    )
452                }
453                AbstractLiteral::Record(entries) => {
454                    let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(entries);
455                    (
456                        f1_tree,
457                        Box::new(move |x| AbstractLiteral::Record(f1_ctx(x))),
458                    )
459                }
460                AbstractLiteral::Function(entries) => {
461                    let entry_count = entries.len();
462                    let flattened: Vec<U> = entries
463                        .iter()
464                        .flat_map(|(lhs, rhs)| [lhs.clone(), rhs.clone()])
465                        .collect();
466
467                    let (f1_tree, f1_ctx) = <Vec<U> as Biplate<To>>::biplate(&flattened);
468                    (
469                        f1_tree,
470                        Box::new(move |x| {
471                            let rebuilt = f1_ctx(x);
472                            assert_eq!(
473                                rebuilt.len(),
474                                entry_count * 2,
475                                "number of function literal children should remain unchanged"
476                            );
477
478                            let mut iter = rebuilt.into_iter();
479                            let mut pairs = Vec::with_capacity(entry_count);
480                            while let (Some(lhs), Some(rhs)) = (iter.next(), iter.next()) {
481                                pairs.push((lhs, rhs));
482                            }
483
484                            AbstractLiteral::Function(pairs)
485                        }),
486                    )
487                }
488            }
489        }
490    }
491}
492
493impl TryFrom<Literal> for i32 {
494    type Error = &'static str;
495
496    fn try_from(value: Literal) -> Result<Self, Self::Error> {
497        match value {
498            Literal::Int(i) => Ok(i),
499            _ => Err("Cannot convert non-i32 literal to i32"),
500        }
501    }
502}
503
504impl TryFrom<Box<Literal>> for i32 {
505    type Error = &'static str;
506
507    fn try_from(value: Box<Literal>) -> Result<Self, Self::Error> {
508        (*value).try_into()
509    }
510}
511
512impl TryFrom<&Box<Literal>> for i32 {
513    type Error = &'static str;
514
515    fn try_from(value: &Box<Literal>) -> Result<Self, Self::Error> {
516        TryFrom::<&Literal>::try_from(value.as_ref())
517    }
518}
519
520impl TryFrom<&Moo<Literal>> for i32 {
521    type Error = &'static str;
522
523    fn try_from(value: &Moo<Literal>) -> Result<Self, Self::Error> {
524        TryFrom::<&Literal>::try_from(value.as_ref())
525    }
526}
527
528impl TryFrom<&Literal> for i32 {
529    type Error = &'static str;
530
531    fn try_from(value: &Literal) -> Result<Self, Self::Error> {
532        match value {
533            Literal::Int(i) => Ok(*i),
534            _ => Err("Cannot convert non-i32 literal to i32"),
535        }
536    }
537}
538
539impl TryFrom<Literal> for bool {
540    type Error = &'static str;
541
542    fn try_from(value: Literal) -> Result<Self, Self::Error> {
543        match value {
544            Literal::Bool(b) => Ok(b),
545            _ => Err("Cannot convert non-bool literal to bool"),
546        }
547    }
548}
549
550impl TryFrom<&Literal> for bool {
551    type Error = &'static str;
552
553    fn try_from(value: &Literal) -> Result<Self, Self::Error> {
554        match value {
555            Literal::Bool(b) => Ok(*b),
556            _ => Err("Cannot convert non-bool literal to bool"),
557        }
558    }
559}
560
561impl From<i32> for Literal {
562    fn from(i: i32) -> Self {
563        Literal::Int(i)
564    }
565}
566
567impl From<bool> for Literal {
568    fn from(b: bool) -> Self {
569        Literal::Bool(b)
570    }
571}
572
573impl From<Literal> for Ustr {
574    fn from(value: Literal) -> Self {
575        // TODO: avoid the temporary-allocation of a string by format! here?
576        Ustr::from(&format!("{value}"))
577    }
578}
579
580impl AbstractLiteral<Expression> {
581    /// If all the elements are literals, returns this as an AbstractLiteral<Literal>.
582    /// Otherwise, returns `None`.
583    pub fn into_literals(self) -> Option<AbstractLiteral<Literal>> {
584        match self {
585            AbstractLiteral::Set(elements) => {
586                let literals = elements
587                    .into_iter()
588                    .map(|expr| match expr {
589                        Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
590                        Expression::AbstractLiteral(_, abslit) => {
591                            Some(Literal::AbstractLiteral(abslit.into_literals()?))
592                        }
593                        _ => None,
594                    })
595                    .collect::<Option<Vec<_>>>()?;
596                Some(AbstractLiteral::Set(literals))
597            }
598            AbstractLiteral::MSet(elements) => {
599                let literals = elements
600                    .into_iter()
601                    .map(|expr| match expr {
602                        Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
603                        Expression::AbstractLiteral(_, abslit) => {
604                            Some(Literal::AbstractLiteral(abslit.into_literals()?))
605                        }
606                        _ => None,
607                    })
608                    .collect::<Option<Vec<_>>>()?;
609                Some(AbstractLiteral::MSet(literals))
610            }
611            AbstractLiteral::Matrix(items, domain) => {
612                let mut literals = vec![];
613                for item in items {
614                    let literal = match item {
615                        Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
616                        Expression::AbstractLiteral(_, abslit) => {
617                            Some(Literal::AbstractLiteral(abslit.into_literals()?))
618                        }
619                        _ => None,
620                    }?;
621                    literals.push(literal);
622                }
623
624                Some(AbstractLiteral::Matrix(literals, domain.resolve()?))
625            }
626            AbstractLiteral::Tuple(items) => {
627                let mut literals = vec![];
628                for item in items {
629                    let literal = match item {
630                        Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
631                        Expression::AbstractLiteral(_, abslit) => {
632                            Some(Literal::AbstractLiteral(abslit.into_literals()?))
633                        }
634                        _ => None,
635                    }?;
636                    literals.push(literal);
637                }
638
639                Some(AbstractLiteral::Tuple(literals))
640            }
641            AbstractLiteral::Record(entries) => {
642                let mut literals = vec![];
643                for entry in entries {
644                    let literal = match entry.value {
645                        Expression::Atomic(_, Atom::Literal(lit)) => Some(lit),
646                        Expression::AbstractLiteral(_, abslit) => {
647                            Some(Literal::AbstractLiteral(abslit.into_literals()?))
648                        }
649                        _ => None,
650                    }?;
651
652                    literals.push((entry.name, literal));
653                }
654                Some(AbstractLiteral::Record(
655                    literals
656                        .into_iter()
657                        .map(|(name, literal)| RecordValue {
658                            name,
659                            value: literal,
660                        })
661                        .collect(),
662                ))
663            }
664            AbstractLiteral::Function(_) => todo!("Implement into_literals for functions"),
665        }
666    }
667}
668
669// need display implementations for other types as well
670impl Display for Literal {
671    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
672        match &self {
673            Literal::Int(i) => write!(f, "{i}"),
674            Literal::Bool(b) => write!(f, "{b}"),
675            Literal::AbstractLiteral(l) => write!(f, "{l:?}"),
676        }
677    }
678}
679
680#[cfg(test)]
681mod tests {
682
683    use super::*;
684    use crate::{into_matrix, matrix};
685    use uniplate::Uniplate;
686
687    #[test]
688    fn matrix_uniplate_universe() {
689        // Can we traverse through matrices with uniplate?
690        let my_matrix: AbstractLiteral<Literal> = into_matrix![
691            vec![Literal::AbstractLiteral(matrix![Literal::Bool(true);Moo::new(GroundDomain::Bool)]); 5];
692            Moo::new(GroundDomain::Bool)
693        ];
694
695        let expected_index_domains = vec![Moo::new(GroundDomain::Bool); 6];
696        let actual_index_domains: Vec<Moo<GroundDomain>> =
697            my_matrix.cata(&move |elem, children| {
698                let mut res = vec![];
699                res.extend(children.into_iter().flatten());
700                if let AbstractLiteral::Matrix(_, index_domain) = elem {
701                    res.push(index_domain);
702                }
703
704                res
705            });
706
707        assert_eq!(actual_index_domains, expected_index_domains);
708    }
709}