1
use conjure_core::{ast::Constant as Const, ast::Expression as Expr, rule::RuleApplicationError};
2
use conjure_rules::register_rule;
3

            
4
/************************************************************************/
5
/*        Rules for translating to Minion-supported constraints         */
6
/************************************************************************/
7

            
8
fn is_nested_sum(exprs: &Vec<Expr>) -> bool {
9
    for e in exprs {
10
        if let Expr::Sum(_) = e {
11
            return true;
12
        }
13
    }
14
    false
15
}
16

            
17
/**
18
 * Helper function to get the vector of expressions from a sum (or error if it's a nested sum - we need to flatten it first)
19
 */
20
fn sum_to_vector(expr: &Expr) -> Result<Vec<Expr>, RuleApplicationError> {
21
    match expr {
22
        Expr::Sum(exprs) => {
23
            if is_nested_sum(exprs) {
24
                Err(RuleApplicationError::RuleNotApplicable)
25
            } else {
26
                Ok(exprs.clone())
27
            }
28
        }
29
        _ => Err(RuleApplicationError::RuleNotApplicable),
30
    }
31
}
32

            
33
/**
34
 * Convert a Geq to a SumGeq if the left hand side is a sum:
35
 * ```text
36
 * sum([a, b, c]) >= d => sum_geq([a, b, c], d)
37
 * ```
38
 */
39
#[register_rule]
40
fn flatten_sum_geq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
41
    match expr {
42
        Expr::Geq(a, b) => {
43
            let exprs = sum_to_vector(a)?;
44
            Ok(Expr::SumGeq(exprs, b.clone()))
45
        }
46
        _ => Err(RuleApplicationError::RuleNotApplicable),
47
    }
48
}
49

            
50
/**
51
 * Convert a Leq to a SumLeq if the left hand side is a sum:
52
 * ```text
53
 * sum([a, b, c]) <= d => sum_leq([a, b, c], d)
54
 * ```
55
 */
56
#[register_rule]
57
fn sum_leq_to_sumleq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
58
    match expr {
59
        Expr::Leq(a, b) => {
60
            let exprs = sum_to_vector(a)?;
61
            Ok(Expr::SumLeq(exprs, b.clone()))
62
        }
63
        _ => Err(RuleApplicationError::RuleNotApplicable),
64
    }
65
}
66

            
67
/**
68
 * Convert a 'Eq(Sum([...]))' to a SumEq
69
 * ```text
70
 * eq(sum([a, b]), c) => sumeq([a, b], c)
71
 * ```
72
*/
73
#[register_rule]
74
fn sum_eq_to_sumeq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
75
    match expr {
76
        Expr::Eq(a, b) => {
77
            let exprs = sum_to_vector(a)?;
78
            Ok(Expr::SumEq(exprs, b.clone()))
79
        }
80
        _ => Err(RuleApplicationError::RuleNotApplicable),
81
    }
82
}
83

            
84
/**
85
 * Convert a `SumEq` to an `And(SumGeq, SumLeq)`
86
 * This is a workaround for Minion not having support for a flat "equals" operation on sums
87
 * ```text
88
 * sumeq([a, b], c) -> watched_and({
89
 *   sumleq([a, b], c),
90
 *   sumgeq([a, b], c)
91
 * })
92
 * ```
93
 * I. e.
94
 * ```text
95
 * ((a + b) >= c) && ((a + b) <= c)
96
 * a + b = c
97
 * ```
98
 */
99
#[register_rule]
100
fn sumeq_to_minion(expr: &Expr) -> Result<Expr, RuleApplicationError> {
101
    match expr {
102
        Expr::SumEq(exprs, eq_to) => Ok(Expr::And(vec![
103
            Expr::SumGeq(exprs.clone(), Box::from(*eq_to.clone())),
104
            Expr::SumLeq(exprs.clone(), Box::from(*eq_to.clone())),
105
        ])),
106
        _ => Err(RuleApplicationError::RuleNotApplicable),
107
    }
108
}
109

            
110
/**
111
* Convert a Lt to an Ineq:
112

            
113
* ```text
114
* a < b => a - b < -1
115
* ```
116
*/
117
#[register_rule]
118
fn lt_to_ineq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
119
    match expr {
120
        Expr::Lt(a, b) => Ok(Expr::Ineq(
121
            a.clone(),
122
            b.clone(),
123
            Box::new(Expr::Constant(Const::Int(-1))),
124
        )),
125
        _ => Err(RuleApplicationError::RuleNotApplicable),
126
    }
127
}
128

            
129
/**
130
* Convert a Gt to an Ineq:
131
*
132
* ```text
133
* a > b => b - a < -1
134
* ```
135
*/
136
#[register_rule]
137
fn gt_to_ineq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
138
    match expr {
139
        Expr::Gt(a, b) => Ok(Expr::Ineq(
140
            b.clone(),
141
            a.clone(),
142
            Box::new(Expr::Constant(Const::Int(-1))),
143
        )),
144
        _ => Err(RuleApplicationError::RuleNotApplicable),
145
    }
146
}
147

            
148
/**
149
* Convert a Geq to an Ineq:
150
*
151
* ```text
152
* a >= b => b - a < 0
153
* ```
154
*/
155
#[register_rule]
156
fn geq_to_ineq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
157
    match expr {
158
        Expr::Geq(a, b) => Ok(Expr::Ineq(
159
            b.clone(),
160
            a.clone(),
161
            Box::new(Expr::Constant(Const::Int(0))),
162
        )),
163
        _ => Err(RuleApplicationError::RuleNotApplicable),
164
    }
165
}
166

            
167
/**
168
* Convert a Leq to an Ineq:
169
*
170
* ```text
171
* a <= b => a - b < 0
172
* ```
173
*/
174
#[register_rule]
175
fn leq_to_ineq(expr: &Expr) -> Result<Expr, RuleApplicationError> {
176
    match expr {
177
        Expr::Leq(a, b) => Ok(Expr::Ineq(
178
            a.clone(),
179
            b.clone(),
180
            Box::new(Expr::Constant(Const::Int(0))),
181
        )),
182
        _ => Err(RuleApplicationError::RuleNotApplicable),
183
    }
184
}