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

            
4
/*****************************************************************************/
5
/*        This file contains basic rules for simplifying expressions         */
6
/*****************************************************************************/
7

            
8
/**
9
 * Remove nothings from expressions:
10
 * ```text
11
 * and([a, nothing, b]) = and([a, b])
12
 * sum([a, nothing, b]) = sum([a, b])
13
 * sum_leq([a, nothing, b], c) = sum_leq([a, b], c)
14
 * ...
15
 * ```
16
*/
17
#[register_rule]
18
fn remove_nothings(expr: &Expr) -> Result<Expr, RuleApplicationError> {
19
    fn remove_nothings(exprs: Vec<&Expr>) -> Result<Vec<&Expr>, RuleApplicationError> {
20
        let mut changed = false;
21
        let mut new_exprs = Vec::new();
22

            
23
        for e in exprs {
24
            match e.clone() {
25
                Expr::Nothing => {
26
                    changed = true;
27
                }
28
                _ => new_exprs.push(e),
29
            }
30
        }
31

            
32
        if changed {
33
            Ok(new_exprs)
34
        } else {
35
            Err(RuleApplicationError::RuleNotApplicable)
36
        }
37
    }
38

            
39
    match expr {
40
        Expr::And(_) | Expr::Or(_) | Expr::Sum(_) => match expr.sub_expressions() {
41
            None => Err(RuleApplicationError::RuleNotApplicable),
42
            Some(sub) => {
43
                let new_sub = remove_nothings(sub)?;
44
                let new_expr = expr.with_sub_expressions(new_sub);
45
                Ok(new_expr)
46
            }
47
        },
48
        Expr::SumEq(_, _) | Expr::SumLeq(_, _) | Expr::SumGeq(_, _) => {
49
            match expr.sub_expressions() {
50
                None => Err(RuleApplicationError::RuleNotApplicable),
51
                Some(sub) => {
52
                    // Keep the last sub expression, which is the right hand side expression
53
                    let new_rhs = sub[sub.len() - 1];
54

            
55
                    // Remove all nothings from the left hand side expressions
56
                    let mut new_sub_exprs = remove_nothings(sub[..sub.len() - 1].to_vec())?;
57

            
58
                    // Add the right hand side expression back
59
                    new_sub_exprs.push(new_rhs);
60

            
61
                    let new_expr = expr.with_sub_expressions(new_sub_exprs);
62
                    Ok(new_expr)
63
                }
64
            }
65
        }
66
        _ => Err(RuleApplicationError::RuleNotApplicable),
67
    }
68
}
69

            
70
/**
71
 * Remove empty expressions:
72
 * ```text
73
 * [] = Nothing
74
 * ```
75
 */
76
#[register_rule]
77
fn empty_to_nothing(expr: &Expr) -> Result<Expr, RuleApplicationError> {
78
    match expr.sub_expressions() {
79
        None => Err(RuleApplicationError::RuleNotApplicable),
80
        Some(sub) => {
81
            if sub.is_empty() {
82
                Ok(Expr::Nothing)
83
            } else {
84
                Err(RuleApplicationError::RuleNotApplicable)
85
            }
86
        }
87
    }
88
}
89

            
90
/**
91
 * Evaluate sum of constants:
92
 * ```text
93
 * sum([1, 2, 3]) = 6
94
 * ```
95
 */
96
#[register_rule]
97
fn sum_constants(expr: &Expr) -> Result<Expr, RuleApplicationError> {
98
    match expr {
99
        Expr::Sum(exprs) => {
100
            let mut sum = 0;
101
            let mut new_exprs = Vec::new();
102
            let mut changed = false;
103
            for e in exprs {
104
                match e {
105
                    Expr::Constant(Const::Int(i)) => {
106
                        sum += i;
107
                        changed = true;
108
                    }
109
                    _ => new_exprs.push(e.clone()),
110
                }
111
            }
112
            if !changed {
113
                return Err(RuleApplicationError::RuleNotApplicable);
114
            }
115
            new_exprs.push(Expr::Constant(Const::Int(sum)));
116
            Ok(Expr::Sum(new_exprs)) // Let other rules handle only one Expr being contained in the sum
117
        }
118
        _ => Err(RuleApplicationError::RuleNotApplicable),
119
    }
120
}
121

            
122
/**
123
 * Unwrap trivial sums:
124
 * ```text
125
 * sum([a]) = a
126
 * ```
127
 */
