1
// https://conjure-cp.github.io/conjure-oxide/docs/conjure_core/representation/trait.Representation.html
2
use conjure_cp::ast::GroundDomain;
3
use conjure_cp::{
4
    ast::{Atom, DeclarationPtr, Domain, Expression, Literal, Metadata, Name, Range, SymbolTable},
5
    register_representation,
6
    representation::Representation,
7
    rule_engine::ApplicationError,
8
};
9

            
10
register_representation!(SATLogInt, "sat_log_int");
11

            
12
// The number of bits used to represent the integer.
13
// This is a fixed value for the representation, but could be made dynamic if needed.
14
const BITS: i32 = 8;
15

            
16
#[derive(Clone, Debug)]
17
pub struct SATLogInt {
18
    src_var: Name,
19
}
20

            
21
impl SATLogInt {
22
    /// Returns the names of the representation variable
23
    fn names(&self) -> impl Iterator<Item = Name> + '_ {
24
        (0..BITS).map(move |index| self.index_to_name(index))
25
    }
26

            
27
    /// Gets the representation variable name for a specific index.
28
    fn index_to_name(&self, index: i32) -> Name {
29
        Name::Represented(Box::new((
30
            self.src_var.clone(),
31
            self.repr_name().into(),
32
            format!("{index:02}").into(), // stored as _00, _01, ...
33
        )))
34
    }
35
}
36

            
37
impl Representation for SATLogInt {
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::Int(ranges) = domain.as_ref() else {
46
            return None;
47
        };
48

            
49
        // Essence only supports decision variables with finite domains
50
        if !ranges
51
            .iter()
52
            .all(|x| matches!(x, Range::Bounded(_, _)) || matches!(x, Range::Single(_)))
53
        {
54
            return None;
55
        }
56

            
57
        Some(SATLogInt {
58
            src_var: name.clone(),
59
        })
60
    }
61

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

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

            
74
        let mut result = std::collections::BTreeMap::new();
75

            
76
        // name_0 is the least significant bit, name_<BITS-1> is the sign bit
77
        for name in self.names() {
78
            result.insert(name, Literal::Bool((value_i32 & 1) != 0));
79
            value_i32 >>= 1;
80
        }
81

            
82
        Ok(result)
83
    }
84

            
85
    fn value_up(
86
        &self,
87
        values: &std::collections::BTreeMap<Name, Literal>,
88
    ) -> Result<Literal, ApplicationError> {
89
        let mut out: i32 = 0;
90
        let mut power: i32 = 1;
91

            
92
        for name in self.names() {
93
            let value = values
94
                .get(&name)
95
                .ok_or(ApplicationError::RuleNotApplicable)?;
96

            
97
            if let Literal::Int(value) = value {
98
                out += *value * power;
99
                power <<= 1;
100
            } else {
101
                return Err(ApplicationError::RuleNotApplicable);
102
            }
103
        }
104

            
105
        let sign_bit = 1 << (BITS - 1);
106
        // Mask to `BITS` bits
107
        out &= (sign_bit << 1) - 1;
108

            
109
        // If the sign bit is set, convert to negative using two's complement
110
        if out & sign_bit != 0 {
111
            out -= sign_bit << 1;
112
        }
113

            
114
        Ok(Literal::Int(out))
115
    }
116

            
117
    fn expression_down(
118
        &self,
119
        st: &SymbolTable,
120
    ) -> Result<std::collections::BTreeMap<Name, Expression>, ApplicationError> {
121
        Ok(self
122
            .names()
123
            .map(|name| {
124
                let decl = st.lookup(&name).unwrap();
125
                (
126
                    name,
127
                    Expression::Atomic(
128
                        Metadata::new(),
129
                        Atom::Reference(conjure_cp::ast::Reference { ptr: decl }),
130
                    ),
131
                )
132
            })
133
            .collect())
134
    }
135

            
136
    fn declaration_down(&self) -> Result<Vec<DeclarationPtr>, ApplicationError> {
137
        Ok(self
138
            .names()
139
            .map(|name| DeclarationPtr::new_var(name, Domain::bool()))
140
            .collect())
141
    }
142

            
143
    fn repr_name(&self) -> &str {
144
        "sat_log_int"
145
    }
146

            
147
    fn box_clone(&self) -> Box<dyn Representation> {
148
        Box::new(self.clone()) as _
149
    }
150
}