1use crate::arena::{ExprArena, ExprId, ExprNode};
2
3pub fn simplify(arena: &mut ExprArena, id: ExprId) -> ExprId {
12 let folded = match arena.get(id).clone() {
13 ExprNode::Unary(op, inner) => match arena.get(inner) {
14 ExprNode::Const(c) => Some(ExprNode::Const(op.apply(*c))),
15 _ => None,
16 },
17 ExprNode::Pow(base, exp) => match (arena.get(base), arena.get(exp)) {
18 (ExprNode::Const(b), ExprNode::Const(e)) => Some(ExprNode::Const(b.powf(*e))),
19 _ => None,
20 },
21 ExprNode::Div(num, den) => match (arena.get(num), arena.get(den)) {
22 (ExprNode::Const(n), ExprNode::Const(d)) => Some(ExprNode::Const(n / d)),
23 _ => None,
24 },
25 ExprNode::Atan2(y, x) => match (arena.get(y), arena.get(x)) {
26 (ExprNode::Const(y), ExprNode::Const(x)) => Some(ExprNode::Const(y.atan2(*x))),
27 _ => None,
28 },
29 ExprNode::Min(children) | ExprNode::Max(children) => {
30 let is_min = matches!(arena.get(id), ExprNode::Min(_));
31 let mut values = children.iter().map(|child| match arena.get(*child) {
32 ExprNode::Const(value) => Some(*value),
33 _ => None,
34 });
35 values.next().flatten().and_then(|first| {
36 values
37 .try_fold(first, |acc, value| {
38 value.map(|v| if is_min { acc.min(v) } else { acc.max(v) })
39 })
40 .map(ExprNode::Const)
41 })
42 }
43 _ => None,
44 };
45 match folded {
46 Some(node) => arena.push(node),
47 None => id,
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::arena::{ExprArena, ExprNode, UnaryOp};
55
56 #[test]
57 fn folds_abs_of_const() {
58 let mut a = ExprArena::new();
59 let c = a.push(ExprNode::Const(-5.0));
60 let abs = a.push(ExprNode::Unary(UnaryOp::Abs, c));
61 let folded = simplify(&mut a, abs);
62 assert!(matches!(a.get(folded), ExprNode::Const(v) if (*v - 5.0).abs() < f64::EPSILON));
63 }
64}