use std::fmt::Debug;
use crate::term::Term;
use crate::context::Context;
use crate::errors::MathError;
use crate::num::Num;
use crate::answer::Answer;
pub type Calculation<N> = Result<Answer<N>, MathError>;
pub trait Operate<N: Num>: Debug {
fn eval(&self, ctx: &Context<N>) -> Calculation<N>;
fn to_string(&self) -> String;
}
#[derive(Debug, Clone)]
pub(crate) struct Add<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Add<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
a.op(&b, |a, b| {
a.add(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({} + {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Sub<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Sub<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
a.op(&b, |a, b| {
a.sub(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({} - {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Mul<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Mul<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
a.op(&b, |a, b| {
a.mul(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({} × {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Div<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Div<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
a.op(&b, |a, b| {
a.div(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({} ÷ {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Pow<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Pow<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
a.op(&b, |a, b| {
a.pow(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({} ^ {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct PlusMinus<N: Num> {
pub a: Term<N>,
pub b: Term<N>,
}
impl<N: Num + 'static> Operate<N> for PlusMinus<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
let b = self.b.eval_ctx(ctx)?;
let adds = a.op(&b, |a, b| {
a.add(b, ctx)
})?;
let subs = a.op(&b, |a, b| {
a.sub(b, ctx)
})?;
Ok(adds.join(subs))
}
fn to_string(&self) -> String {
format!("({} ± {})", self.a, self.b)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Neg<N: Num> {
pub a: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Neg<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
a.op(&N::from_f64(-1.0, ctx)?, |a, b| {
a.mul(b, ctx)
})
}
fn to_string(&self) -> String {
format!("(-{})", self.a)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Pos<N: Num> {
pub a: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Pos<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
Ok(a)
}
fn to_string(&self) -> String {
format!("(+{})", self.a)
}
}
#[derive(Debug, Clone)]
pub(crate) struct PosNeg<N: Num> {
pub a: Term<N>,
}
impl<N: Num + 'static> Operate<N> for PosNeg<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
a.unop(|a| {
let pos = a;
let neg = a.mul(&N::from_f64(-1.0, ctx)?.unwrap_single(), ctx)?;
Ok(neg.join(Answer::Single(pos.clone())))
})
}
fn to_string(&self) -> String {
format!("(±{})", self.a)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Fact<N: Num> {
pub a: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Fact<N> {
fn eval(&self, _ctx: &Context<N>) -> Calculation<N> {
Err(MathError::Unimplemented {
op: "Factorial".to_string(),
num_type: "Any".to_string(),
})
}
fn to_string(&self) -> String {
format!("({}!)", self.a)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Percent<N: Num> {
pub a: Term<N>,
}
impl<N: Num + 'static> Operate<N> for Percent<N> {
fn eval(&self, ctx: &Context<N>) -> Calculation<N> {
let a = self.a.eval_ctx(ctx)?;
a.op(&N::from_f64(0.01, ctx)?, |a, b| {
a.mul(b, ctx)
})
}
fn to_string(&self) -> String {
format!("({}%)", self.a)
}
}