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

            
7
use super::{Expression, Literal, Typeable};
8

            
9
/// The possible kinds of associative-commutative (AC) operator.
10
///
11
/// AC operators take a single vector as input and are commonly used alongside comprehensions.
12
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13
pub enum ACOperatorKind {
14
    And,
15
    Or,
16
    Product,
17
    Sum,
18
}
19

            
20
impl ACOperatorKind {
21
    /// Creates a new [Expression] of this AC operator kind with the given child expression.
22
    ///
23
    /// The child expression given should be of type matrix.
24
    pub fn as_expression(&self, child_expr: Expression) -> Expression {
25
        assert!(
26
            matches!(child_expr.return_type(), ReturnType::Matrix(_)),
27
            "The child expression given to ACOperatorKind::to_expression should be of type matrix."
28
        );
29
        let box_expr = Moo::new(child_expr);
30
        match self {
31
            ACOperatorKind::And => Expression::And(Metadata::new(), box_expr),
32
            ACOperatorKind::Or => Expression::Or(Metadata::new(), box_expr),
33
            ACOperatorKind::Product => Expression::Product(Metadata::new(), box_expr),
34
            ACOperatorKind::Sum => Expression::Sum(Metadata::new(), box_expr),
35
        }
36
    }
37

            
38
    /// Returns the identity element of this operation.
39
    ///
40
    /// # Example
41
    ///
42
    /// ```
43
    /// use conjure_cp_core::ast::{ac_operators::ACOperatorKind,Literal};
44
    ///
45
    /// let identity = ACOperatorKind::And.identity();
46
    /// assert_eq!(identity,Literal::Bool(true));
47
    /// ```
48
    pub fn identity(&self) -> Literal {
49
        match self {
50
            ACOperatorKind::And => Literal::Bool(true),
51
            ACOperatorKind::Or => Literal::Bool(false),
52
            ACOperatorKind::Product => Literal::Int(1),
53
            ACOperatorKind::Sum => Literal::Int(0),
54
        }
55
    }
56

            
57
    /// Given some guard and tail expressions, constructs the skipping operator for this operation.
58
    ///
59
    /// The skipping operator is operator that takes some boolean guard expression b and some tail
60
    /// expression x. If b is true, then it evaluates to x, otherwise it evaluates to the identity
61
    /// element.
62
    ///
63
    /// # Usage
64
    ///
65
    /// This can be used to add guards to elements of AC operations. In the example model below, we
66
    /// only want to multiply y*z by 2 if multiplyByTwo is true:
67
    ///
68
    /// ```plain
69
    /// find multiplyByTwo: bool
70
    /// find x: int(1..5)
71
    /// find y: int(1..5)
72
    /// find z: int(1..5)
73
    ///
74
    /// such that
75
    ///  
76
    /// x = product([y,z,[1,x;int(0..1)][toInt(b)]])
77
    /// ```
78
    ///
79
    /// `[1,x;int(0..1)][toInt(b)]` is the skipping operator for product.
80
    ///
81
    /// This method constructs the skipping operator, substituting in the given expressions for b
82
    /// and x.
83
    pub fn make_skip_operation(&self, guard_expr: Expression, tail_expr: Expression) -> Expression {
84
        assert!(
85
            matches!(guard_expr.return_type(), ReturnType::Bool),
86
            "The guard expression in a skipping operation should be type boolean."
87
        );
88

            
89
        match self {
90
            ACOperatorKind::And => {
91
                assert!(
92
                    matches!(tail_expr.return_type(), ReturnType::Bool),
93
                    "The tail expression in an and skipping operation should be type boolean."
94
                );
95
                let tail_expr_boxed = Moo::new(tail_expr);
96
                let guard_expr_boxed = Moo::new(guard_expr);
97
                Expression::Imply(Metadata::new(), guard_expr_boxed, tail_expr_boxed)
98
            }
99
            ACOperatorKind::Or => {
100
                assert!(
101
                    matches!(tail_expr.return_type(), ReturnType::Bool),
102
                    "The tail expression in an or skipping operation should be type boolean."
103
                );
104
                Expression::And(
105
                    Metadata::new(),
106
                    Moo::new(matrix_expr![guard_expr, tail_expr]),
107
                )
108
            }
109
            ACOperatorKind::Product => {
110
                assert!(
111
                    matches!(tail_expr.return_type(), ReturnType::Int),
112
                    "The tail expression in a product skipping operation should be type int."
113
                );
114
                let guard_expr_boxed = Moo::new(guard_expr);
115
                Expression::UnsafeIndex(
116
                    Metadata::new(),
117
                    Moo::new(
118
                        matrix_expr![Expression::Atomic(Metadata::new(),1.into()),tail_expr;Domain::int(vec![Range::Bounded(0,1)])],
119
                    ),
120
                    vec![Expression::ToInt(Metadata::new(), guard_expr_boxed)],
121
                )
122
            }
123
            ACOperatorKind::Sum => {
124
                let guard_expr_boxed = Moo::new(guard_expr);
125
                assert!(
126
                    matches!(tail_expr.return_type(), ReturnType::Int),
127
                    "The tail expression in a sum skipping operation should be type int."
128
                );
129
                Expression::Product(
130
                    Metadata::new(),
131
                    Moo::new(matrix_expr![
132
                        Expression::ToInt(Metadata::new(), guard_expr_boxed),
133
                        tail_expr
134
                    ]),
135
                )
136
            }
137
        }
138
    }
139

            
140
    /// Gives the return type of the operator, and the return types its elements should be.
141
    pub fn return_type(&self) -> ReturnType {
142
        match self {
143
            ACOperatorKind::And | ACOperatorKind::Or => ReturnType::Bool,
144
            ACOperatorKind::Product | ACOperatorKind::Sum => ReturnType::Int,
145
        }
146
    }
147
}
148

            
149
impl TryFrom<&Expression> for ACOperatorKind {
150
    type Error = ();
151
    fn try_from(expr: &Expression) -> Result<Self, Self::Error> {
152
        match expr {
153
            Expression::And(_, _) => Ok(ACOperatorKind::And),
154
            Expression::Or(_, _) => Ok(ACOperatorKind::Or),
155
            Expression::Product(_, _) => Ok(ACOperatorKind::Product),
156
            Expression::Sum(_, _) => Ok(ACOperatorKind::Sum),
157
            _ => Err(()),
158
        }
159
    }
160
}
161

            
162
impl TryFrom<Expression> for ACOperatorKind {
163
    type Error = ();
164

            
165
    fn try_from(value: Expression) -> Result<Self, Self::Error> {
166
        TryFrom::try_from(&value)
167
    }
168
}
169

            
170
impl TryFrom<Box<Expression>> for ACOperatorKind {
171
    type Error = ();
172

            
173
    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
174
        TryFrom::try_from(value.as_ref())
175
    }
176
}