1use std::f64::consts::PI;
6
7#[derive(Debug, Clone, Copy)]
9pub enum MathNumber {
10 Integer(i64),
11 Float(f64),
12}
13
14impl MathNumber {
15 pub fn as_float(&self) -> f64 {
16 match self {
17 MathNumber::Integer(i) => *i as f64,
18 MathNumber::Float(f) => *f,
19 }
20 }
21
22 pub fn as_int(&self) -> i64 {
23 match self {
24 MathNumber::Integer(i) => *i,
25 MathNumber::Float(f) => *f as i64,
26 }
27 }
28
29 pub fn is_integer(&self) -> bool {
30 matches!(self, MathNumber::Integer(_))
31 }
32}
33
34impl From<i64> for MathNumber {
35 fn from(i: i64) -> Self {
36 MathNumber::Integer(i)
37 }
38}
39
40impl From<f64> for MathNumber {
41 fn from(f: f64) -> Self {
42 MathNumber::Float(f)
43 }
44}
45
46pub struct MathFunctions;
48
49impl MathFunctions {
50 pub fn call(name: &str, args: &[MathNumber]) -> Result<MathNumber, String> {
52 match name {
53 "abs" => Self::abs(args),
54 "acos" => Self::unary_float(args, f64::acos),
55 "acosh" => Self::unary_float(args, f64::acosh),
56 "asin" => Self::unary_float(args, f64::asin),
57 "asinh" => Self::unary_float(args, f64::asinh),
58 "atan" => Self::atan(args),
59 "atanh" => Self::unary_float(args, f64::atanh),
60 "cbrt" => Self::unary_float(args, f64::cbrt),
61 "ceil" => Self::unary_float(args, f64::ceil),
62 "copysign" => Self::binary_float(args, f64::copysign),
63 "cos" => Self::unary_float(args, f64::cos),
64 "cosh" => Self::unary_float(args, f64::cosh),
65 "erf" => Self::erf(args),
66 "erfc" => Self::erfc(args),
67 "exp" => Self::unary_float(args, f64::exp),
68 "expm1" => Self::unary_float(args, f64::exp_m1),
69 "fabs" => Self::unary_float(args, f64::abs),
70 "float" => Self::to_float(args),
71 "floor" => Self::unary_float(args, f64::floor),
72 "fmod" => Self::binary_float(args, |a, b| a % b),
73 "gamma" | "tgamma" => Self::gamma(args),
74 "hypot" => Self::binary_float(args, f64::hypot),
75 "ilogb" => Self::ilogb(args),
76 "int" => Self::to_int(args),
77 "isinf" => Self::isinf(args),
78 "isnan" => Self::isnan(args),
79 "j0" => Self::j0(args),
80 "j1" => Self::j1(args),
81 "jn" => Self::jn(args),
82 "ldexp" => Self::ldexp(args),
83 "lgamma" => Self::lgamma(args),
84 "log" | "ln" => Self::unary_float(args, f64::ln),
85 "log10" => Self::unary_float(args, f64::log10),
86 "log1p" => Self::unary_float(args, f64::ln_1p),
87 "log2" => Self::unary_float(args, f64::log2),
88 "logb" => Self::logb(args),
89 "max" => Self::max(args),
90 "min" => Self::min(args),
91 "nextafter" => Self::nextafter(args),
92 "pow" => Self::binary_float(args, f64::powf),
93 "rint" | "round" => Self::unary_float(args, f64::round),
94 "scalb" | "scalbn" => Self::scalbn(args),
95 "sin" => Self::unary_float(args, f64::sin),
96 "sinh" => Self::unary_float(args, f64::sinh),
97 "sqrt" => Self::unary_float(args, f64::sqrt),
98 "tan" => Self::unary_float(args, f64::tan),
99 "tanh" => Self::unary_float(args, f64::tanh),
100 "trunc" => Self::unary_float(args, f64::trunc),
101 "y0" => Self::y0(args),
102 "y1" => Self::y1(args),
103 "yn" => Self::yn(args),
104 _ => Err(format!("unknown function: {}", name)),
105 }
106 }
107
108 pub fn list() -> Vec<&'static str> {
110 vec![
111 "abs",
112 "acos",
113 "acosh",
114 "asin",
115 "asinh",
116 "atan",
117 "atanh",
118 "cbrt",
119 "ceil",
120 "copysign",
121 "cos",
122 "cosh",
123 "erf",
124 "erfc",
125 "exp",
126 "expm1",
127 "fabs",
128 "float",
129 "floor",
130 "fmod",
131 "gamma",
132 "hypot",
133 "ilogb",
134 "int",
135 "isinf",
136 "isnan",
137 "j0",
138 "j1",
139 "jn",
140 "ldexp",
141 "lgamma",
142 "log",
143 "log10",
144 "log1p",
145 "log2",
146 "logb",
147 "max",
148 "min",
149 "nextafter",
150 "pow",
151 "rint",
152 "round",
153 "scalb",
154 "scalbn",
155 "sin",
156 "sinh",
157 "sqrt",
158 "tan",
159 "tanh",
160 "trunc",
161 "y0",
162 "y1",
163 "yn",
164 ]
165 }
166
167 fn check_args(args: &[MathNumber], min: usize, max: usize, name: &str) -> Result<(), String> {
168 if args.len() < min {
169 return Err(format!("{}: not enough arguments", name));
170 }
171 if args.len() > max {
172 return Err(format!("{}: too many arguments", name));
173 }
174 Ok(())
175 }
176
177 fn unary_float(args: &[MathNumber], f: fn(f64) -> f64) -> Result<MathNumber, String> {
178 if args.is_empty() {
179 return Err("not enough arguments".to_string());
180 }
181 Ok(MathNumber::Float(f(args[0].as_float())))
182 }
183
184 fn binary_float(args: &[MathNumber], f: fn(f64, f64) -> f64) -> Result<MathNumber, String> {
185 if args.len() < 2 {
186 return Err("not enough arguments".to_string());
187 }
188 Ok(MathNumber::Float(f(args[0].as_float(), args[1].as_float())))
189 }
190
191 fn abs(args: &[MathNumber]) -> Result<MathNumber, String> {
192 Self::check_args(args, 1, 1, "abs")?;
193 match args[0] {
194 MathNumber::Integer(i) => Ok(MathNumber::Integer(i.abs())),
195 MathNumber::Float(f) => Ok(MathNumber::Float(f.abs())),
196 }
197 }
198
199 fn atan(args: &[MathNumber]) -> Result<MathNumber, String> {
200 Self::check_args(args, 1, 2, "atan")?;
201 if args.len() == 2 {
202 Ok(MathNumber::Float(
203 args[0].as_float().atan2(args[1].as_float()),
204 ))
205 } else {
206 Ok(MathNumber::Float(args[0].as_float().atan()))
207 }
208 }
209
210 fn to_float(args: &[MathNumber]) -> Result<MathNumber, String> {
211 Self::check_args(args, 1, 1, "float")?;
212 Ok(MathNumber::Float(args[0].as_float()))
213 }
214
215 fn to_int(args: &[MathNumber]) -> Result<MathNumber, String> {
216 Self::check_args(args, 1, 1, "int")?;
217 Ok(MathNumber::Integer(args[0].as_int()))
218 }
219
220 fn isinf(args: &[MathNumber]) -> Result<MathNumber, String> {
221 Self::check_args(args, 1, 1, "isinf")?;
222 let f = args[0].as_float();
223 Ok(MathNumber::Integer(if f.is_infinite() { 1 } else { 0 }))
224 }
225
226 fn isnan(args: &[MathNumber]) -> Result<MathNumber, String> {
227 Self::check_args(args, 1, 1, "isnan")?;
228 let f = args[0].as_float();
229 Ok(MathNumber::Integer(if f.is_nan() { 1 } else { 0 }))
230 }
231
232 fn ilogb(args: &[MathNumber]) -> Result<MathNumber, String> {
233 Self::check_args(args, 1, 1, "ilogb")?;
234 let f = args[0].as_float();
235 if f == 0.0 {
236 return Ok(MathNumber::Integer(i64::MIN));
237 }
238 Ok(MathNumber::Integer(f.abs().log2().floor() as i64))
239 }
240
241 fn logb(args: &[MathNumber]) -> Result<MathNumber, String> {
242 Self::check_args(args, 1, 1, "logb")?;
243 let f = args[0].as_float();
244 if f == 0.0 {
245 return Ok(MathNumber::Float(f64::NEG_INFINITY));
246 }
247 Ok(MathNumber::Float(f.abs().log2().floor()))
248 }
249
250 fn ldexp(args: &[MathNumber]) -> Result<MathNumber, String> {
251 Self::check_args(args, 2, 2, "ldexp")?;
252 let x = args[0].as_float();
253 let exp = args[1].as_int() as i32;
254 Ok(MathNumber::Float(x * 2f64.powi(exp)))
255 }
256
257 fn scalbn(args: &[MathNumber]) -> Result<MathNumber, String> {
258 Self::ldexp(args)
259 }
260
261 fn nextafter(args: &[MathNumber]) -> Result<MathNumber, String> {
262 Self::check_args(args, 2, 2, "nextafter")?;
263 let x = args[0].as_float();
264 let y = args[1].as_float();
265
266 if x == y {
267 return Ok(MathNumber::Float(y));
268 }
269
270 let bits = x.to_bits();
271 let next_bits = if (y > x) == (x >= 0.0) {
272 bits.wrapping_add(1)
273 } else {
274 bits.wrapping_sub(1)
275 };
276 Ok(MathNumber::Float(f64::from_bits(next_bits)))
277 }
278
279 fn max(args: &[MathNumber]) -> Result<MathNumber, String> {
280 if args.is_empty() {
281 return Err("max: not enough arguments".to_string());
282 }
283 let mut max_val = args[0].as_float();
284 for arg in &args[1..] {
285 let val = arg.as_float();
286 if val > max_val {
287 max_val = val;
288 }
289 }
290 if args.iter().all(|a| a.is_integer()) {
291 Ok(MathNumber::Integer(max_val as i64))
292 } else {
293 Ok(MathNumber::Float(max_val))
294 }
295 }
296
297 fn min(args: &[MathNumber]) -> Result<MathNumber, String> {
298 if args.is_empty() {
299 return Err("min: not enough arguments".to_string());
300 }
301 let mut min_val = args[0].as_float();
302 for arg in &args[1..] {
303 let val = arg.as_float();
304 if val < min_val {
305 min_val = val;
306 }
307 }
308 if args.iter().all(|a| a.is_integer()) {
309 Ok(MathNumber::Integer(min_val as i64))
310 } else {
311 Ok(MathNumber::Float(min_val))
312 }
313 }
314
315 fn gamma(args: &[MathNumber]) -> Result<MathNumber, String> {
316 Self::check_args(args, 1, 1, "gamma")?;
317 let x = args[0].as_float();
318 Ok(MathNumber::Float(gamma_fn(x)))
319 }
320
321 fn lgamma(args: &[MathNumber]) -> Result<MathNumber, String> {
322 Self::check_args(args, 1, 1, "lgamma")?;
323 let x = args[0].as_float();
324 Ok(MathNumber::Float(lgamma_fn(x)))
325 }
326
327 fn erf(args: &[MathNumber]) -> Result<MathNumber, String> {
328 Self::check_args(args, 1, 1, "erf")?;
329 let x = args[0].as_float();
330 Ok(MathNumber::Float(erf_fn(x)))
331 }
332
333 fn erfc(args: &[MathNumber]) -> Result<MathNumber, String> {
334 Self::check_args(args, 1, 1, "erfc")?;
335 let x = args[0].as_float();
336 Ok(MathNumber::Float(1.0 - erf_fn(x)))
337 }
338
339 fn j0(args: &[MathNumber]) -> Result<MathNumber, String> {
340 Self::check_args(args, 1, 1, "j0")?;
341 let x = args[0].as_float();
342 Ok(MathNumber::Float(bessel_j0(x)))
343 }
344
345 fn j1(args: &[MathNumber]) -> Result<MathNumber, String> {
346 Self::check_args(args, 1, 1, "j1")?;
347 let x = args[0].as_float();
348 Ok(MathNumber::Float(bessel_j1(x)))
349 }
350
351 fn jn(args: &[MathNumber]) -> Result<MathNumber, String> {
352 Self::check_args(args, 2, 2, "jn")?;
353 let n = args[0].as_int() as i32;
354 let x = args[1].as_float();
355 Ok(MathNumber::Float(bessel_jn(n, x)))
356 }
357
358 fn y0(args: &[MathNumber]) -> Result<MathNumber, String> {
359 Self::check_args(args, 1, 1, "y0")?;
360 let x = args[0].as_float();
361 Ok(MathNumber::Float(bessel_y0(x)))
362 }
363
364 fn y1(args: &[MathNumber]) -> Result<MathNumber, String> {
365 Self::check_args(args, 1, 1, "y1")?;
366 let x = args[0].as_float();
367 Ok(MathNumber::Float(bessel_y1(x)))
368 }
369
370 fn yn(args: &[MathNumber]) -> Result<MathNumber, String> {
371 Self::check_args(args, 2, 2, "yn")?;
372 let n = args[0].as_int() as i32;
373 let x = args[1].as_float();
374 Ok(MathNumber::Float(bessel_yn(n, x)))
375 }
376}
377
378fn gamma_fn(x: f64) -> f64 {
379 if x <= 0.0 && x == x.floor() {
380 return f64::INFINITY;
381 }
382
383 if x < 0.5 {
384 PI / (PI * x).sin() / gamma_fn(1.0 - x)
385 } else {
386 let x = x - 1.0;
387 let g = 7;
388 let c = [
389 0.99999999999980993,
390 676.5203681218851,
391 -1259.1392167224028,
392 771.32342877765313,
393 -176.61502916214059,
394 12.507343278686905,
395 -0.13857109526572012,
396 9.9843695780195716e-6,
397 1.5056327351493116e-7,
398 ];
399
400 let mut sum = c[0];
401 for (i, &coef) in c.iter().enumerate().skip(1) {
402 sum += coef / (x + i as f64);
403 }
404
405 let t = x + g as f64 + 0.5;
406 (2.0 * PI).sqrt() * t.powf(x + 0.5) * (-t).exp() * sum
407 }
408}
409
410fn lgamma_fn(x: f64) -> f64 {
411 gamma_fn(x).abs().ln()
412}
413
414fn erf_fn(x: f64) -> f64 {
415 let a1 = 0.254829592;
416 let a2 = -0.284496736;
417 let a3 = 1.421413741;
418 let a4 = -1.453152027;
419 let a5 = 1.061405429;
420 let p = 0.3275911;
421
422 let sign = if x < 0.0 { -1.0 } else { 1.0 };
423 let x = x.abs();
424
425 let t = 1.0 / (1.0 + p * x);
426 let y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x * x).exp();
427
428 sign * y
429}
430
431fn bessel_j0(x: f64) -> f64 {
432 let x = x.abs();
433 if x < 8.0 {
434 let y = x * x;
435 let ans1 = 57568490574.0
436 + y * (-13362590354.0
437 + y * (651619640.7 + y * (-11214424.18 + y * (77392.33017 + y * (-184.9052456)))));
438 let ans2 = 57568490411.0
439 + y * (1029532985.0
440 + y * (9494680.718 + y * (59272.64853 + y * (267.8532712 + y * 1.0))));
441 ans1 / ans2
442 } else {
443 let z = 8.0 / x;
444 let y = z * z;
445 let xx = x - 0.785398164;
446 let ans1 = 1.0
447 + y * (-0.1098628627e-2
448 + y * (0.2734510407e-4 + y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
449 let ans2 = -0.1562499995e-1
450 + y * (0.1430488765e-3
451 + y * (-0.6911147651e-5 + y * (0.7621095161e-6 - y * 0.934945152e-7)));
452 (0.636619772 / x).sqrt() * (xx.cos() * ans1 - z * xx.sin() * ans2)
453 }
454}
455
456fn bessel_j1(x: f64) -> f64 {
457 let ax = x.abs();
458 if ax < 8.0 {
459 let y = x * x;
460 let ans1 = x
461 * (72362614232.0
462 + y * (-7895059235.0
463 + y * (242396853.1
464 + y * (-2972611.439 + y * (15704.48260 + y * (-30.16036606))))));
465 let ans2 = 144725228442.0
466 + y * (2300535178.0
467 + y * (18583304.74 + y * (99447.43394 + y * (376.9991397 + y * 1.0))));
468 ans1 / ans2
469 } else {
470 let z = 8.0 / ax;
471 let y = z * z;
472 let xx = ax - 2.356194491;
473 let ans1 = 1.0
474 + y * (0.183105e-2
475 + y * (-0.3516396496e-4 + y * (0.2457520174e-5 + y * (-0.240337019e-6))));
476 let ans2 = 0.04687499995
477 + y * (-0.2002690873e-3
478 + y * (0.8449199096e-5 + y * (-0.88228987e-6 + y * 0.105787412e-6)));
479 let ans = (0.636619772 / ax).sqrt() * (xx.cos() * ans1 - z * xx.sin() * ans2);
480 if x < 0.0 {
481 -ans
482 } else {
483 ans
484 }
485 }
486}
487
488fn bessel_jn(n: i32, x: f64) -> f64 {
489 match n {
490 0 => bessel_j0(x),
491 1 => bessel_j1(x),
492 _ => {
493 if x == 0.0 {
494 return 0.0;
495 }
496 let n = n.unsigned_abs() as usize;
497 let ax = x.abs();
498
499 if ax > n as f64 {
500 let mut bjm = bessel_j0(ax);
501 let mut bj = bessel_j1(ax);
502 for j in 1..n {
503 let bjp = 2.0 * j as f64 / ax * bj - bjm;
504 bjm = bj;
505 bj = bjp;
506 }
507 bj
508 } else {
509 let tox = 2.0 / ax;
510 let m = 2 * ((n + (((40.0 * n as f64).sqrt()) as usize)) / 2);
511 let mut bjp = 0.0;
512 let mut bj = 1.0;
513 let mut ans = 0.0;
514 let mut sum = 0.0;
515 for j in (1..=m).rev() {
516 let bjm = j as f64 * tox * bj - bjp;
517 bjp = bj;
518 bj = bjm;
519 if bj.abs() > 1e10 {
520 bj *= 1e-10;
521 bjp *= 1e-10;
522 ans *= 1e-10;
523 sum *= 1e-10;
524 }
525 if j % 2 != 0 {
526 sum += bj;
527 }
528 if j == n {
529 ans = bjp;
530 }
531 }
532 sum = 2.0 * sum - bj;
533 ans /= sum;
534 if x < 0.0 && n % 2 != 0 {
535 -ans
536 } else {
537 ans
538 }
539 }
540 }
541 }
542}
543
544fn bessel_y0(x: f64) -> f64 {
545 if x < 8.0 {
546 let y = x * x;
547 let ans1 = -2957821389.0
548 + y * (7062834065.0
549 + y * (-512359803.6 + y * (10879881.29 + y * (-86327.92757 + y * 228.4622733))));
550 let ans2 = 40076544269.0
551 + y * (745249964.8
552 + y * (7189466.438 + y * (47447.26470 + y * (226.1030244 + y * 1.0))));
553 ans1 / ans2 + 0.636619772 * bessel_j0(x) * x.ln()
554 } else {
555 let z = 8.0 / x;
556 let y = z * z;
557 let xx = x - 0.785398164;
558 let ans1 = 1.0
559 + y * (-0.1098628627e-2
560 + y * (0.2734510407e-4 + y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
561 let ans2 = -0.1562499995e-1
562 + y * (0.1430488765e-3
563 + y * (-0.6911147651e-5 + y * (0.7621095161e-6 + y * (-0.934945152e-7))));
564 (0.636619772 / x).sqrt() * (xx.sin() * ans1 + z * xx.cos() * ans2)
565 }
566}
567
568fn bessel_y1(x: f64) -> f64 {
569 if x < 8.0 {
570 let y = x * x;
571 let ans1 = x
572 * (-0.4900604943e13
573 + y * (0.1275274390e13
574 + y * (-0.5153438139e11
575 + y * (0.7349264551e9 + y * (-0.4237922726e7 + y * 0.8511937935e4)))));
576 let ans2 = 0.2499580570e14
577 + y * (0.4244419664e12
578 + y * (0.3733650367e10
579 + y * (0.2245904002e8 + y * (0.1020426050e6 + y * (0.3549632885e3 + y)))));
580 ans1 / ans2 + 0.636619772 * (bessel_j1(x) * x.ln() - 1.0 / x)
581 } else {
582 let z = 8.0 / x;
583 let y = z * z;
584 let xx = x - 2.356194491;
585 let ans1 = 1.0
586 + y * (0.183105e-2
587 + y * (-0.3516396496e-4 + y * (0.2457520174e-5 + y * (-0.240337019e-6))));
588 let ans2 = 0.04687499995
589 + y * (-0.2002690873e-3
590 + y * (0.8449199096e-5 + y * (-0.88228987e-6 + y * 0.105787412e-6)));
591 (0.636619772 / x).sqrt() * (xx.sin() * ans1 + z * xx.cos() * ans2)
592 }
593}
594
595fn bessel_yn(n: i32, x: f64) -> f64 {
596 match n {
597 0 => bessel_y0(x),
598 1 => bessel_y1(x),
599 _ => {
600 let tox = 2.0 / x;
601 let mut bym = bessel_y0(x);
602 let mut by = bessel_y1(x);
603 for j in 1..n {
604 let byp = j as f64 * tox * by - bym;
605 bym = by;
606 by = byp;
607 }
608 by
609 }
610 }
611}
612
613pub mod constants {
615 pub const PI: f64 = std::f64::consts::PI;
616 pub const E: f64 = std::f64::consts::E;
617 pub const TAU: f64 = std::f64::consts::TAU;
618 pub const PHI: f64 = 1.618033988749895; pub const SQRT2: f64 = std::f64::consts::SQRT_2;
620 pub const LN2: f64 = std::f64::consts::LN_2;
621 pub const LN10: f64 = std::f64::consts::LN_10;
622}
623
624#[cfg(test)]
625mod tests {
626 use super::*;
627 use constants::E;
628
629 #[test]
630 fn test_abs() {
631 let result = MathFunctions::call("abs", &[MathNumber::Integer(-5)]).unwrap();
632 assert!(matches!(result, MathNumber::Integer(5)));
633
634 let result = MathFunctions::call("abs", &[MathNumber::Float(-3.14)]).unwrap();
635 if let MathNumber::Float(f) = result {
636 assert!((f - 3.14).abs() < 1e-10);
637 } else {
638 panic!("expected float");
639 }
640 }
641
642 #[test]
643 fn test_trig() {
644 let result = MathFunctions::call("sin", &[MathNumber::Float(0.0)]).unwrap();
645 if let MathNumber::Float(f) = result {
646 assert!(f.abs() < 1e-10);
647 }
648
649 let result = MathFunctions::call("cos", &[MathNumber::Float(0.0)]).unwrap();
650 if let MathNumber::Float(f) = result {
651 assert!((f - 1.0).abs() < 1e-10);
652 }
653
654 let result = MathFunctions::call("tan", &[MathNumber::Float(0.0)]).unwrap();
655 if let MathNumber::Float(f) = result {
656 assert!(f.abs() < 1e-10);
657 }
658 }
659
660 #[test]
661 fn test_sqrt() {
662 let result = MathFunctions::call("sqrt", &[MathNumber::Float(4.0)]).unwrap();
663 if let MathNumber::Float(f) = result {
664 assert!((f - 2.0).abs() < 1e-10);
665 }
666 }
667
668 #[test]
669 fn test_log() {
670 let result = MathFunctions::call("log", &[MathNumber::Float(E)]).unwrap();
671 if let MathNumber::Float(f) = result {
672 assert!((f - 1.0).abs() < 1e-10);
673 }
674
675 let result = MathFunctions::call("log10", &[MathNumber::Float(100.0)]).unwrap();
676 if let MathNumber::Float(f) = result {
677 assert!((f - 2.0).abs() < 1e-10);
678 }
679 }
680
681 #[test]
682 fn test_exp() {
683 let result = MathFunctions::call("exp", &[MathNumber::Float(1.0)]).unwrap();
684 if let MathNumber::Float(f) = result {
685 assert!((f - E).abs() < 1e-10);
686 }
687 }
688
689 #[test]
690 fn test_floor_ceil() {
691 let result = MathFunctions::call("floor", &[MathNumber::Float(3.7)]).unwrap();
692 if let MathNumber::Float(f) = result {
693 assert!((f - 3.0).abs() < 1e-10);
694 }
695
696 let result = MathFunctions::call("ceil", &[MathNumber::Float(3.2)]).unwrap();
697 if let MathNumber::Float(f) = result {
698 assert!((f - 4.0).abs() < 1e-10);
699 }
700 }
701
702 #[test]
703 fn test_pow() {
704 let result =
705 MathFunctions::call("pow", &[MathNumber::Float(2.0), MathNumber::Float(3.0)]).unwrap();
706 if let MathNumber::Float(f) = result {
707 assert!((f - 8.0).abs() < 1e-10);
708 }
709 }
710
711 #[test]
712 fn test_atan2() {
713 let result =
714 MathFunctions::call("atan", &[MathNumber::Float(1.0), MathNumber::Float(1.0)]).unwrap();
715 if let MathNumber::Float(f) = result {
716 assert!((f - PI / 4.0).abs() < 1e-10);
717 }
718 }
719
720 #[test]
721 fn test_hypot() {
722 let result =
723 MathFunctions::call("hypot", &[MathNumber::Float(3.0), MathNumber::Float(4.0)])
724 .unwrap();
725 if let MathNumber::Float(f) = result {
726 assert!((f - 5.0).abs() < 1e-10);
727 }
728 }
729
730 #[test]
731 fn test_min_max() {
732 let result = MathFunctions::call(
733 "max",
734 &[
735 MathNumber::Integer(1),
736 MathNumber::Integer(5),
737 MathNumber::Integer(3),
738 ],
739 )
740 .unwrap();
741 assert!(matches!(result, MathNumber::Integer(5)));
742
743 let result = MathFunctions::call(
744 "min",
745 &[
746 MathNumber::Float(1.5),
747 MathNumber::Float(0.5),
748 MathNumber::Float(2.5),
749 ],
750 )
751 .unwrap();
752 if let MathNumber::Float(f) = result {
753 assert!((f - 0.5).abs() < 1e-10);
754 }
755 }
756
757 #[test]
758 fn test_int_float_conversion() {
759 let result = MathFunctions::call("int", &[MathNumber::Float(3.7)]).unwrap();
760 assert!(matches!(result, MathNumber::Integer(3)));
761
762 let result = MathFunctions::call("float", &[MathNumber::Integer(5)]).unwrap();
763 if let MathNumber::Float(f) = result {
764 assert!((f - 5.0).abs() < 1e-10);
765 }
766 }
767
768 #[test]
769 fn test_isinf_isnan() {
770 let result = MathFunctions::call("isinf", &[MathNumber::Float(f64::INFINITY)]).unwrap();
771 assert!(matches!(result, MathNumber::Integer(1)));
772
773 let result = MathFunctions::call("isinf", &[MathNumber::Float(1.0)]).unwrap();
774 assert!(matches!(result, MathNumber::Integer(0)));
775
776 let result = MathFunctions::call("isnan", &[MathNumber::Float(f64::NAN)]).unwrap();
777 assert!(matches!(result, MathNumber::Integer(1)));
778 }
779
780 #[test]
781 fn test_bessel_j0() {
782 let result = MathFunctions::call("j0", &[MathNumber::Float(0.0)]).unwrap();
783 if let MathNumber::Float(f) = result {
784 assert!((f - 1.0).abs() < 1e-6);
785 }
786 }
787
788 #[test]
789 fn test_list() {
790 let funcs = MathFunctions::list();
791 assert!(funcs.contains(&"sin"));
792 assert!(funcs.contains(&"cos"));
793 assert!(funcs.contains(&"sqrt"));
794 assert!(funcs.contains(&"log"));
795 }
796}