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

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

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

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

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

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

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

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

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

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

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

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