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 crate::ast::{Atom, Literal};
11

            
12
use super::utils::is_all_constant;
13

            
14
register_rule_set!("Bubble", 100, ("Base"));
15

            
16
// Bubble reduction rules
17

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

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

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

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

            
63
// Bubble applications
64

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

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

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

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

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

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

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