1
use conjure_core::ast::{Expression, ReturnType};
2
use conjure_core::metadata::Metadata;
3
use conjure_core::rule_engine::{
4
    register_rule, register_rule_set, ApplicationError, ApplicationError::*, ApplicationResult,
5
    Reduction,
6
};
7
use conjure_core::Model;
8
use uniplate::Uniplate;
9

            
10
use super::utils::is_all_constant;
11

            
12
register_rule_set!("Bubble", 100, ("Base"));
13

            
14
// Bubble reduction rules
15

            
16
/*
17
    Reduce bubbles with a boolean expression to a conjunction with their condition.
18

            
19
    e.g. (a / b = c) @ (b != 0) => (a / b = c) & (b != 0)
20
*/
21
#[register_rule(("Bubble", 8900))]
22
87992
fn expand_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
23
102
    match expr {
24
102
        Expression::Bubble(_, a, b) if a.return_type() == Some(ReturnType::Bool) => {
25
102
            Ok(Reduction::pure(Expression::And(
26
102
                Metadata::new(),
27
102
                vec![*a.clone(), *b.clone()],
28
102
            )))
29
        }
30
87890
        _ => Err(ApplicationError::RuleNotApplicable),
31
    }
32
87992
}
33

            
34
/*
35
    Bring bubbles with a non-boolean expression higher up the tree.
36

            
37
    E.g. ((a / b) @ (b != 0)) = c => (a / b = c) @ (b != 0)
38
*/
39
#[register_rule(("Bubble", 8900))]
40
87992
fn bubble_up(expr: &Expression, _: &Model) -> ApplicationResult {
41
87992
    let mut sub = expr.children();
42
87992
    let mut bubbled_conditions = vec![];
43
92956
    for e in sub.iter_mut() {
44
92956
        if let Expression::Bubble(_, a, b) = e {
45
119
            if a.return_type() != Some(ReturnType::Bool) {
46
102
                bubbled_conditions.push(*b.clone());
47
102
                *e = *a.clone();
48
102
            }
49
92837
        }
50
    }
51
87992
    if bubbled_conditions.is_empty() {
52
87890
        return Err(ApplicationError::RuleNotApplicable);
53
102
    }
54
102
    Ok(Reduction::pure(Expression::Bubble(
55
102
        Metadata::new(),
56
102
        Box::new(expr.with_children(sub)),
57
102
        Box::new(Expression::And(Metadata::new(), bubbled_conditions)),
58
102
    )))
59
87992
}
60

            
61
// Bubble applications
62

            
63
/*
64
    Convert an unsafe division to a safe division with a bubble condition.
65

            
66
    Division by zero is undefined and therefore not allowed, so we add a condition to check for it.
67
    This condition is brought up the tree and expanded into a conjunction with the first boolean-type expression it is paired with.
68

            
69
    E.g. a / b => (a / b) @ (b != 0)
70

            
71
*/
72
#[register_rule(("Bubble", 6000))]
73
87992
fn div_to_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
74
87992
    if is_all_constant(expr) {
75
18513
        return Err(RuleNotApplicable);
76
69479
    }
77
69479
    if let Expression::UnsafeDiv(_, a, b) = expr {
78
102
        return Ok(Reduction::pure(Expression::Bubble(
79
102
            Metadata::new(),
80
102
            Box::new(Expression::SafeDiv(Metadata::new(), a.clone(), b.clone())),
81
102
            Box::new(Expression::Neq(
82
102
                Metadata::new(),
83
102
                b.clone(),
84
102
                Box::new(Expression::from(0)),
85
102
            )),
86
102
        )));
87
69377
    }
88
69377
    Err(ApplicationError::RuleNotApplicable)
89
87992
}