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(items.iter().copied(), Number::from(0), |a, b| a + b, pool, builtins),
29 ExprData::Mul(items) => fold(items.iter().copied(), Number::from(1), |a, b| a * b, pool, builtins),
30 ExprData::Pow { base, exp } => {
31 let b = numeric_value(pool, builtins, base)?;
32 let e = numeric_value(pool, builtins, exp)?;
33 match b.pow(&e) {
34 Some(r) => Some(r),
35 None => Some(Number::Real(Real::F64(b.to_f64_lossy().powf(e.to_f64_lossy())))),
36 }
37 }
38 ExprData::Apply { f, args } => apply_value(pool, builtins, f, &args),
39 ExprData::Indeterminate(_) => None,
40 }
41}
42
43fn fold(
44 items: impl Iterator<Item = ExprId>,
45 init: Number,
46 f: impl Fn(Number, Number) -> Number,
47 pool: &ExprPool,
48 builtins: &BuiltinSymbols,
49) -> Option<Number> {
50 let mut acc = init;
51 for it in items {
52 acc = f(acc, numeric_value(pool, builtins, it)?);
53 }
54 Some(acc)
55}
56
57fn symbol_value(builtins: &BuiltinSymbols, s: crate::symbol::SymbolId) -> Option<Number> {
58 if s == builtins.e {
59 Some(Number::Real(Real::F64(std::f64::consts::E)))
60 } else if s == builtins.pi {
61 Some(Number::Real(Real::F64(std::f64::consts::PI)))
62 } else {
63 None
64 }
65}
66
67fn apply_value(pool: &ExprPool, builtins: &BuiltinSymbols, f: ExprId, args: &[ExprId]) -> Option<Number> {
68 if args.len() != 1 {
69 return None;
70 }
71 let arg = numeric_value(pool, builtins, args[0])?;
72 if f == pool.symbol(builtins.sqrt) {
73 return match arg.sqrt() {
74 Some(r) => Some(r),
75 None => Some(Number::Real(Real::F64(arg.to_f64_lossy().sqrt()))),
76 };
77 }
78 if f == pool.symbol(builtins.abs) {
79 return Some(arg.abs());
80 }
81 let x = arg.to_f64_lossy();
82 let v = if f == pool.symbol(builtins.exp) {
83 Some(x.exp())
84 } else if f == pool.symbol(builtins.log) || f == pool.symbol(builtins.ln) {
85 Some(x.ln())
86 } else if f == pool.symbol(builtins.sin) {
87 Some(x.sin())
88 } else if f == pool.symbol(builtins.cos) {
89 Some(x.cos())
90 } else if f == pool.symbol(builtins.tan) {
91 Some(x.tan())
92 } else {
93 None
94 }?;
95 Some(Number::Real(Real::F64(v)))
96}