use crate::error::{EvaluationError, Result};
pub trait EvaluationDomain: Sized + Clone + 'static {
fn from_f64(value: f64) -> Self;
fn zero() -> Self;
fn one() -> Self;
fn add_ref(&self, other: &Self) -> Self;
fn sub_ref(&self, other: &Self) -> Self;
fn mul_ref(&self, other: &Self) -> Self;
fn div_ref(&self, other: &Self) -> Result<Self>;
fn neg_ref(&self) -> Self;
fn powi_ref(&self, exp: i64) -> Self;
fn resolve_builtin(name: &str, arg: &Self) -> Result<Self>;
}
impl EvaluationDomain for f64 {
#[inline]
fn from_f64(value: f64) -> Self {
value
}
#[inline]
fn zero() -> Self {
0.0
}
#[inline]
fn one() -> Self {
1.0
}
#[inline]
fn add_ref(&self, other: &Self) -> Self {
self + other
}
#[inline]
fn sub_ref(&self, other: &Self) -> Self {
self - other
}
#[inline]
fn mul_ref(&self, other: &Self) -> Self {
self * other
}
#[inline]
fn div_ref(&self, other: &Self) -> Result<Self> {
if *other == 0.0 {
Err(EvaluationError::DivisionByZero)
} else {
Ok(self / other)
}
}
#[inline]
fn neg_ref(&self) -> Self {
-self
}
#[inline]
fn powi_ref(&self, exp: i64) -> Self {
self.powi(exp as i32)
}
fn resolve_builtin(name: &str, arg: &Self) -> Result<Self> {
match name.to_lowercase().as_str() {
"sin" => Ok(arg.sin()),
"cos" => Ok(arg.cos()),
"tan" => Ok(arg.tan()),
"sec" => Ok(1.0 / arg.cos()),
"csc" => Ok(1.0 / arg.sin()),
"cot" => Ok(1.0 / arg.tan()),
"exp" => Ok(arg.exp()),
"log" => {
if *arg <= 0.0 {
Err(EvaluationError::UnsupportedOperation {
message: "log of non-positive number".into(),
})
} else {
Ok(arg.ln())
}
}
"sqrt" => {
if *arg < 0.0 {
Err(EvaluationError::UnsupportedOperation {
message: "sqrt of negative number".into(),
})
} else {
Ok(arg.sqrt())
}
}
"abs" => Ok(arg.abs()),
_ => Err(EvaluationError::FunctionNotFound {
name: name.to_string(),
}),
}
}
}
pub trait PowfExtension: EvaluationDomain {
fn powf_ref(&self, exp: &Self) -> Result<Self>;
}
impl PowfExtension for f64 {
fn powf_ref(&self, exp: &Self) -> Result<Self> {
Ok(self.powf(*exp))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f64_arithmetic() {
assert_eq!(f64::zero(), 0.0);
assert_eq!(f64::one(), 1.0);
assert_eq!(3.0f64.add_ref(&2.0), 5.0);
assert_eq!(3.0f64.sub_ref(&2.0), 1.0);
assert_eq!(3.0f64.mul_ref(&2.0), 6.0);
assert_eq!(6.0f64.div_ref(&2.0).unwrap(), 3.0);
assert!(6.0f64.div_ref(&0.0).is_err());
assert_eq!(3.0f64.neg_ref(), -3.0);
assert_eq!(2.0f64.powi_ref(3), 8.0);
assert_eq!(2.0f64.powi_ref(0), 1.0);
}
#[test]
fn f64_builtin_sin_lowercase() {
let result = f64::resolve_builtin("sin", &std::f64::consts::FRAC_PI_2).unwrap();
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_sin_capitalized() {
let result = f64::resolve_builtin("Sin", &std::f64::consts::FRAC_PI_2).unwrap();
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_cos() {
let result = f64::resolve_builtin("cos", &std::f64::consts::PI).unwrap();
assert!((result + 1.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_exp() {
let result = f64::resolve_builtin("exp", &1.0).unwrap();
assert!((result - std::f64::consts::E).abs() < 1e-10);
}
#[test]
fn f64_builtin_log() {
let result = f64::resolve_builtin("log", &std::f64::consts::E).unwrap();
assert!((result - 1.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_log_negative() {
assert!(f64::resolve_builtin("log", &(-1.0)).is_err());
}
#[test]
fn f64_builtin_sqrt() {
let result = f64::resolve_builtin("sqrt", &4.0).unwrap();
assert!((result - 2.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_sqrt_negative() {
assert!(f64::resolve_builtin("sqrt", &(-1.0)).is_err());
}
#[test]
fn f64_builtin_abs() {
assert_eq!(f64::resolve_builtin("abs", &(-3.0)).unwrap(), 3.0);
assert_eq!(f64::resolve_builtin("abs", &3.0).unwrap(), 3.0);
}
#[test]
fn f64_builtin_tan() {
let result = f64::resolve_builtin("tan", &0.0).unwrap();
assert!((result - 0.0).abs() < 1e-10);
}
#[test]
fn f64_builtin_unknown() {
assert!(f64::resolve_builtin("unknown_fn", &0.0).is_err());
}
#[test]
fn f64_from_f64() {
assert_eq!(f64::from_f64(42.0), 42.0);
}
}