1
use std::collections::HashSet;
2

            
3
use conjure_macros::register_rule;
4

            
5
use crate::ast::{Atom, Expression as Expr, Literal::*};
6
use crate::rule_engine::{ApplicationResult, Reduction};
7
use crate::Model;
8

            
9
#[register_rule(("Base",9000))]
10
18
fn partial_evaluator(expr: &Expr, _: &Model) -> ApplicationResult {
11
    use conjure_core::rule_engine::ApplicationError::RuleNotApplicable;
12
    use Expr::*;
13

            
14
    // NOTE: If nothing changes, we must return RuleNotApplicable, or the rewriter will try this
15
    // rule infinitely!
16
    // This is why we always check whether we found a constant or not.
17
18
    match expr.clone() {
18
        Bubble(_, _, _) => Err(RuleNotApplicable),
19
9
        Atomic(_, _) => Err(RuleNotApplicable),
20
        Sum(m, vec) => {
21
            let mut acc = 0;
22
            let mut n_consts = 0;
23
            let mut new_vec: Vec<Expr> = Vec::new();
24
            for expr in vec {
25
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
26
                    acc += x;
27
                    n_consts += 1;
28
                } else {
29
                    new_vec.push(expr);
30
                }
31
            }
32
            if acc != 0 {
33
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(acc))));
34
            }
35

            
36
            if n_consts <= 1 {
37
                Err(RuleNotApplicable)
38
            } else {
39
                Ok(Reduction::pure(Sum(m, new_vec)))
40
            }
41
        }
42

            
43
        Min(m, vec) => {
44
            let mut acc: Option<i32> = None;
45
            let mut n_consts = 0;
46
            let mut new_vec: Vec<Expr> = Vec::new();
47
            for expr in vec {
48
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
49
                    n_consts += 1;
50
                    acc = match acc {
51
                        Some(i) => {
52
                            if i > x {
53
                                Some(x)
54
                            } else {
55
                                Some(i)
56
                            }
57
                        }
58
                        None => Some(x),
59
                    };
60
                } else {
61
                    new_vec.push(expr);
62
                }
63
            }
64

            
65
            if let Some(i) = acc {
66
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(i))));
67
            }
68

            
69
            if n_consts <= 1 {
70
                Err(RuleNotApplicable)
71
            } else {
72
                Ok(Reduction::pure(Min(m, new_vec)))
73
            }
74
        }
75
        Max(m, vec) => {
76
            let mut acc: Option<i32> = None;
77
            let mut n_consts = 0;
78
            let mut new_vec: Vec<Expr> = Vec::new();
79
            for expr in vec {
80
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
81
                    n_consts += 1;
82
                    acc = match acc {
83
                        Some(i) => {
84
                            if i < x {
85
                                Some(x)
86
                            } else {
87
                                Some(i)
88
                            }
89
                        }
90
                        None => Some(x),
91
                    };
92
                } else {
93
                    new_vec.push(expr);
94
                }
95
            }
96

            
97
            if let Some(i) = acc {
98
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(i))));
99
            }
100

            
101
            if n_consts <= 1 {
102
                Err(RuleNotApplicable)
103
            } else {
104
                Ok(Reduction::pure(Max(m, new_vec)))
105
            }
106
        }
107
        Not(_, _) => Err(RuleNotApplicable),
108
9
        Or(m, vec) => {
109
9
            let mut new_vec: Vec<Expr> = Vec::new();
110
9
            let mut has_const: bool = false;
111
9
            for expr in vec {
112
                if let Expr::Atomic(_, Atom::Literal(Bool(x))) = expr {
113
                    has_const = true;
114
                    if x {
115
                        return Ok(Reduction::pure(Atomic(
116
                            Default::default(),
117
                            Atom::Literal(Bool(true)),
118
                        )));
119
                    }
120
                } else {
121
                    new_vec.push(expr);
122
                }
123
            }
124

            
125
9
            if !has_const {
126
9
                Err(RuleNotApplicable)
127
            } else {
128
                Ok(Reduction::pure(Or(m, new_vec)))
129
            }
130
        }
131
        And(m, vec) => {
132
            let mut new_vec: Vec<Expr> = Vec::new();
133
            let mut has_const: bool = false;
134
            for expr in vec {
135
                if let Expr::Atomic(_, Atom::Literal(Bool(x))) = expr {
136
                    has_const = true;
137
                    if !x {
138
                        return Ok(Reduction::pure(Atomic(
139
                            Default::default(),
140
                            Atom::Literal(Bool(false)),
141
                        )));
142
                    }
143
                } else {
144
                    new_vec.push(expr);
145
                }
146
            }
147

            
148
            if !has_const {
149
                Err(RuleNotApplicable)
150
            } else {
151
                Ok(Reduction::pure(And(m, new_vec)))
152
            }
153
        }
154
        Eq(_, _, _) => Err(RuleNotApplicable),
155
        Neq(_, _, _) => Err(RuleNotApplicable),
156
        Geq(_, _, _) => Err(RuleNotApplicable),
