1
use crate::{
2
    ast::Metadata,
3
    ast::{Domain, Moo, Range, ReturnType},
4
    bug, bug_assert, matrix_expr,
5
};
6

            
7
use super::{Expression, Literal, Typeable};
8
use serde::{Deserialize, Serialize};
9

            
10
/// The possible kinds of associative-commutative (AC) operator.
11
///
12
/// AC operators take a single vector as input and are commonly used alongside comprehensions.
13
///
14
/// `Min`/`Max` are included here for the sole purpose of tagging a comprehension's
15
/// [`skip_operator`](super::comprehension::Comprehension::skip_operator) so that a symbolic guard
16
/// inside `min([... | ...])`/`max([... | ...])` can be lowered correctly by the native
17
/// comprehension expander -- unlike `And`/`Or`/`Sum`/`Product`, they have no universal identity
18
/// element (the "safe value to substitute for a guarded-out element" depends on the element's own
19
/// domain), so [`identity`](Self::identity) is intentionally unreachable for them; callers that
20
/// might see a `Min`/`Max` operator must check for that first (see
21
/// `expand_native.rs`'s guard on the identity-dropping optimisation for the only current example).
22
/// They are deliberately not wired into `TryFrom<&Expression>` or the AC-comprehension merge/
23
/// via-solver machinery, which only ever handles the four true AC operators.
24
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25
pub enum ACOperatorKind {
26
    And,
27
    Or,
28
    Product,
29
    Sum,
30
    Min,
31
    Max,
32
}
33

            
34
impl ACOperatorKind {
35
    /// Creates a new [Expression] of this AC operator kind with the given child expression.
36
    ///
37
    /// The child expression given should be of type matrix.
38
523433
    pub fn as_expression(&self, child_expr: Expression) -> Expression {
39
        bug_assert!(
40
523433
            matches!(child_expr.return_type(), ReturnType::Matrix(_)),
41
            "The child expression given to ACOperatorKind::to_expression should be of type matrix."
42
        );
43
523433
        let box_expr = Moo::new(child_expr);
44
523433
        match self {
45
353760
            ACOperatorKind::And => Expression::And(Metadata::new(), box_expr),
46
76618
            ACOperatorKind::Or => Expression::Or(Metadata::new(), box_expr),
47
            ACOperatorKind::Product => Expression::Product(Metadata::new(), box_expr),
48
93026
            ACOperatorKind::Sum => Expression::Sum(Metadata::new(), box_expr),
49
29
            ACOperatorKind::Min => Expression::Min(Metadata::new(), box_expr),
50
            ACOperatorKind::Max => Expression::Max(Metadata::new(), box_expr),
51
        }
52
523433
    }
53

            
54
    /// Returns the identity element of this operation.
55
    ///
56
    /// # Example
57
    ///
58
    /// ```
59
    /// use conjure_cp_core::ast::{ac_operators::ACOperatorKind,Literal};
60
    ///
61
    /// let identity = ACOperatorKind::And.identity();
62
    /// assert_eq!(identity,Literal::Bool(true));
63
    /// ```
64
    ///
65
    /// # Panics
66
    ///
67
    /// `Min`/`Max` have no universal identity element -- see the type-level doc comment. Callers
68
    /// must not call this for those two variants.
69
4083750
    pub fn identity(&self) -> Literal {
70
4083750
        match self {
71
3268832
            ACOperatorKind::And => Literal::Bool(true),
72
701358
            ACOperatorKind::Or => Literal::Bool(false),
73
            ACOperatorKind::Product => Literal::Int(1),
74
113560
            ACOperatorKind::Sum => Literal::Int(0),
75
            ACOperatorKind::Min | ACOperatorKind::Max => {
76
                bug!("ACOperatorKind::{self:?} has no universal identity element")
77
            }
78
        }
79
4083750
    }
80

            
81
    /// Given some guard and tail expressions, constructs the skipping operator for this operation.
82
    ///
83
    /// The skipping operator is operator that takes some boolean guard expression b and some tail
84
    /// expression x. If b is true, then it evaluates to x, otherwise it evaluates to the identity
85
    /// element.
86
    ///
87
    /// # Usage
88
    ///
89
    /// This can be used to add guards to elements of AC operations. In the example model below, we
90
    /// only want to multiply y*z by 2 if multiplyByTwo is true:
91
    ///
92
    /// ```plain
93
    /// find multiplyByTwo: bool
94
    /// find x: int(1..5)
95
    /// find y: int(1..5)
96
    /// find z: int(1..5)
97
    ///
98
    /// such that
99
    ///  
100
    /// x = product([y,z,[1,x;int(0..1)][toInt(b)]])
101
    /// ```
102
    ///
103
    /// `[1,x;int(0..1)][toInt(b)]` is the skipping operator for product.
104
    ///
105
    /// This method constructs the skipping operator, substituting in the given expressions for b
106
    /// and x.
107
291188
    pub fn make_skip_operation(&self, guard_expr: Expression, tail_expr: Expression) -> Expression {
108
        bug_assert!(
109
291188
            matches!(guard_expr.return_type(), ReturnType::Bool),
110
            "The guard expression in a skipping operation should be type boolean."
111
        );
112

            
113
291188
        match self {
114
            ACOperatorKind::And => {
115
                bug_assert!(
116
194488
                    matches!(tail_expr.return_type(), ReturnType::Bool),
117
                    "The tail expression in an and skipping operation should be type boolean."
118
                );
119
194488
                let tail_expr_boxed = Moo::new(tail_expr);
120
194488
                let guard_expr_boxed = Moo::new(guard_expr);
121
194488
                Expression::Imply(Metadata::new(), guard_expr_boxed, tail_expr_boxed)
122
            }
123
            ACOperatorKind::Or => {
124
                bug_assert!(
125
52490
                    matches!(tail_expr.return_type(), ReturnType::Bool),
126
                    "The tail expression in an or skipping operation should be type boolean."
127
                );
128
52490
                Expression::And(
129
52490
                    Metadata::new(),
130
52490
                    Moo::new(matrix_expr![guard_expr, tail_expr]),
131
52490
                )
132
            }
133
            ACOperatorKind::Product => {
134
                bug_assert!(
135
                    matches!(tail_expr.return_type(), ReturnType::Int),
136
                    "The tail expression in a product skipping operation should be type int."
137
                );
138
                let guard_expr_boxed = Moo::new(guard_expr);
139
                Expression::UnsafeIndex(
140
                    Metadata::new(),
141
                    Moo::new(
142
                        matrix_expr![Expression::Atomic(Metadata::new(),1.into()),tail_expr;Domain::int(vec![Range::Bounded(0,1)])],
143
                    ),
144
                    vec![Expression::ToInt(Metadata::new(), guard_expr_boxed)],
145
                )
146
            }
147
            ACOperatorKind::Sum => {
148
44210
                let guard_expr_boxed = Moo::new(guard_expr);
149
                bug_assert!(
150
44210
                    matches!(tail_expr.return_type(), ReturnType::Int),
151
                    "The tail expression in a sum skipping operation should be type int."
152
                );
153
44210
                Expression::Product(
154
44210
                    Metadata::new(),
155
44210
                    Moo::new(matrix_expr![
156
44210
                        Expression::ToInt(Metadata::new(), guard_expr_boxed),
157
44210
                        tail_expr
158
44210
                    ]),
159
44210
                )
160
            }
161
            ACOperatorKind::Min | ACOperatorKind::Max => {
162
                bug!(
163
                    "ACOperatorKind::{self:?} has no universal identity element, so make_skip_operation \
164
                     cannot build a skip operation for it -- use make_min_max_skip_operation instead, \
165
                     which takes an explicit skip value"
166
                )
167
            }
168
        }
169
291188
    }
170

            
171
    /// The min/max equivalent of [`make_skip_operation`](Self::make_skip_operation): `self` must
172
    /// be `Min` or `Max`. Unlike the four true AC operators, min/max have no value that's always
173
    /// safe to substitute for a guarded-out element -- callers must supply one themselves (`skip_value`),
174
    /// since this method has no way to know it: by the time a per-branch skip operation is being
175
    /// built, `tail_expr` is already the branch-specific (and so potentially far too narrow)
176
    /// result, not the comprehension's general return-expression domain the skip value should
177
    /// come from. Any domain bound safe for every element being aggregated works (e.g. the
178
    /// element's declared domain's max, for `min`, or min, for `max`) -- including it can never
179
    /// change a min/max computed over at least one *included* real element, since it is never more
180
    /// extreme than any value the element could actually take.
181
29
    pub fn make_min_max_skip_operation(
182
29
        &self,
183
29
        guard_expr: Expression,
184
29
        tail_expr: Expression,
185
29
        skip_value: Literal,
186
29
    ) -> Expression {
187
        bug_assert!(
188
29
            matches!(self, ACOperatorKind::Min | ACOperatorKind::Max),
189
            "make_min_max_skip_operation is only valid for ACOperatorKind::Min/Max."
190
        );
191
        bug_assert!(
192
29
            matches!(guard_expr.return_type(), ReturnType::Bool),
193
            "The guard expression in a skipping operation should be type boolean."
194
        );
195
        bug_assert!(
196
29
            matches!(tail_expr.return_type(), ReturnType::Int),
197
            "The tail expression in a min/max skipping operation should be type int."
198
        );
199
29
        let guard_expr_boxed = Moo::new(guard_expr);
200
29
        Expression::UnsafeIndex(
201
29
            Metadata::new(),
202
29
            Moo::new(
203
29
                matrix_expr![Expression::Atomic(Metadata::new(), skip_value.into()), tail_expr; Domain::int(vec![Range::Bounded(0, 1)])],
204
29
            ),
205
29
            vec![Expression::ToInt(Metadata::new(), guard_expr_boxed)],
206
29
        )
207
29
    }
208

            
209
    /// Gives the return type of the operator, and the return types its elements should be.
210
    pub fn return_type(&self) -> ReturnType {
211
        match self {
212
            ACOperatorKind::And | ACOperatorKind::Or => ReturnType::Bool,
213
            ACOperatorKind::Product
214
            | ACOperatorKind::Sum
215
            | ACOperatorKind::Min
216
            | ACOperatorKind::Max => ReturnType::Int,
217
        }
218
    }
219
}
220

            
221
impl TryFrom<&Expression> for ACOperatorKind {
222
    type Error = ();
223
33259412
    fn try_from(expr: &Expression) -> Result<Self, Self::Error> {
224
33259412
        match expr {
225
16022372
            Expression::And(_, _) => Ok(ACOperatorKind::And),
226
10838742
            Expression::Or(_, _) => Ok(ACOperatorKind::Or),
227
745275
            Expression::Product(_, _) => Ok(ACOperatorKind::Product),
228
5628611
            Expression::Sum(_, _) => Ok(ACOperatorKind::Sum),
229
24412
            _ => Err(()),
230
        }
231
33259412
    }
232
}
233

            
234
impl TryFrom<Expression> for ACOperatorKind {
235
    type Error = ();
236

            
237
    fn try_from(value: Expression) -> Result<Self, Self::Error> {
238
        TryFrom::try_from(&value)
239
    }
240
}
241

            
242
impl TryFrom<Box<Expression>> for ACOperatorKind {
243
    type Error = ();
244

            
245
    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
246
        TryFrom::try_from(value.as_ref())
247
    }
248
}