1use num_bigint::BigInt;
2use num_rational::BigRational;
3
4use crate::builtins::BuiltinSymbols;
5use crate::expr_pool::{ExprData, ExprId, ExprPool};
6use crate::number::Number;
7
8pub fn simplify(pool: &ExprPool, builtins: &BuiltinSymbols, id: ExprId) -> ExprId {
12 let node = match pool.get(id) {
13 Some(n) => n,
14 None => return id,
15 };
16 match node {
17 ExprData::Add(items) => {
18 if items.is_empty() {
19 return id;
20 }
21 let mut acc = simplify(pool, builtins, items[0]);
22 for &it in &items[1..] {
23 let s = simplify(pool, builtins, it);
24 acc = pool.add2(acc, s);
25 }
26 acc
27 }
28 ExprData::Mul(items) => {
29 if items.is_empty() {
30 return id;
31 }
32 let mut acc = simplify(pool, builtins, items[0]);
33 for &it in &items[1..] {
34 let s = simplify(pool, builtins, it);
35 acc = pool.mul2(acc, s);
36 }
37 acc
38 }
39 ExprData::Pow { base, exp } => {
40 let b = simplify(pool, builtins, base);
41 let e = simplify(pool, builtins, exp);
42 if let Some(ExprData::Apply { f, args }) = pool.get(b)
43 && f == pool.symbol(builtins.sqrt)
44 && args.len() == 1
45 && e == pool.integer(2)
46 {
47 return simplify(pool, builtins, args[0]);
48 }
49 if b == pool.symbol(builtins.e) && let Some(r) = euler(pool, builtins, e) {
50 return r;
51 }
52 pool.pow2(b, e)
53 }
54 ExprData::Apply { f, args } => {
55 let mut new_args = Vec::with_capacity(args.len());
56 for &a in args.iter() {
57 new_args.push(simplify(pool, builtins, a));
58 }
59 if let Some(r) = apply_rule(pool, builtins, f, &new_args) {
60 r
61 } else {
62 pool.apply(f, &new_args)
63 }
64 }
65 _ => id,
66 }
67}
68
69fn apply_rule(pool: &ExprPool, builtins: &BuiltinSymbols, f: ExprId, args: &[ExprId]) -> Option<ExprId> {
70 if args.len() != 1 {
71 return None;
72 }
73 let arg = args[0];
74 if f == pool.symbol(builtins.sqrt) {
75 if let Some(n) = pool.const_number(arg) && let Some(s) = n.sqrt() {
76 return Some(pool.number(&s));
77 }
78 return None;
79 }
80 let sin = pool.symbol(builtins.sin);
81 let cos = pool.symbol(builtins.cos);
82 let tan = pool.symbol(builtins.tan);
83 if f == sin || f == cos || f == tan {
84 if let Some((c, s)) = trig_of_angle(pool, builtins, arg) {
85 if f == sin {
86 return Some(pool.number(&s));
87 }
88 if f == cos {
89 return Some(pool.number(&c));
90 }
91 if !c.is_zero() {
92 return Some(pool.number(&(s / c)));
93 }
94 }
95 return None;
96 }
97 if f == pool.symbol(builtins.exp) {
98 if arg == pool.integer(0) {
99 return Some(pool.integer(1));
100 }
101 return None;
102 }
103 if f == pool.symbol(builtins.log) || f == pool.symbol(builtins.ln) {
104 if arg == pool.integer(1) {
105 return Some(pool.integer(0));
106 }
107 if arg == pool.symbol(builtins.e) {
108 return Some(pool.integer(1));
109 }
110 return None;
111 }
112 if f == pool.symbol(builtins.abs) {
113 if let Some(n) = pool.const_number(arg) {
114 return Some(pool.number(&n.abs()));
115 }
116 return None;
117 }
118 None
119}
120
121fn trig_of_angle(pool: &ExprPool, builtins: &BuiltinSymbols, expr: ExprId) -> Option<(Number, Number)> {
123 let c = rational_pi_coefficient(pool, builtins, expr)?;
124 exact_trig(&c)
125}
126
127fn rational_pi_coefficient(pool: &ExprPool, builtins: &BuiltinSymbols, expr: ExprId) -> Option<BigRational> {
128 let node = pool.get(expr)?;
129 match node {
130 ExprData::Symbol(s) if s == builtins.pi => Some(BigRational::new(BigInt::from(1), BigInt::from(1))),
131 ExprData::Integer(_) | ExprData::Rational(_) | ExprData::Real(_) => {
132 let n = pool.const_number(expr)?;
133 if n.is_zero() {
134 Some(BigRational::new(BigInt::from(0), BigInt::from(1)))
135 } else {
136 None
137 }
138 }
139 ExprData::Mul(items) => {
140 let mut coeff: Option<BigRational> = None;
141 let mut found_pi = false;
142 for &it in items.iter() {
143 match pool.get(it)? {
144 ExprData::Symbol(s) if s == builtins.pi => found_pi = true,
145 ExprData::Integer(_) | ExprData::Rational(_) => {
146 let c = match pool.const_number(it)? {
147 Number::Integer(i) => BigRational::from_integer(i),
148 Number::Rational(r) => r,
149 _ => return None,
150 };
151 coeff = Some(match coeff {
152 Some(acc) => acc * c,
153 None => c,
154 });
155 }
156 _ => return None,
157 }
158 }
159 if !found_pi {
160 return None;
161 }
162 Some(coeff.unwrap_or_else(|| BigRational::new(BigInt::from(1), BigInt::from(1))))
163 }
164 _ => None,
165 }
166}
167
168fn exact_trig(c: &BigRational) -> Option<(Number, Number)> {
169 let two = BigRational::new(BigInt::from(2), BigInt::from(1));
170 let mut c = c % two.clone();
171 if c < BigRational::new(BigInt::from(0), BigInt::from(1)) {
172 c += two;
173 }
174 let zero = BigRational::new(BigInt::from(0), BigInt::from(1));
175 let half = BigRational::new(BigInt::from(1), BigInt::from(2));
176 let one = BigRational::new(BigInt::from(1), BigInt::from(1));
177 let three_halves = BigRational::new(BigInt::from(3), BigInt::from(2));
178 if c == zero {
179 Some((Number::from(1), Number::from(0)))
180 } else if c == half {
181 Some((Number::from(0), Number::from(1)))
182 } else if c == one {
183 Some((Number::from(-1), Number::from(0)))
184 } else if c == three_halves {
185 Some((Number::from(0), Number::from(-1)))
186 } else {
187 None
188 }
189}
190
191fn euler(pool: &ExprPool, builtins: &BuiltinSymbols, z: ExprId) -> Option<ExprId> {
193 let i = pool.symbol(builtins.i);
194 let theta = match pool.get(z)? {
195 ExprData::Symbol(s) if s == builtins.i => return None,
196 ExprData::Mul(items) => {
197 let mut theta_items = Vec::new();
198 let mut has_i = false;
199 for &it in items.iter() {
200 if it == i {
201 has_i = true;
202 } else {
203 theta_items.push(it);
204 }
205 }
206 if !has_i || theta_items.is_empty() {
207 return None;
208 }
209 let mut acc = theta_items[0];
210 for &it in &theta_items[1..] {
211 acc = pool.mul2(acc, it);
212 }
213 acc
214 }
215 _ => return None,
216 };
217 let (c, s) = trig_of_angle(pool, builtins, theta)?;
218 if s == Number::from(0) {
219 Some(pool.number(&c))
220 } else if c == Number::from(0) && s == Number::from(1) {
221 Some(i)
222 } else if c == Number::from(0) && s == Number::from(-1) {
223 Some(pool.mul2(pool.integer(-1), i))
224 } else {
225 None
226 }
227}