128
#[register_rule]
129
fn unwrap_sum(expr: &Expr) -> Result<Expr, RuleApplicationError> {
130
    match expr {
131
        Expr::Sum(exprs) if (exprs.len() == 1) => Ok(exprs[0].clone()),
132
        _ => Err(RuleApplicationError::RuleNotApplicable),
133
    }
134
}
135

            
136
/**
137
 * Flatten nested sums:
138
 * ```text
139
 * sum(sum(a, b), c) = sum(a, b, c)
140
 * ```
141
 */
142
#[register_rule]
143
pub fn flatten_nested_sum(expr: &Expr) -> Result<Expr, RuleApplicationError> {
144
    match expr {
145
        Expr::Sum(exprs) => {
146
            let mut new_exprs = Vec::new();
147
            let mut changed = false;
148
            for e in exprs {
149
                match e {
150
                    Expr::Sum(sub_exprs) => {
151
                        changed = true;
152
                        for e in sub_exprs {
153
                            new_exprs.push(e.clone());
154
                        }
155
                    }
156
                    _ => new_exprs.push(e.clone()),
157
                }
158
            }
159
            if !changed {
160
                return Err(RuleApplicationError::RuleNotApplicable);
161
            }
162
            Ok(Expr::Sum(new_exprs))
163
        }
164
        _ => Err(RuleApplicationError::RuleNotApplicable),
165
    }
166
}
167

            
168
/**
169
* Unwrap nested `or`
170

            
171
* ```text
172
* or(or(a, b), c) = or(a, b, c)
173
* ```
174
 */
175
#[register_rule]
176
fn unwrap_nested_or(expr: &Expr) -> Result<Expr, RuleApplicationError> {
177
    match expr {
178
        Expr::Or(exprs) => {
179
            let mut new_exprs = Vec::new();
180
            let mut changed = false;
181
            for e in exprs {
182
                match e {
183
                    Expr::Or(exprs) => {
184
                        changed = true;
185
                        for e in exprs {
186
                            new_exprs.push(e.clone());
187
                        }
188
                    }
189
                    _ => new_exprs.push(e.clone()),
190
                }
191
            }
192
            if !changed {
193
                return Err(RuleApplicationError::RuleNotApplicable);
194
            }
195
            Ok(Expr::Or(new_exprs))
196
        }
197
        _ => Err(RuleApplicationError::RuleNotApplicable),
198
    }
199
}
200

            
201
/**
202
* Unwrap nested `and`
203

            
204
* ```text
205
* and(and(a, b), c) = and(a, b, c)
206
* ```
207
 */
208
#[register_rule]
209
fn unwrap_nested_and(expr: &Expr) -> Result<Expr, RuleApplicationError> {
210
    match expr {
211
        Expr::And(exprs) => {
212
            let mut new_exprs = Vec::new();
213
            let mut changed = false;
214
            for e in exprs {
215
                match e {
216
                    Expr::And(exprs) => {
217
                        changed = true;
218
                        for e in exprs {
219
                            new_exprs.push(e.clone());
220
                        }
221
                    }
222
                    _ => new_exprs.push(e.clone()),
223
                }
224
            }
225
            if !changed {
226
                return Err(RuleApplicationError::RuleNotApplicable);
227
            }
228
            Ok(Expr::And(new_exprs))
229
        }
230
        _ => Err(RuleApplicationError::RuleNotApplicable),
231
    }
232
}
233

            
234
/**
235
* Remove double negation:
236

            
237
* ```text
238
* not(not(a)) = a
239
* ```
240
 */
241
#[register_rule]
242
fn remove_double_negation(expr: &Expr) -> Result<Expr, RuleApplicationError> {
243
    match expr {
244
        Expr::Not(contents) => match contents.as_ref() {
245
            Expr::Not(expr_box) => Ok(*expr_box.clone()),
246
            _ => Err(RuleApplicationError::RuleNotApplicable),
247
        },
248
        _ => Err(RuleApplicationError::RuleNotApplicable),
249
    }
250
}
251

            
252
/**
253
 * Remove trivial `and` (only one element):
254
 * ```text
255
 * and([a]) = a
256
 * ```
257
 */
