use std::{f64, fmt::Display};
#[derive(Debug, Clone, PartialEq)]
pub enum Constant {
E,
Infinity,
NaN,
Pi,
}
impl Display for Constant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Constant::E => write!(f, "e"),
Constant::Pi => write!(f, "π"),
Constant::Infinity => write!(f, "∞"),
Constant::NaN => write!(f, "NaN"),
}
}
}
impl From<&Constant> for f64 {
fn from(value: &Constant) -> Self {
match value {
Constant::E => f64::consts::E,
Constant::Infinity => f64::INFINITY,
Constant::NaN => f64::NAN,
Constant::Pi => f64::consts::PI,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
Constant(Constant),
Number(f64),
Add {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Subtract {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Multiply {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Divide {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Modulo {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Negate(Box<Expression>),
Sine(Box<Expression>),
Cosine(Box<Expression>),
Tangent(Box<Expression>),
Exponentiate {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
Factorial(Box<Expression>),
Degrees(Box<Expression>),
Radians(Box<Expression>),
Round {
value: Box<Expression>,
decimals: Box<Expression>,
},
SquareRoot(Box<Expression>),
}
impl Expression {
pub fn evaluate(&self) -> f64 {
let evaluate = match self {
Expression::Constant(constant) => constant.into(),
Expression::Number(number) => *number,
Expression::Add { lhs, rhs } => lhs.evaluate() + rhs.evaluate(),
Expression::Subtract { lhs, rhs } => lhs.evaluate() - rhs.evaluate(),
Expression::Multiply { lhs, rhs } => lhs.evaluate() * rhs.evaluate(),
Expression::Divide { lhs, rhs } => lhs.evaluate() / rhs.evaluate(),
Expression::Modulo { lhs, rhs } => {
let l = lhs.evaluate();
let r = rhs.evaluate();
((l % r) + r) % r
}
Expression::Negate(expression) => -expression.evaluate(),
Expression::Sine(expression) => expression.evaluate().sin(),
Expression::Cosine(expression) => expression.evaluate().cos(),
Expression::Tangent(expression) => expression.evaluate().tan(),
Expression::Exponentiate { lhs, rhs } => lhs.evaluate().powf(rhs.evaluate()),
Expression::Factorial(expression) => match expression.evaluate() {
n if n == f64::INFINITY => n,
n if n < 0.0 || n.fract() != 0.0 => f64::NAN,
n => (1..=n.trunc() as i64).fold(1.0, |a, b| a * b as f64),
},
Expression::Degrees(expression) => expression.evaluate().to_degrees(),
Expression::Radians(expression) => expression.evaluate().to_radians(),
Expression::Round { value, decimals } => {
let n = value.evaluate();
let d = decimals.evaluate();
if d < 0.0 || d.fract() != 0.0 {
return f64::NAN;
};
let scale = 10_f64.powf(d);
(scale * n).round() / scale
}
Expression::SquareRoot(expression) => expression.evaluate().sqrt(),
};
println!("evaluate:{}", evaluate);
evaluate
}
}
impl From<Constant> for Expression {
fn from(value: Constant) -> Self {
Expression::Constant(value)
}
}
impl From<f64> for Expression {
fn from(value: f64) -> Self {
Expression::Number(value)
}
}
impl From<f64> for Box<Expression> {
fn from(n: f64) -> Self {
Box::new(Expression::Number(n))
}
}
impl From<&Expression> for f64 {
fn from(expr: &Expression) -> Self {
expr.evaluate()
}
}
impl Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expression::Number(n) => write!(f, "{}", n),
Expression::Constant(c) => write!(f, "{}", c),
Expression::Add { lhs, rhs } => write!(f, "({} + {})", lhs, rhs),
Expression::Subtract { lhs, rhs } => write!(f, "({} - {})", lhs, rhs),
Expression::Multiply { lhs, rhs } => write!(f, "({} * {})", lhs, rhs),
Expression::Divide { lhs, rhs } => write!(f, "({} / {})", lhs, rhs),
Expression::Modulo { lhs, rhs } => write!(f, "({} % {})", lhs, rhs),
Expression::Negate(expr) => write!(f, "-{}", expr),
Expression::Exponentiate { lhs, rhs } => write!(f, "({} ^ {})", lhs, rhs),
Expression::Factorial(expr) => write!(f, "{}!", expr),
Expression::SquareRoot(expr) => write!(f, "√{}", expr),
Expression::Sine(expr) => write!(f, "sin({})", expr),
Expression::Cosine(expr) => write!(f, "cos({})", expr),
Expression::Tangent(expr) => write!(f, "tan({})", expr),
Expression::Degrees(expr) => write!(f, "degrees({})", expr),
Expression::Radians(expr) => write!(f, "radians({})", expr),
Expression::Round { value, decimals } => {
write!(f, "round({}, {})", value, decimals)
}
}
}
}
#[cfg(test)]
mod tests {
use std::f64;
use super::*;
fn box_expression(expression: Expression) -> Box<Expression> {
Box::new(expression)
}
#[test]
fn test_from() {
let e: Expression = Constant::E.into();
assert_eq!(e, Expression::Constant(Constant::E));
let n: Expression = 6.0.into();
assert_eq!(n, Expression::Number(6.0));
let bn: Box<Expression> = 6.0.into();
assert_eq!(bn, Box::new(Expression::Number(6.0)));
let f: f64= (&Expression::Number(6.0)).into();
assert_eq!(f, 6.0);
}
#[test]
fn test_constants() {
assert_eq!(
f64::from(&Expression::Constant(Constant::E)),
f64::consts::E
);
assert!(f64::from(&Expression::Constant(Constant::Infinity)).is_infinite(),);
assert!(f64::from(&Expression::Constant(Constant::NaN)).is_nan());
assert_eq!(
f64::from(&Expression::Constant(Constant::Pi)),
f64::consts::PI
);
}
#[test]
fn test_basic_arithmetic() {
let add = Expression::Add {
lhs: box_expression(Expression::Number(2.0)),
rhs: box_expression(Expression::Number(1.0)),
};
assert_eq!(add.evaluate(), 3.0);
let sub = Expression::Subtract {
lhs: box_expression(Expression::Number(2.0)),
rhs: box_expression(Expression::Number(1.0)),
};
assert_eq!(sub.evaluate(), 1.0);
let mul = Expression::Multiply {
lhs: box_expression(Expression::Number(2.0)),
rhs: box_expression(Expression::Number(1.0)),
};
assert_eq!(mul.evaluate(), 2.0);
let div = Expression::Divide {
lhs: box_expression(Expression::Number(2.0)),
rhs: box_expression(Expression::Number(1.0)),
};
assert_eq!(div.evaluate(), 2.0);
let modu = Expression::Modulo {
lhs: box_expression(Expression::Number(3.0)),
rhs: box_expression(Expression::Number(2.0)),
};
assert_eq!(modu.evaluate(), 1.0);
let neg = Expression::Negate(box_expression(Expression::Number(2.0)));
assert_eq!(neg.evaluate(), -2.0);
}
#[test]
fn test_exponentiate() {
let pow = Expression::Exponentiate {
lhs: box_expression(Expression::Number(3.0)),
rhs: box_expression(Expression::Number(2.0)),
};
assert_eq!(pow.evaluate(), 9.0);
}
#[test]
fn test_factorial() {
let fac = Expression::Factorial(box_expression(Expression::Number(5.0)));
assert_eq!(fac.evaluate(), 120.0);
}
#[test]
fn test_trigonometry() {
let sin = Expression::Sine(box_expression(Expression::Number(f64::consts::PI / 6.0)));
assert!((sin.evaluate() - 0.5).abs() < 1e-10); let cos = Expression::Cosine(box_expression(Expression::Number(f64::consts::PI / 3.0)));
assert!((cos.evaluate() - 0.5).abs() < 1e-10);
let tan = Expression::Tangent(box_expression(Expression::Number(f64::consts::PI / 4.0)));
assert!((tan.evaluate() - 1.0).abs() < 1e-10);
}
#[test]
fn test_degrees_radians() {
let rad_to_deg = Expression::Degrees(box_expression(Expression::Constant(Constant::Pi)));
assert_eq!(rad_to_deg.evaluate(), 180.0);
let deg_to_rad = Expression::Radians(box_expression(Expression::Number(180.0)));
assert!((deg_to_rad.evaluate() - f64::consts::PI).abs() < 1e-10);
}
#[test]
fn test_square_root() {
let sqrt_16 = Expression::SquareRoot(box_expression(Expression::Number(16.0)));
assert_eq!(sqrt_16.evaluate(), 4.0);
let sqrt_neg = Expression::SquareRoot(box_expression(Expression::Number(-4.0)));
assert!(sqrt_neg.evaluate().is_nan());
}
#[test]
fn test_round() {
let round2 = Expression::Round {
value: box_expression(Expression::Number(3.1415)),
decimals: box_expression(Expression::Number(2.0)),
};
assert_eq!(round2.evaluate(), 3.14);
}
#[test]
fn test_complex_expression() {
let complex = Expression::Multiply {
lhs: box_expression(Expression::Add {
lhs: box_expression(Expression::Number(2.0)),
rhs: box_expression(Expression::Number(3.0)),
}),
rhs: box_expression(Expression::Divide {
lhs: box_expression(Expression::Constant(Constant::Pi)),
rhs: box_expression(Expression::Number(2.0)),
}),
};
assert!((complex.evaluate() - (5.0 * f64::consts::PI / 2.0)).abs() < 1e-10);
}
}