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
100062
fn expand_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
23
442
    match expr {
24
442
        Expression::Bubble(_, a, b) if a.return_type() == Some(ReturnType::Bool) => {
25
442
            Ok(Reduction::pure(Expression::And(
26
442
                Metadata::new(),
27
442
                vec![*a.clone(), *b.clone()],
28
442
            )))
29
        }
30
99620
        _ => Err(ApplicationError::RuleNotApplicable),
31
    }
32
100062
}
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
100062
fn bubble_up(expr: &Expression, _: &Model) -> ApplicationResult {
41
100062
    let mut sub = expr.children();
42
100062
    let mut bubbled_conditions = vec![];
43
108834
    for e in sub.iter_mut() {
44
108834
        if let Expression::Bubble(_, a, b) = e {
45
748
            if a.return_type() != Some(ReturnType::Bool) {
46
544
                bubbled_conditions.push(*b.clone());
47
544
                *e = *a.clone();
48
544
            }
49
108086
        }
50
    }
51
100062
    if bubbled_conditions.is_empty() {
52
99518
        return Err(ApplicationError::RuleNotApplicable);
53
544
    }
54
544
    Ok(Reduction::pure(Expression::Bubble(
55
544
        Metadata::new(),
56
544
        Box::new(expr.with_children(sub)),
57
544
        Box::new(Expression::And(Metadata::new(), bubbled_conditions)),
58
544
    )))
59
100062
}
60

            
61
// Bubble applications
62

            
63
/// Converts an unsafe division to a safe division with a bubble condition.
64
///
65
/// ```text
66
///     a / b => (a / b) @ (b != 0)
67
/// ```
68
///
69
/// Division by zero is undefined and therefore not allowed, so we add a condition to check for it.
70
/// This condition is brought up the tree and expanded into a conjunction with the first
71
/// boolean-type expression it is paired with.
72

            
73
#[register_rule(("Bubble", 6000))]
74
100062
fn div_to_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
75
100062
    if is_all_constant(expr) {
76
19873
        return Err(RuleNotApplicable);
77
80189
    }
78
80189
    if let Expression::UnsafeDiv(_, a, b) = expr {
79
        // bubble bottom up
80
323
        if a.can_be_undefined() || b.can_be_undefined() {
81
102
            return Err(RuleNotApplicable);
82
221
        }
83

            
84
        // either do bubble / bubble or not bubble / not bubble
85
221
        if matches!(**a, Expression::Bubble(_, _, _)) != matches!(**b, Expression::Bubble(_, _, _))
86
        {
87
            return Err(RuleNotApplicable);
88
221
        }
89
221

            
90
221
        return Ok(Reduction::pure(Expression::Bubble(
91
221
            Metadata::new(),
92
221
            Box::new(Expression::SafeDiv(Metadata::new(), a.clone(), b.clone())),
93
221
            Box::new(Expression::Neq(
94
221
                Metadata::new(),
95
221
                b.clone(),
96
221
                Box::new(Expression::from(0)),
97
221
            )),
98
221
        )));
99
79866
    }
100
79866
    Err(ApplicationError::RuleNotApplicable)
101
100062
}
102

            
103
/// Converts an unsafe mod to a safe mod with a bubble condition.
104
///
105
/// ```text
106
/// a % b => (a % b) @ (b != 0)
107
/// ```
108
///
109
/// Mod zero is undefined and therefore not allowed, so we add a condition to check for it.
110
/// This condition is brought up the tree and expanded into a conjunction with the first
111
/// boolean-type expression it is paired with.
112
///
113
#[register_rule(("Bubble", 6000))]
114
100062
fn mod_to_bubble(expr: &Expression, _: &Model) -> ApplicationResult {
115
100062
    if is_all_constant(expr) {
116
19873
        return Err(RuleNotApplicable);
117
80189
    }
118
80189
    if let Expression::UnsafeMod(_, a, b) = expr {
119
        // bubble bottom up
120
323
        if a.can_be_undefined() || b.can_be_undefined() {
121
102
            return Err(RuleNotApplicable);
122
221
        }
123

            
124
        // either do bubble / bubble or not bubble / not bubble
125
221
        if matches!(**a, Expression::Bubble(_, _, _)) != matches!(**b, Expression::Bubble(_, _, _))
126
        {
127
            return Err(RuleNotApplicable);
128
221
        }
129
221

            
130
221
        return Ok(Reduction::pure(Expression::Bubble(
131
221
            Metadata::new(),
132
221
            Box::new(Expression::SafeMod(Metadata::new(), a.clone(), b.clone())),
133
221
            Box::new(Expression::Neq(
134
221
                Metadata::new(),
135
221
                b.clone(),
136
221
                Box::new(Expression::from(0)),
137
221
            )),
138
221
        )));
139
79866
    }
140
79866
    Err(ApplicationError::RuleNotApplicable)
141
100062
}