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 uniplate::Uniplate;
8

            
9
use crate::ast::{Atom, Literal, SymbolTable};
10

            
11
use super::utils::is_all_constant;
12

            
13
register_rule_set!("Bubble", ("Base"));
14

            
15
// Bubble reduction rules
16

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

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

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

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

            
62
// Bubble applications
63

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

            
74
#[register_rule(("Bubble", 6000))]
75
224162
fn div_to_bubble(expr: &Expression, _: &SymbolTable) -> ApplicationResult {
76
224162
    if is_all_constant(expr) {
77
39304
        return Err(RuleNotApplicable);
78
184858
    }
79
184858
    if let Expression::UnsafeDiv(_, a, b) = expr {
80
        // bubble bottom up
81
544
        if !a.is_safe() || !b.is_safe() {
82
68
            return Err(RuleNotApplicable);
83
476
        }
84
476

            
85
476
        return Ok(Reduction::pure(Expression::Bubble(
86
476
            Metadata::new(),
87
476
            Box::new(Expression::SafeDiv(Metadata::new(), a.clone(), b.clone())),
88
476
            Box::new(Expression::Neq(
89
476
                Metadata::new(),
90
476
                b.clone(),
91
476
                Box::new(Expression::from(0)),
92
476
            )),
93
476
        )));
94
184314
    }
95
184314
    Err(ApplicationError::RuleNotApplicable)
96
224162
}
97

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

            
119
340
        return Ok(Reduction::pure(Expression::Bubble(
120
340
            Metadata::new(),
121
340
            Box::new(Expression::SafeMod(Metadata::new(), a.clone(), b.clone())),
122
340
            Box::new(Expression::Neq(
123
340
                Metadata::new(),
124
340
                b.clone(),
125
340
                Box::new(Expression::from(0)),
126
340
            )),
127
340
        )));
128
184467
    }
129
184467
    Err(ApplicationError::RuleNotApplicable)
130
224162
}
131

            
132
/// Converts an unsafe pow to a safe pow with a bubble condition.
133
///
134
/// ```text
135
/// a**b => (a ** b) @ ((a!=0 \/ b!=0) /\ b>=0
136
/// ```
137
///
138
/// Pow is only defined when `(a!=0 \/ b!=0) /\ b>=0`, so we add a condition to check for it.
139
/// This condition is brought up the tree and expanded into a conjunction with the first
140
/// boolean-type expression it is paired with.
141
///
142
#[register_rule(("Bubble", 6000))]
143
224162
fn pow_to_bubble(expr: &Expression, _: &SymbolTable) -> ApplicationResult {
144
224162
    if is_all_constant(expr) {
145
39304
        return Err(RuleNotApplicable);
146
184858
    }
147
184858
    if let Expression::UnsafePow(_, a, b) = expr.clone() {
148
        // bubble bottom up
149
102
        if !a.is_safe() || !b.is_safe() {
150
17
            return Err(RuleNotApplicable);
151
85
        }
152
85

            
153
85
        return Ok(Reduction::pure(Expression::Bubble(
154
85
            Metadata::new(),
155
85
            Box::new(Expression::SafePow(Metadata::new(), a.clone(), b.clone())),
156
85
            Box::new(Expression::And(
157
85
                Metadata::new(),
158
85
                vec![
159
85
                    Expression::Or(
160
85
                        Metadata::new(),
161
85
                        vec![
162
85
                            Expression::Neq(
163
85
                                Metadata::new(),
164
85
                                a,
165
85
                                Box::new(Atom::Literal(Literal::Int(0)).into()),
166
85
                            ),
167
85
                            Expression::Neq(
168
85
                                Metadata::new(),
169
85
                                b.clone(),
170
85
                                Box::new(Atom::Literal(Literal::Int(0)).into()),
171
85
                            ),
172
85
                        ],
173
85
                    ),
174
85
                    Expression::Geq(
175
85
                        Metadata::new(),
176
85
                        b,
177
85
                        Box::new(Atom::Literal(Literal::Int(0)).into()),
178
85
                    ),
179
85
                ],
180
85
            )),
181
85
        )));
182
184756
    }
183
184756
    Err(ApplicationError::RuleNotApplicable)
184
224162
}