use core::ops::{Add, Sub, Mul, Div, Neg};
use core::fmt::{self, Display};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct StackRef(pub u16);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ConstantId {
Pi,
E,
Sqrt2,
Phi, EulerGamma, }
#[derive(Debug, Clone)]
pub enum LazyExpr {
Literal(&'static str),
Value(Box<super::stack_evaluator::StackValue>),
Constant(ConstantId),
Variable(StackRef),
Negate(Box<LazyExpr>),
Add(Box<LazyExpr>, Box<LazyExpr>),
Sub(Box<LazyExpr>, Box<LazyExpr>),
Mul(Box<LazyExpr>, Box<LazyExpr>),
Div(Box<LazyExpr>, Box<LazyExpr>),
Exp(Box<LazyExpr>),
Ln(Box<LazyExpr>),
Sqrt(Box<LazyExpr>),
Pow(Box<LazyExpr>, Box<LazyExpr>),
Sinh(Box<LazyExpr>),
Cosh(Box<LazyExpr>),
Tanh(Box<LazyExpr>),
Asinh(Box<LazyExpr>),
Acosh(Box<LazyExpr>),
Atanh(Box<LazyExpr>),
Sin(Box<LazyExpr>),
Cos(Box<LazyExpr>),
Tan(Box<LazyExpr>),
Asin(Box<LazyExpr>),
Acos(Box<LazyExpr>),
Atan(Box<LazyExpr>),
Atan2(Box<LazyExpr>, Box<LazyExpr>),
}
impl LazyExpr {
pub fn is_constant(&self) -> bool {
match self {
LazyExpr::Literal(_) | LazyExpr::Constant(_) | LazyExpr::Value(_) => true,
LazyExpr::Variable(_) => false,
LazyExpr::Negate(inner) | LazyExpr::Exp(inner) | LazyExpr::Ln(inner) | LazyExpr::Sqrt(inner)
| LazyExpr::Sinh(inner) | LazyExpr::Cosh(inner) | LazyExpr::Tanh(inner)
| LazyExpr::Asinh(inner) | LazyExpr::Acosh(inner) | LazyExpr::Atanh(inner)
| LazyExpr::Sin(inner) | LazyExpr::Cos(inner) | LazyExpr::Tan(inner)
| LazyExpr::Asin(inner) | LazyExpr::Acos(inner) | LazyExpr::Atan(inner) => inner.is_constant(),
LazyExpr::Add(l, r) | LazyExpr::Sub(l, r) |
LazyExpr::Mul(l, r) | LazyExpr::Div(l, r) |
LazyExpr::Pow(l, r) | LazyExpr::Atan2(l, r) => {
l.is_constant() && r.is_constant()
}
}
}
pub fn depth(&self) -> usize {
match self {
LazyExpr::Literal(_) | LazyExpr::Constant(_) | LazyExpr::Variable(_) | LazyExpr::Value(_) => 1,
LazyExpr::Negate(inner) | LazyExpr::Exp(inner) | LazyExpr::Ln(inner) | LazyExpr::Sqrt(inner)
| LazyExpr::Sinh(inner) | LazyExpr::Cosh(inner) | LazyExpr::Tanh(inner)
| LazyExpr::Asinh(inner) | LazyExpr::Acosh(inner) | LazyExpr::Atanh(inner)
| LazyExpr::Sin(inner) | LazyExpr::Cos(inner) | LazyExpr::Tan(inner)
| LazyExpr::Asin(inner) | LazyExpr::Acos(inner) | LazyExpr::Atan(inner) => 1 + inner.depth(),
LazyExpr::Add(l, r) | LazyExpr::Sub(l, r) |
LazyExpr::Mul(l, r) | LazyExpr::Div(l, r) |
LazyExpr::Pow(l, r) | LazyExpr::Atan2(l, r) => {
1 + l.depth().max(r.depth())
}
}
}
pub fn operation_count(&self) -> usize {
match self {
LazyExpr::Literal(_) | LazyExpr::Constant(_) | LazyExpr::Variable(_) | LazyExpr::Value(_) => 0,
LazyExpr::Negate(inner) | LazyExpr::Exp(inner) | LazyExpr::Ln(inner) | LazyExpr::Sqrt(inner)
| LazyExpr::Sinh(inner) | LazyExpr::Cosh(inner) | LazyExpr::Tanh(inner)
| LazyExpr::Asinh(inner) | LazyExpr::Acosh(inner) | LazyExpr::Atanh(inner)
| LazyExpr::Sin(inner) | LazyExpr::Cos(inner) | LazyExpr::Tan(inner)
| LazyExpr::Asin(inner) | LazyExpr::Acos(inner) | LazyExpr::Atan(inner) => 1 + inner.operation_count(),
LazyExpr::Add(l, r) | LazyExpr::Sub(l, r) |
LazyExpr::Mul(l, r) | LazyExpr::Div(l, r) |
LazyExpr::Pow(l, r) | LazyExpr::Atan2(l, r) => {
1 + l.operation_count() + r.operation_count()
}
}
}
pub fn pow(self, exponent: LazyExpr) -> LazyExpr {
LazyExpr::Pow(Box::new(self), Box::new(exponent))
}
}
impl From<super::stack_evaluator::StackValue> for LazyExpr {
fn from(val: super::stack_evaluator::StackValue) -> Self {
LazyExpr::Value(Box::new(val))
}
}
pub const fn gmath(input: &'static str) -> LazyExpr {
LazyExpr::Literal(input)
}
impl Add for LazyExpr {
type Output = LazyExpr;
fn add(self, other: Self) -> Self::Output {
LazyExpr::Add(Box::new(self), Box::new(other))
}
}
impl Sub for LazyExpr {
type Output = LazyExpr;
fn sub(self, other: Self) -> Self::Output {
LazyExpr::Sub(Box::new(self), Box::new(other))
}
}
impl Mul for LazyExpr {
type Output = LazyExpr;
fn mul(self, other: Self) -> Self::Output {
LazyExpr::Mul(Box::new(self), Box::new(other))
}
}
impl Div for LazyExpr {
type Output = LazyExpr;
fn div(self, other: Self) -> Self::Output {
LazyExpr::Div(Box::new(self), Box::new(other))
}
}
impl Neg for LazyExpr {
type Output = LazyExpr;
fn neg(self) -> Self::Output {
LazyExpr::Negate(Box::new(self))
}
}
impl LazyExpr {
pub fn exp(self) -> Self {
LazyExpr::Exp(Box::new(self))
}
pub fn ln(self) -> Self {
LazyExpr::Ln(Box::new(self))
}
pub fn sqrt(self) -> Self {
LazyExpr::Sqrt(Box::new(self))
}
pub fn sinh(self) -> Self {
LazyExpr::Sinh(Box::new(self))
}
pub fn cosh(self) -> Self {
LazyExpr::Cosh(Box::new(self))
}
pub fn tanh(self) -> Self {
LazyExpr::Tanh(Box::new(self))
}
pub fn asinh(self) -> Self {
LazyExpr::Asinh(Box::new(self))
}
pub fn acosh(self) -> Self {
LazyExpr::Acosh(Box::new(self))
}
pub fn atanh(self) -> Self {
LazyExpr::Atanh(Box::new(self))
}
pub fn sin(self) -> Self {
LazyExpr::Sin(Box::new(self))
}
pub fn cos(self) -> Self {
LazyExpr::Cos(Box::new(self))
}
pub fn tan(self) -> Self {
LazyExpr::Tan(Box::new(self))
}
pub fn asin(self) -> Self {
LazyExpr::Asin(Box::new(self))
}
pub fn acos(self) -> Self {
LazyExpr::Acos(Box::new(self))
}
pub fn atan(self) -> Self {
LazyExpr::Atan(Box::new(self))
}
pub fn atan2(self, x: LazyExpr) -> LazyExpr {
LazyExpr::Atan2(Box::new(self), Box::new(x))
}
}
impl Display for LazyExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LazyExpr::Literal(s) => write!(f, "{}", s),
LazyExpr::Value(v) => write!(f, "{}", v),
LazyExpr::Constant(c) => write!(f, "{:?}", c),
LazyExpr::Variable(r) => write!(f, "var[{}]", r.0),
LazyExpr::Negate(inner) => write!(f, "-({})", inner),
LazyExpr::Add(l, r) => write!(f, "({} + {})", l, r),
LazyExpr::Sub(l, r) => write!(f, "({} - {})", l, r),
LazyExpr::Mul(l, r) => write!(f, "({} * {})", l, r),
LazyExpr::Div(l, r) => write!(f, "({} / {})", l, r),
LazyExpr::Exp(inner) => write!(f, "exp({})", inner),
LazyExpr::Ln(inner) => write!(f, "ln({})", inner),
LazyExpr::Sqrt(inner) => write!(f, "sqrt({})", inner),
LazyExpr::Pow(base, exp) => write!(f, "pow({}, {})", base, exp),
LazyExpr::Sinh(inner) => write!(f, "sinh({})", inner),
LazyExpr::Cosh(inner) => write!(f, "cosh({})", inner),
LazyExpr::Tanh(inner) => write!(f, "tanh({})", inner),
LazyExpr::Asinh(inner) => write!(f, "asinh({})", inner),
LazyExpr::Acosh(inner) => write!(f, "acosh({})", inner),
LazyExpr::Atanh(inner) => write!(f, "atanh({})", inner),
LazyExpr::Sin(inner) => write!(f, "sin({})", inner),
LazyExpr::Cos(inner) => write!(f, "cos({})", inner),
LazyExpr::Tan(inner) => write!(f, "tan({})", inner),
LazyExpr::Asin(inner) => write!(f, "asin({})", inner),
LazyExpr::Acos(inner) => write!(f, "acos({})", inner),
LazyExpr::Atan(inner) => write!(f, "atan({})", inner),
LazyExpr::Atan2(y, x) => write!(f, "atan2({}, {})", y, x),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_expression_building() {
let expr = gmath("10") + gmath("20") * gmath("30");
assert_eq!(expr.depth(), 3);
assert_eq!(expr.operation_count(), 2);
}
#[test]
fn test_constant_detection() {
let const_expr = gmath("10") + gmath("20");
assert!(const_expr.is_constant());
let var_expr = LazyExpr::Variable(StackRef(0)) + gmath("10");
assert!(!var_expr.is_constant());
}
}