use std::f64::consts;
use num_complex::Complex64;
use num_traits::ToPrimitive;
use crate::symbolic::core::Expr;
const F64_EPSILON: f64 = 1e-9;
#[must_use]
pub fn evaluate_numerical(expr: &Expr) -> Option<f64> {
let complex_val = evaluate_complex(expr)?;
if complex_val.im.abs() < F64_EPSILON {
Some(complex_val.re)
} else {
None
}
}
#[must_use]
#[allow(clippy::suboptimal_flops)]
pub fn evaluate_complex(expr: &Expr) -> Option<Complex64> {
match expr {
| Expr::Dag(node) => evaluate_complex(&node.to_expr().expect("Eva Complex")),
| Expr::Constant(c) => Some(Complex64::new(*c, 0.0)),
| Expr::BigInt(i) => Some(Complex64::new(i.to_f64()?, 0.0)),
| Expr::Rational(r) => Some(Complex64::new(r.to_f64()?, 0.0)),
| Expr::Pi => Some(Complex64::new(consts::PI, 0.0)),
| Expr::E => Some(Complex64::new(consts::E, 0.0)),
| Expr::Add(a, b) => Some(evaluate_complex(a)? + evaluate_complex(b)?),
| Expr::Sub(a, b) => Some(evaluate_complex(a)? - evaluate_complex(b)?),
| Expr::Mul(a, b) => Some(evaluate_complex(a)? * evaluate_complex(b)?),
| Expr::Div(a, b) => Some(evaluate_complex(a)? / evaluate_complex(b)?),
| Expr::Power(b, e) => {
let base = evaluate_complex(b)?;
let exp = evaluate_complex(e)?;
if exp.im == 0.0 {
Some(base.powf(exp.re))
} else {
Some(base.powc(exp))
}
},
| Expr::Sqrt(a) => Some(evaluate_complex(a)?.sqrt()),
| Expr::Log(a) => Some(evaluate_complex(a)?.ln()),
| Expr::Exp(a) => Some(evaluate_complex(a)?.exp()),
| Expr::Abs(a) => Some(Complex64::new(evaluate_complex(a)?.norm(), 0.0)),
| Expr::Sin(a) => Some(evaluate_complex(a)?.sin()),
| Expr::Cos(a) => Some(evaluate_complex(a)?.cos()),
| Expr::Tan(a) => Some(evaluate_complex(a)?.tan()),
| Expr::ArcSin(a) => Some(evaluate_complex(a)?.asin()),
| Expr::ArcCos(a) => Some(evaluate_complex(a)?.acos()),
| Expr::ArcTan(a) => Some(evaluate_complex(a)?.atan()),
| Expr::Sinh(a) => Some(evaluate_complex(a)?.sinh()),
| Expr::Cosh(a) => Some(evaluate_complex(a)?.cosh()),
| Expr::Tanh(a) => Some(evaluate_complex(a)?.tanh()),
| Expr::ArcSinh(a) => Some(evaluate_complex(a)?.asinh()),
| Expr::ArcCosh(a) => Some(evaluate_complex(a)?.acosh()),
| Expr::ArcTanh(a) => Some(evaluate_complex(a)?.atanh()),
| Expr::Complex(re, im) => {
Some(Complex64::new(
evaluate_complex(re)?.re,
evaluate_complex(im)?.re,
))
},
| Expr::AddList(list) => {
let mut sum = Complex64::new(0.0, 0.0);
for item in list {
sum += evaluate_complex(item)?;
}
Some(sum)
},
| Expr::MulList(list) => {
let mut prod = Complex64::new(1.0, 0.0);
for item in list {
prod *= evaluate_complex(item)?;
}
Some(prod)
},
| Expr::LogBase(base, arg) => {
let b = evaluate_complex(base)?;
let a = evaluate_complex(arg)?;
Some(a.ln() / b.ln())
},
| Expr::Factorial(a) => {
let val = evaluate_complex(a)?;
if val.im == 0.0 && val.re.fract() == 0.0 && val.re >= 0.0 {
let mut result = 1.0;
for i in 2..=(val.re as i64).try_into().unwrap_or(0) {
result *= f64::from(i);
}
Some(Complex64::new(result, 0.0))
} else {
None
}
},
| Expr::Floor(a) => {
let val = evaluate_complex(a)?;
if val.im == 0.0 {
Some(Complex64::new(val.re.floor(), 0.0))
} else {
None
}
},
| Expr::Neg(a) => Some(-evaluate_complex(a)?),
| _ => None,
}
}