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
1272
    fn names(&self) -> impl Iterator<Item = Name> + '_ {
22
1272
        self.indices
23
1272
            .iter()
24
2544
            .map(move |index| self.indices_to_name(std::slice::from_ref(index)))
25
1272
    }
26

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

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

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

            
45
21
        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
21
        let indices = (1..(elem_domain.len() + 1) as i32)
51
21
            .map(Literal::Int)
52
21
            .collect();
53

            
54
21
        Some(TupleToAtom {
55
21
            src_var: name.clone(),
56
21
            indices,
57
21
            elem_domain: elem_domain.clone(),
58
21
        })
59
21
    }
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
1209
    fn value_up(
88
1209
        &self,
89
1209
        values: &std::collections::BTreeMap<Name, Literal>,
90
1209
    ) -> Result<Literal, ApplicationError> {
91
1209
        let mut tuple = Vec::new();
92

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

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

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

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

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

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