Skip to main content

zsh/
mathfunc.rs

1//! Mathematical functions for arithmetic expressions - port of Modules/mathfunc.c
2//!
3//! Provides standard math functions like sin, cos, sqrt, log, etc.
4
5use std::f64::consts::PI;
6
7/// Math number type - can be integer or float
8#[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
46/// Math function registry
47pub struct MathFunctions;
48
49impl MathFunctions {
50    /// Evaluate a math function by name
51    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    /// List all available functions
109    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
613/// Mathematical constants
614pub 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; // Golden ratio
619    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}