1use std::fmt;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct MissingVarError {
29 pub var_name: String,
30}
31
32impl fmt::Display for MissingVarError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(f, "Missing variable '{}'", self.var_name)
35 }
36}
37
38impl std::error::Error for MissingVarError {}
39
40#[derive(Debug, Clone, PartialEq)]
41pub enum Exp {
42 Val(f64),
43 Var(String),
44 Add(Box<Exp>, Box<Exp>),
45 Sub(Box<Exp>, Box<Exp>),
46 Mul(Box<Exp>, Box<Exp>),
47 Div(Box<Exp>, Box<Exp>),
48 Power(Box<Exp>, f64),
49 Neg(Box<Exp>),
50 Sin(Box<Exp>),
51 Cos(Box<Exp>),
52 Ln(Box<Exp>),
53 Exp(Box<Exp>),
54}
55
56#[allow(clippy::self_named_constructors, clippy::should_implement_trait)]
57impl Exp {
58 pub fn var(name: impl Into<String>) -> Self {
59 Exp::Var(name.into())
60 }
61
62 pub fn val(v: f64) -> Self {
63 Exp::Val(v)
64 }
65
66 pub fn add(lhs: Exp, rhs: Exp) -> Self {
67 Exp::Add(Box::new(lhs), Box::new(rhs))
68 }
69
70 pub fn sub(lhs: Exp, rhs: Exp) -> Self {
71 Exp::Sub(Box::new(lhs), Box::new(rhs))
72 }
73
74 pub fn mul(lhs: Exp, rhs: Exp) -> Self {
75 Exp::Mul(Box::new(lhs), Box::new(rhs))
76 }
77
78 pub fn div(lhs: Exp, rhs: Exp) -> Self {
79 Exp::Div(Box::new(lhs), Box::new(rhs))
80 }
81
82 pub fn power(base: Exp, exp: f64) -> Self {
83 Exp::Power(Box::new(base), exp)
84 }
85
86 pub fn neg(exp: Exp) -> Self {
87 Exp::Neg(Box::new(exp))
88 }
89
90 pub fn sin(exp: Exp) -> Self {
91 Exp::Sin(Box::new(exp))
92 }
93
94 pub fn cos(exp: Exp) -> Self {
95 Exp::Cos(Box::new(exp))
96 }
97
98 pub fn ln(exp: Exp) -> Self {
99 Exp::Ln(Box::new(exp))
100 }
101
102 pub fn exp(exp: Exp) -> Self {
103 Exp::Exp(Box::new(exp))
104 }
105}
106
107impl fmt::Display for Exp {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 Exp::Val(v) => write!(f, "{v}"),
111 Exp::Var(name) => write!(f, "{name}"),
112 Exp::Add(l, r) => write!(f, "({l} + {r})"),
113 Exp::Sub(l, r) => write!(f, "({l} - {r})"),
114 Exp::Mul(l, r) => write!(f, "({l} * {r})"),
115 Exp::Div(l, r) => write!(f, "({l} / {r})"),
116 Exp::Power(base, exp) => write!(f, "({base}^{exp})"),
117 Exp::Neg(e) => write!(f, "(-{e})"),
118 Exp::Sin(e) => write!(f, "sin({e})"),
119 Exp::Cos(e) => write!(f, "cos({e})"),
120 Exp::Ln(e) => write!(f, "ln({e})"),
121 Exp::Exp(e) => write!(f, "exp({e})"),
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_constructors_match_variants() {
132 assert_eq!(Exp::var("x"), Exp::Var("x".to_string()));
133 assert_eq!(Exp::val(2.5), Exp::Val(2.5));
134
135 let expr = Exp::add(Exp::val(1.0), Exp::val(2.0));
136 assert_eq!(
137 expr,
138 Exp::Add(Box::new(Exp::Val(1.0)), Box::new(Exp::Val(2.0)))
139 );
140 }
141
142 #[test]
143 fn test_display_formats_expression() {
144 let x = Exp::var("x");
145 let y = Exp::var("y");
146 let expr = Exp::sub(
147 Exp::add(
148 Exp::power(x.clone(), 2.0),
149 Exp::mul(Exp::val(3.0), y.clone()),
150 ),
151 Exp::val(1.0),
152 );
153
154 assert_eq!(format!("{expr}"), "(((x^2) + (3 * y)) - 1)");
155 }
156}