157
        Leq(_, _, _) => Err(RuleNotApplicable),
158
        Gt(_, _, _) => Err(RuleNotApplicable),
159
        Lt(_, _, _) => Err(RuleNotApplicable),
160
        SafeDiv(_, _, _) => Err(RuleNotApplicable),
161
        UnsafeDiv(_, _, _) => Err(RuleNotApplicable),
162
        SumEq(m, vec, eq) => {
163
            let mut acc = 0;
164
            let mut new_vec: Vec<Expr> = Vec::new();
165
            let mut n_consts = 0;
166
            for expr in vec {
167
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
168
                    n_consts += 1;
169
                    acc += x;
170
                } else {
171
                    new_vec.push(expr);
172
                }
173
            }
174

            
175
            if let Expr::Atomic(_, Atom::Literal(Int(x))) = *eq {
176
                if acc != 0 {
177
                    // when rhs is a constant, move lhs constants to rhs
178
                    return Ok(Reduction::pure(SumEq(
179
                        m,
180
                        new_vec,
181
                        Box::new(Expr::Atomic(
182
                            Default::default(),
183
                            Atom::Literal(Int(x - acc)),
184
                        )),
185
                    )));
186
                }
187
            } else if acc != 0 {
188
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(acc))));
189
            }
190

            
191
            if n_consts <= 1 {
192
                Err(RuleNotApplicable)
193
            } else {
194
                Ok(Reduction::pure(SumEq(m, new_vec, eq)))
195
            }
196
        }
197
        SumGeq(m, vec, geq) => {
198
            let mut acc = 0;
199
            let mut new_vec: Vec<Expr> = Vec::new();
200
            let mut n_consts = 0;
201
            for expr in vec {
202
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
203
                    n_consts += 1;
204
                    acc += x;
205
                } else {
206
                    new_vec.push(expr);
207
                }
208
            }
209

            
210
            if let Expr::Atomic(_, Atom::Literal(Int(x))) = *geq {
211
                if acc != 0 {
212
                    // when rhs is a constant, move lhs constants to rhs
213
                    return Ok(Reduction::pure(SumGeq(
214
                        m,
215
                        new_vec,
216
                        Box::new(Expr::Atomic(
217
                            Default::default(),
218
                            Atom::Literal(Int(x - acc)),
219
                        )),
220
                    )));
221
                }
222
            } else if acc != 0 {
223
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(acc))));
224
            }
225

            
226
            if n_consts <= 1 {
227
                Err(RuleNotApplicable)
228
            } else {
229
                Ok(Reduction::pure(SumGeq(m, new_vec, geq)))
230
            }
231
        }
232
        SumLeq(m, vec, leq) => {
233
            let mut acc = 0;
234
            let mut new_vec: Vec<Expr> = Vec::new();
235
            let mut n_consts = 0;
236
            for expr in vec {
237
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
238
                    n_consts += 1;
239
                    acc += x;
240
                } else {
241
                    new_vec.push(expr);
242
                }
243
            }
244

            
245
            if let Expr::Atomic(_, Atom::Literal(Int(x))) = *leq {
246
                // when rhs is a constant, move lhs constants to rhs
247
                if acc != 0 {
248
                    return Ok(Reduction::pure(SumLeq(
249
                        m,
250
                        new_vec,
251
                        Box::new(Expr::Atomic(
252
                            Default::default(),
253
                            Atom::Literal(Int(x - acc)),
254
                        )),
255
                    )));
256
                }
257
            } else if acc != 0 {
258
                new_vec.push(Expr::Atomic(Default::default(), Atom::Literal(Int(acc))));
259
            }
260

            
261
            if n_consts <= 1 {
262
                Err(RuleNotApplicable)
263
            } else {
264
                Ok(Reduction::pure(SumLeq(m, new_vec, leq)))
265
            }
266
        }
267
        DivEqUndefZero(_, _, _, _) => Err(RuleNotApplicable),
268
        Ineq(_, _, _, _) => Err(RuleNotApplicable),
269
        AllDiff(m, vec) => {
270
            let mut consts: HashSet<i32> = HashSet::new();
271

            
272
            // check for duplicate constant values which would fail the constraint
273
            for expr in &vec {
274
                if let Expr::Atomic(_, Atom::Literal(Int(x))) = expr {
275
                    if !consts.insert(*x) {
276
                        return Ok(Reduction::pure(Expr::Atomic(m, Atom::Literal(Bool(false)))));
277
                    }
278
                }
279
            }
280

            
281
            // nothing has changed
282
            Err(RuleNotApplicable)
283
        }
284
        WatchedLiteral(_, _, _) => Err(RuleNotApplicable),
285
        Reify(_, _, _) => Err(RuleNotApplicable),
286
        AuxDeclaration(_, _, _) => Err(RuleNotApplicable),
287
        UnsafeMod(_, _, _) => Err(RuleNotApplicable),
288
        SafeMod(_, _, _) => Err(RuleNotApplicable),
289
        ModuloEqUndefZero(_, _, _, _) => Err(RuleNotApplicable),
290
    }
291
18
}