1
use conjure_cp::ast::{DeclarationPtr, DomainPtr, GroundDomain, Moo};
2
use itertools::Itertools;
3

            
4
use super::prelude::*;
5

            
6
register_representation!(TupleToAtom, "tuple_to_atom");
7

            
8
#[derive(Clone, Debug)]
9
pub struct TupleToAtom {
10
    src_var: Name,
11

            
12
    // all the possible indices in this matrix, in order.
13
    indices: Vec<Literal>,
14

            
15
    // the element domains for each item in the tuple.
16
    elem_domain: Vec<Moo<GroundDomain>>,
17
}
18

            
19
impl TupleToAtom {
20
    /// Returns the names of the representation variable (there must be a much easier way to do this but oh well)
21
5088
    fn names(&self) -> impl Iterator<Item = Name> + '_ {
22
5088
        self.indices
23
5088
            .iter()
24
10176
            .map(move |index| self.indices_to_name(std::slice::from_ref(index)))
25
5088
    }
26

            
27
    /// Gets the representation variable name for a specific set of indices.
28
10176
    fn indices_to_name(&self, indices: &[Literal]) -> Name {
29
10176
        Name::Represented(Box::new((
30
10176
            self.src_var.clone(),
31
10176
            self.repr_name().into(),
32
10176
            indices.iter().join("_").into(),
33
10176
        )))
34
10176
    }
35
}
36

            
37
impl Representation for TupleToAtom {
38
84
    fn init(name: &Name, symtab: &SymbolTable) -> Option<Self> {
39
84
        let domain = symtab.resolve_domain(name)?;
40

            
41
84
        if !domain.is_finite() {
42
            return None;
43
84
        }
44

            
45
84
        let GroundDomain::Tuple(elem_domain) = domain.as_ref() else {
46
            return None;
47
        };
48

            
49
        //indices may not be needed as a field as we can always use the length of the tuple
50
84
        let indices = (1..(elem_domain.len() + 1) as i32)
51
84
            .map(Literal::Int)
52
84
            .collect();
53

            
54
84
        Some(TupleToAtom {
55
84
            src_var: name.clone(),
56
84
            indices,
57
84
            elem_domain: elem_domain.clone(),
58
84
        })
59
84
    }
60

            
61
    fn variable_name(&self) -> &Name {
62
        &self.src_var
63
    }
64

            
65
    fn value_down(
66
        &self,
67
        value: Literal,
68
    ) -> Result<std::collections::BTreeMap<Name, Literal>, ApplicationError> {
69
        let Literal::AbstractLiteral(tuple) = value else {
70
            return Err(ApplicationError::RuleNotApplicable);
71
        };
72

            
73
        let AbstractLiteral::Tuple(elems) = tuple else {
74
            return Err(ApplicationError::RuleNotApplicable);
75
        };
76

            
77
        let mut result = std::collections::BTreeMap::new();
78

            
79
        for (i, elem) in elems.into_iter().enumerate() {
80
            let name = format!("{}_{}", self.src_var, i + 1);
81
            result.insert(Name::user(&name), elem);
82
        }
83

            
84
        Ok(result)
85
    }
86

            
87
4836
    fn value_up(
88
4836
        &self,
89
4836
        values: &std::collections::BTreeMap<Name, Literal>,
90
4836
    ) -> Result<Literal, ApplicationError> {
91
4836
        let mut tuple = Vec::new();
92

            
93
9672
        for name in self.names() {
94
9672
            let value = values
95
9672
                .get(&name)
96
9672
                .ok_or(ApplicationError::RuleNotApplicable)?;
97
9672
            tuple.push(value.clone());
98
        }
99

            
100
4836
        Ok(Literal::AbstractLiteral(AbstractLiteral::Tuple(tuple)))
101
4836
    }
102

            
103
168
    fn expression_down(
104
168
        &self,
105
168
        st: &SymbolTable,
106
168
    ) -> Result<std::collections::BTreeMap<Name, Expression>, ApplicationError> {
107
168
        Ok(self
108
168
            .names()
109
336
            .map(|name| {
110
336
                let decl = st.lookup(&name).unwrap();
111
336
                (
112
336
                    name,
113
336
                    Expression::Atomic(
114
336
                        Metadata::new(),
115
336
                        Atom::Reference(conjure_cp::ast::Reference::new(decl)),
116
336
                    ),
117
336
                )
118
336
            })
119
168
            .collect())
120
168
    }
121

            
122
84
    fn declaration_down(&self) -> Result<Vec<DeclarationPtr>, ApplicationError> {
123
84
        Ok(self
124
84
            .names()
125
84
            .zip(self.elem_domain.iter().cloned())
126
168
            .map(|(name, domain)| DeclarationPtr::new_find(name, DomainPtr::from(domain)))
127
84
            .collect())
128
84
    }
129

            
130
10452
    fn repr_name(&self) -> &str {
131
10452
        "tuple_to_atom"
132
10452
    }
133

            
134
9378
    fn box_clone(&self) -> Box<dyn Representation> {
135
9378
        Box::new(self.clone()) as _
136
9378
    }
137
}