258
#[register_rule]
259
fn remove_trivial_and(expr: &Expr) -> Result<Expr, RuleApplicationError> {
260
    match expr {
261
        Expr::And(exprs) => {
262
            if exprs.len() == 1 {
263
                return Ok(exprs[0].clone());
264
            }
265
            Err(RuleApplicationError::RuleNotApplicable)
266
        }
267
        _ => Err(RuleApplicationError::RuleNotApplicable),
268
    }
269
}
270

            
271
/**
272
 * Remove trivial `or` (only one element):
273
 * ```text
274
 * or([a]) = a
275
 * ```
276
 */
277
#[register_rule]
278
fn remove_trivial_or(expr: &Expr) -> Result<Expr, RuleApplicationError> {
279
    match expr {
280
        Expr::Or(exprs) => {
281
            if exprs.len() == 1 {
282
                return Ok(exprs[0].clone());
283
            }
284
            Err(RuleApplicationError::RuleNotApplicable)
285
        }
286
        _ => Err(RuleApplicationError::RuleNotApplicable),
287
    }
288
}
289

            
290
/**
291
 * Remove constant bools from or expressions
292
 * ```text
293
 * or([true, a]) = true
294
 * or([false, a]) = a
295
 * ```
296
 */
297
#[register_rule]
298
fn remove_constants_from_or(expr: &Expr) -> Result<Expr, RuleApplicationError> {
299
    match expr {
300
        Expr::Or(exprs) => {
301
            let mut new_exprs = Vec::new();
302
            let mut changed = false;
303
            for e in exprs {
304
                match e {
305
                    Expr::Constant(Const::Bool(val)) => {
306
                        if *val {
307
                            // If we find a true, the whole expression is true
308
                            return Ok(Expr::Constant(Const::Bool(true)));
309
                        } else {
310
                            // If we find a false, we can ignore it
311
                            changed = true;
312
                        }
313
                    }
314
                    _ => new_exprs.push(e.clone()),
315
                }
316
            }
317
            if !changed {
318
                return Err(RuleApplicationError::RuleNotApplicable);
319
            }
320
            Ok(Expr::Or(new_exprs))
321
        }
322
        _ => Err(RuleApplicationError::RuleNotApplicable),
323
    }
324
}
325

            
326
/**
327
 * Remove constant bools from and expressions
328
 * ```text
329
 * and([true, a]) = a
330
 * and([false, a]) = false
331
 * ```
332
 */
333
#[register_rule]
334
fn remove_constants_from_and(expr: &Expr) -> Result<Expr, RuleApplicationError> {
335
    match expr {
336
        Expr::And(exprs) => {
337
            let mut new_exprs = Vec::new();
338
            let mut changed = false;
339
            for e in exprs {
340
                match e {
341
                    Expr::Constant(Const::Bool(val)) => {
342
                        if !*val {
343
                            // If we find a false, the whole expression is false
344
                            return Ok(Expr::Constant(Const::Bool(false)));
345
                        } else {
346
                            // If we find a true, we can ignore it
347
                            changed = true;
348
                        }
349
                    }
350
                    _ => new_exprs.push(e.clone()),
351
                }
352
            }
353
            if !changed {
354
                return Err(RuleApplicationError::RuleNotApplicable);
355
            }
356
            Ok(Expr::And(new_exprs))
357
        }
358
        _ => Err(RuleApplicationError::RuleNotApplicable),
359
    }
360
}
361

            
362
/**
363
 * Evaluate Not expressions with constant bools
364
 * ```text
365
 * not(true) = false
366
 * not(false) = true
367
 * ```
368
 */
369
#[register_rule]
370
fn evaluate_constant_not(expr: &Expr) -> Result<Expr, RuleApplicationError> {
371
    match expr {
372
        Expr::Not(contents) => match contents.as_ref() {
373
            Expr::Constant(Const::Bool(val)) => Ok(Expr::Constant(Const::Bool(!val))),
374
            _ => Err(RuleApplicationError::RuleNotApplicable),
375
        },
376
        _ => Err(RuleApplicationError::RuleNotApplicable),
377
    }
378
}