-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsimplify_expr.rs
111 lines (94 loc) · 3.73 KB
/
simplify_expr.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::cell::RefCell;
use super::ast::{Expr, Opcode, Rounding};
fn is_negative_value(expr: Box<Expr>) -> (bool, Box<Expr>) {
match *expr {
Expr::Negative(ne) => match *ne {
Expr::Negative(internal) => is_negative_value(internal),
_ => (true, ne),
}
// Need to handle this case separately because in case of nested negatives the
// inner most operation will not be simplified
Expr::Op(l, o, r) => {
let simplified_internal_expr = simplify_sign(Box::new(Expr::Op(l, o, r)));
match *simplified_internal_expr {
Expr::Negative(ne) => (true, ne),
_ => (false, simplified_internal_expr),
}
},
_ => (false, expr),
}
}
fn simplify_add(lnv: (bool, Box<Expr>), rnv: (bool, Box<Expr>)) -> Box<Expr> {
let expr = match (lnv.0, rnv.0) {
(true, false) => Expr::Op(rnv.1, Opcode::Sub, lnv.1),
(true, true) => Expr::Negative(Box::new(Expr::Op(lnv.1, Opcode::Add, rnv.1))),
(false, true) => Expr::Op(lnv.1, Opcode::Sub, rnv.1),
(false, false) => Expr::Op(lnv.1, Opcode::Add, rnv.1),
};
Box::new(expr)
}
fn simplify_sub(lnv: (bool, Box<Expr>), rnv: (bool, Box<Expr>)) -> Box<Expr> {
let expr = match (lnv.0, rnv.0) {
(true, false) => Expr::Negative(Box::new(Expr::Op(lnv.1, Opcode::Add, rnv.1))),
(true, true) => Expr::Op(rnv.1, Opcode::Sub, lnv.1),
(false, true) => Expr::Op(lnv.1, Opcode::Add, rnv.1),
(false, false) => Expr::Op(lnv.1, Opcode::Sub, rnv.1),
};
Box::new(expr)
}
fn simplify_mul(lnv: (bool, Box<Expr>), rnv: (bool, Box<Expr>), r: RefCell<Rounding>) -> Box<Expr> {
let expr = if lnv.0 ^ rnv.0 {
Expr::Negative(Box::new(Expr::Op(lnv.1, Opcode::Mul(RefCell::new(Rounding::Init)), rnv.1)))
} else {
Expr::Op(lnv.1, Opcode::Mul(r), rnv.1)
};
Box::new(expr)
}
fn simplify_div(lnv: (bool, Box<Expr>), rnv: (bool, Box<Expr>), r: RefCell<Rounding>) -> Box<Expr> {
let expr = if lnv.0 ^ rnv.0 {
Expr::Negative(Box::new(Expr::Op(lnv.1, Opcode::Div(RefCell::new(Rounding::Init)), rnv.1)))
} else {
Expr::Op(lnv.1, Opcode::Div(r), rnv.1)
};
Box::new(expr)
}
fn simplify_pow(lnv: (bool, Box<Expr>), rnv: (bool, Box<Expr>)) -> Box<Expr> {
let expr = if rnv.0 {
Expr::Op(
Box::new(Expr::Number(1)),
Opcode::Div(RefCell::new(Rounding::Init)),
Box::new(Expr::Op(lnv.1, Opcode::Pow, rnv.1))
)
} else {
Expr::Op(lnv.1, Opcode::Pow, rnv.1)
};
Box::new(expr)
}
/// Simplifies the signs to bring the negative sign from values to the operations
/// and finally bring it out to the expression level if possible
/// It also reagganges the addition and substration formula to make them look better
/// with the sign. For example (-a + b) will be re-arranged to (b - a)
pub fn simplify_sign(expr: Box<Expr>) -> Box<Expr> {
match *expr {
Expr::Op(left, op, right) => {
let simplified_left = match *left {
Expr::Op(..) => simplify_sign(left),
_ => left
};
let simplified_right = match *right {
Expr::Op(..) => simplify_sign(right),
_ => right
};
let lnv = is_negative_value(simplified_left);
let rnv = is_negative_value(simplified_right);
match op {
Opcode::Add => simplify_add(lnv, rnv),
Opcode::Sub => simplify_sub(lnv, rnv),
Opcode::Mul(r) => simplify_mul(lnv, rnv, r),
Opcode::Div(r) => simplify_div(lnv, rnv, r),
Opcode::Pow => simplify_pow(lnv, rnv),
}
},
_ => expr
}
}