1use crate::builtins::BuiltinSymbols;
9use crate::expr_pool::{ExprData, ExprId, ExprPool};
10use crate::number::{Number, Real};
11use crate::value::Value;
12
13pub fn collapse_value(pool: &ExprPool, builtins: &BuiltinSymbols, v: &Value) -> Option<Number> {
16 match v {
17 Value::Number(n) => Some(n.clone()),
18 Value::Expr(id) => numeric_value(pool, builtins, *id),
19 _ => None,
20 }
21}
22
23pub fn numeric_value(pool: &ExprPool, builtins: &BuiltinSymbols, id: ExprId) -> Option<Number> {
25 match pool.get(id)? {
26 ExprData::Symbol(s) => symbol_value(builtins, s),
27 ExprData::Integer(_) | ExprData::Rational(_) | ExprData::Real(_) => pool.const_number(id),
28 ExprData::Add(items) => fold(
29 items.iter().copied(),
30 Number::from(0),
31 |a, b| a + b,
32 pool,
33 builtins,
34 ),
35 ExprData::Mul(items) => fold(
36 items.iter().copied(),
37 Number::from(1),
38 |a, b| a * b,
39 pool,
40 builtins,
41 ),
42 ExprData::Pow { base, exp } => {
43 let b = numeric_value(pool, builtins, base)?;
44 let e = numeric_value(pool, builtins, exp)?;
45 match b.pow(&e) {
46 Some(r) => Some(r),
47 None => Some(Number::Real(Real::F64(
48 b.to_f64_lossy().powf(e.to_f64_lossy()),
49 ))),
50 }
51 }
52 ExprData::Apply { f, args } => apply_value(pool, builtins, f, &args),
53 ExprData::Indeterminate(_) => None,
54 }
55}
56
57fn fold(
58 items: impl Iterator<Item = ExprId>,
59 init: Number,
60 f: impl Fn(Number, Number) -> Number,
61 pool: &ExprPool,
62 builtins: &BuiltinSymbols,
63) -> Option<Number> {
64 let mut acc = init;
65 for it in items {
66 acc = f(acc, numeric_value(pool, builtins, it)?);
67 }
68 Some(acc)
69}
70
71fn symbol_value(builtins: &BuiltinSymbols, s: crate::symbol::SymbolId) -> Option<Number> {
72 if s == builtins.e {
73 Some(Number::Real(Real::F64(std::f64::consts::E)))
74 } else if s == builtins.pi {
75 Some(Number::Real(Real::F64(std::f64::consts::PI)))
76 } else {
77 None
78 }
79}
80
81fn apply_value(
82 pool: &ExprPool,
83 builtins: &BuiltinSymbols,
84 f: ExprId,
85 args: &[ExprId],
86) -> Option<Number> {
87 if args.len() != 1 {
88 return None;
89 }
90 let arg = numeric_value(pool, builtins, args[0])?;
91 if f == pool.symbol(builtins.sqrt) {
92 return match arg.sqrt() {
93 Some(r) => Some(r),
94 None => Some(Number::Real(Real::F64(arg.to_f64_lossy().sqrt()))),
95 };
96 }
97 if f == pool.symbol(builtins.abs) {
98 return Some(arg.abs());
99 }
100 let x = arg.to_f64_lossy();
101 let v = if f == pool.symbol(builtins.exp) {
102 Some(x.exp())
103 } else if f == pool.symbol(builtins.log) || f == pool.symbol(builtins.ln) {
104 Some(x.ln())
105 } else if f == pool.symbol(builtins.sin) {
106 Some(x.sin())
107 } else if f == pool.symbol(builtins.cos) {
108 Some(x.cos())
109 } else if f == pool.symbol(builtins.tan) {
110 Some(x.tan())
111 } else {
112 None
113 }?;
114 Some(Number::Real(Real::F64(v)))
115}