1use crate::error::{EvaluationError, Result};
15
16pub trait EvaluationDomain: Sized + Clone + 'static {
34 fn from_f64(value: f64) -> Self;
36
37 fn zero() -> Self;
39
40 fn one() -> Self;
42
43 fn add_ref(&self, other: &Self) -> Self;
45
46 fn sub_ref(&self, other: &Self) -> Self;
48
49 fn mul_ref(&self, other: &Self) -> Self;
51
52 fn div_ref(&self, other: &Self) -> Result<Self>;
54
55 fn neg_ref(&self) -> Self;
57
58 fn powi_ref(&self, exp: i64) -> Self;
60
61 fn resolve_builtin(name: &str, arg: &Self) -> Result<Self>;
79}
80
81impl EvaluationDomain for f64 {
86 #[inline]
87 fn from_f64(value: f64) -> Self {
88 value
89 }
90
91 #[inline]
92 fn zero() -> Self {
93 0.0
94 }
95
96 #[inline]
97 fn one() -> Self {
98 1.0
99 }
100
101 #[inline]
102 fn add_ref(&self, other: &Self) -> Self {
103 self + other
104 }
105
106 #[inline]
107 fn sub_ref(&self, other: &Self) -> Self {
108 self - other
109 }
110
111 #[inline]
112 fn mul_ref(&self, other: &Self) -> Self {
113 self * other
114 }
115
116 #[inline]
117 fn div_ref(&self, other: &Self) -> Result<Self> {
118 if *other == 0.0 {
119 Err(EvaluationError::DivisionByZero)
120 } else {
121 Ok(self / other)
122 }
123 }
124
125 #[inline]
126 fn neg_ref(&self) -> Self {
127 -self
128 }
129
130 #[inline]
131 fn powi_ref(&self, exp: i64) -> Self {
132 self.powi(exp as i32)
133 }
134
135 fn resolve_builtin(name: &str, arg: &Self) -> Result<Self> {
136 match name.to_lowercase().as_str() {
137 "sin" => Ok(arg.sin()),
138 "cos" => Ok(arg.cos()),
139 "tan" => Ok(arg.tan()),
140 "sec" => Ok(1.0 / arg.cos()),
141 "csc" => Ok(1.0 / arg.sin()),
142 "cot" => Ok(1.0 / arg.tan()),
143 "exp" => Ok(arg.exp()),
144 "log" => {
145 if *arg <= 0.0 {
146 Err(EvaluationError::UnsupportedOperation {
147 message: "log of non-positive number".into(),
148 })
149 } else {
150 Ok(arg.ln())
151 }
152 }
153 "sqrt" => {
154 if *arg < 0.0 {
155 Err(EvaluationError::UnsupportedOperation {
156 message: "sqrt of negative number".into(),
157 })
158 } else {
159 Ok(arg.sqrt())
160 }
161 }
162 "abs" => Ok(arg.abs()),
163 _ => Err(EvaluationError::FunctionNotFound {
164 name: name.to_string(),
165 }),
166 }
167 }
168}
169
170pub trait PowfExtension: EvaluationDomain {
179 fn powf_ref(&self, exp: &Self) -> Result<Self>;
181}
182
183impl PowfExtension for f64 {
184 fn powf_ref(&self, exp: &Self) -> Result<Self> {
185 Ok(self.powf(*exp))
186 }
187}
188
189use ocas_domain::DoubleF64;
194
195impl EvaluationDomain for DoubleF64 {
196 #[inline]
197 fn from_f64(value: f64) -> Self {
198 DoubleF64::from_f64(value)
199 }
200
201 #[inline]
202 fn zero() -> Self {
203 DoubleF64::ZERO
204 }
205
206 #[inline]
207 fn one() -> Self {
208 DoubleF64::ONE
209 }
210
211 #[inline]
212 fn add_ref(&self, other: &Self) -> Self {
213 *self + *other
214 }
215
216 #[inline]
217 fn sub_ref(&self, other: &Self) -> Self {
218 *self - *other
219 }
220
221 #[inline]
222 fn mul_ref(&self, other: &Self) -> Self {
223 *self * *other
224 }
225
226 #[inline]
227 fn div_ref(&self, other: &Self) -> Result<Self> {
228 if other.hi == 0.0 && other.lo == 0.0 {
229 Err(EvaluationError::DivisionByZero)
230 } else {
231 Ok(*self / *other)
232 }
233 }
234
235 #[inline]
236 fn neg_ref(&self) -> Self {
237 -*self
238 }
239
240 #[inline]
241 fn powi_ref(&self, exp: i64) -> Self {
242 self.powi(exp)
243 }
244
245 fn resolve_builtin(name: &str, arg: &Self) -> Result<Self> {
246 match name.to_lowercase().as_str() {
247 "sin" => Ok(arg.sin()),
248 "cos" => Ok(arg.cos()),
249 "tan" => Ok(arg.tan()),
250 "sec" => Ok(DoubleF64::ONE / arg.cos()),
251 "csc" => Ok(DoubleF64::ONE / arg.sin()),
252 "cot" => Ok(DoubleF64::ONE / arg.tan()),
253 "exp" => Ok(arg.exp()),
254 "log" => {
255 if arg.hi <= 0.0 {
256 Err(EvaluationError::UnsupportedOperation {
257 message: "log of non-positive number".into(),
258 })
259 } else {
260 Ok(arg.ln())
261 }
262 }
263 "sqrt" => {
264 if arg.hi < 0.0 {
265 Err(EvaluationError::UnsupportedOperation {
266 message: "sqrt of negative number".into(),
267 })
268 } else {
269 Ok(arg.sqrt())
270 }
271 }
272 "abs" => Ok(arg.dabs()),
273 _ => Err(EvaluationError::FunctionNotFound {
274 name: name.to_string(),
275 }),
276 }
277 }
278}
279
280impl PowfExtension for DoubleF64 {
281 fn powf_ref(&self, exp: &Self) -> Result<Self> {
282 if self.hi <= 0.0 {
284 return Err(EvaluationError::UnsupportedOperation {
285 message: "powf with non-positive base".into(),
286 });
287 }
288 Ok(exp.mul(self.ln()).exp())
289 }
290}
291
292#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn f64_arithmetic() {
302 assert_eq!(f64::zero(), 0.0);
303 assert_eq!(f64::one(), 1.0);
304 assert_eq!(3.0f64.add_ref(&2.0), 5.0);
305 assert_eq!(3.0f64.sub_ref(&2.0), 1.0);
306 assert_eq!(3.0f64.mul_ref(&2.0), 6.0);
307 assert_eq!(6.0f64.div_ref(&2.0).unwrap(), 3.0);
308 assert!(6.0f64.div_ref(&0.0).is_err());
309 assert_eq!(3.0f64.neg_ref(), -3.0);
310 assert_eq!(2.0f64.powi_ref(3), 8.0);
311 assert_eq!(2.0f64.powi_ref(0), 1.0);
312 }
313
314 #[test]
315 fn f64_builtin_sin_lowercase() {
316 let result = f64::resolve_builtin("sin", &std::f64::consts::FRAC_PI_2).unwrap();
317 assert!((result - 1.0).abs() < 1e-10);
318 }
319
320 #[test]
321 fn f64_builtin_sin_capitalized() {
322 let result = f64::resolve_builtin("Sin", &std::f64::consts::FRAC_PI_2).unwrap();
323 assert!((result - 1.0).abs() < 1e-10);
324 }
325
326 #[test]
327 fn f64_builtin_cos() {
328 let result = f64::resolve_builtin("cos", &std::f64::consts::PI).unwrap();
329 assert!((result + 1.0).abs() < 1e-10);
330 }
331
332 #[test]
333 fn f64_builtin_exp() {
334 let result = f64::resolve_builtin("exp", &1.0).unwrap();
335 assert!((result - std::f64::consts::E).abs() < 1e-10);
336 }
337
338 #[test]
339 fn f64_builtin_log() {
340 let result = f64::resolve_builtin("log", &std::f64::consts::E).unwrap();
341 assert!((result - 1.0).abs() < 1e-10);
342 }
343
344 #[test]
345 fn f64_builtin_log_negative() {
346 assert!(f64::resolve_builtin("log", &(-1.0)).is_err());
347 }
348
349 #[test]
350 fn f64_builtin_sqrt() {
351 let result = f64::resolve_builtin("sqrt", &4.0).unwrap();
352 assert!((result - 2.0).abs() < 1e-10);
353 }
354
355 #[test]
356 fn f64_builtin_sqrt_negative() {
357 assert!(f64::resolve_builtin("sqrt", &(-1.0)).is_err());
358 }
359
360 #[test]
361 fn f64_builtin_abs() {
362 assert_eq!(f64::resolve_builtin("abs", &(-3.0)).unwrap(), 3.0);
363 assert_eq!(f64::resolve_builtin("abs", &3.0).unwrap(), 3.0);
364 }
365
366 #[test]
367 fn f64_builtin_tan() {
368 let result = f64::resolve_builtin("tan", &0.0).unwrap();
369 assert!((result - 0.0).abs() < 1e-10);
370 }
371
372 #[test]
373 fn f64_builtin_unknown() {
374 assert!(f64::resolve_builtin("unknown_fn", &0.0).is_err());
375 }
376
377 #[test]
378 fn f64_from_f64() {
379 assert_eq!(f64::from_f64(42.0), 42.0);
380 }
381}