Skip to main content

cel_core/ext/
math_ext.rs

1//! Math extension library for CEL.
2//!
3//! This module provides additional math functions beyond the CEL standard library,
4//! matching the cel-go math extension.
5//!
6//! # Functions
7//!
8//! - `math.greatest(...)` - Returns greatest of arguments (variadic or list)
9//! - `math.least(...)` - Returns least of arguments (variadic or list)
10//! - `math.ceil(double)` - Ceiling function
11//! - `math.floor(double)` - Floor function
12//! - `math.round(double)` - Round to nearest integer
13//! - `math.trunc(double)` - Truncate toward zero
14//! - `math.abs(number)` - Absolute value
15//! - `math.sign(number)` - Sign of number (-1, 0, or 1)
16//! - `math.isNaN(double)` - Check if NaN
17//! - `math.isInf(double)` - Check if infinite
18//! - `math.isFinite(double)` - Check if finite
19//! - `math.bitAnd(int, int)` - Bitwise AND
20//! - `math.bitOr(int, int)` - Bitwise OR
21//! - `math.bitXor(int, int)` - Bitwise XOR
22//! - `math.bitNot(int)` - Bitwise NOT
23//! - `math.bitShiftLeft(int, int)` - Left shift
24//! - `math.bitShiftRight(int, int)` - Right shift
25
26use std::cmp::Ordering;
27
28use crate::eval::{EvalError, Value};
29use crate::types::{CelType, FunctionDecl, OverloadDecl};
30
31/// Returns the math extension library function declarations.
32pub fn math_extension() -> Vec<FunctionDecl> {
33    let mut funcs = Vec::new();
34
35    // math.greatest and math.least with multiple arities
36    funcs.push(build_minmax_function("math.greatest", true));
37    funcs.push(build_minmax_function("math.least", false));
38
39    // Simple double functions
40    funcs.push(
41        FunctionDecl::new("math.ceil").with_overload(
42            OverloadDecl::function("math_ceil_double", vec![CelType::Double], CelType::Double)
43                .with_impl(|args| match &args[0] {
44                    Value::Double(v) => Value::Double(v.ceil()),
45                    _ => Value::error(EvalError::invalid_argument("expected double")),
46                }),
47        ),
48    );
49    funcs.push(
50        FunctionDecl::new("math.floor").with_overload(
51            OverloadDecl::function("math_floor_double", vec![CelType::Double], CelType::Double)
52                .with_impl(|args| match &args[0] {
53                    Value::Double(v) => Value::Double(v.floor()),
54                    _ => Value::error(EvalError::invalid_argument("expected double")),
55                }),
56        ),
57    );
58    funcs.push(
59        FunctionDecl::new("math.round").with_overload(
60            OverloadDecl::function("math_round_double", vec![CelType::Double], CelType::Double)
61                .with_impl(|args| match &args[0] {
62                    Value::Double(v) => {
63                        // Round half away from zero (CEL spec behavior)
64                        let rounded = if *v >= 0.0 {
65                            (*v + 0.5).floor()
66                        } else {
67                            (*v - 0.5).ceil()
68                        };
69                        Value::Double(rounded)
70                    }
71                    _ => Value::error(EvalError::invalid_argument("expected double")),
72                }),
73        ),
74    );
75    funcs.push(
76        FunctionDecl::new("math.trunc").with_overload(
77            OverloadDecl::function("math_trunc_double", vec![CelType::Double], CelType::Double)
78                .with_impl(|args| match &args[0] {
79                    Value::Double(v) => Value::Double(v.trunc()),
80                    _ => Value::error(EvalError::invalid_argument("expected double")),
81                }),
82        ),
83    );
84
85    // math.abs
86    funcs.push(
87        FunctionDecl::new("math.abs")
88            .with_overload(
89                OverloadDecl::function("math_abs_int", vec![CelType::Int], CelType::Int).with_impl(
90                    |args| match &args[0] {
91                        Value::Int(v) => match v.checked_abs() {
92                            Some(r) => Value::Int(r),
93                            None => Value::error(EvalError::overflow("integer overflow in abs")),
94                        },
95                        _ => Value::error(EvalError::invalid_argument("expected int")),
96                    },
97                ),
98            )
99            .with_overload(
100                OverloadDecl::function("math_abs_uint", vec![CelType::UInt], CelType::UInt)
101                    .with_impl(|args| match &args[0] {
102                        Value::UInt(v) => Value::UInt(*v),
103                        _ => Value::error(EvalError::invalid_argument("expected uint")),
104                    }),
105            )
106            .with_overload(
107                OverloadDecl::function("math_abs_double", vec![CelType::Double], CelType::Double)
108                    .with_impl(|args| match &args[0] {
109                        Value::Double(v) => Value::Double(v.abs()),
110                        _ => Value::error(EvalError::invalid_argument("expected double")),
111                    }),
112            ),
113    );
114
115    // math.sign
116    funcs.push(
117        FunctionDecl::new("math.sign")
118            .with_overload(
119                OverloadDecl::function("math_sign_int", vec![CelType::Int], CelType::Int)
120                    .with_impl(|args| match &args[0] {
121                        Value::Int(v) => Value::Int(v.signum()),
122                        _ => Value::error(EvalError::invalid_argument("expected int")),
123                    }),
124            )
125            .with_overload(
126                OverloadDecl::function("math_sign_uint", vec![CelType::UInt], CelType::UInt)
127                    .with_impl(|args| match &args[0] {
128                        Value::UInt(v) => Value::UInt(if *v == 0 { 0 } else { 1 }),
129                        _ => Value::error(EvalError::invalid_argument("expected uint")),
130                    }),
131            )
132            .with_overload(
133                OverloadDecl::function("math_sign_double", vec![CelType::Double], CelType::Double)
134                    .with_impl(|args| match &args[0] {
135                        Value::Double(v) => {
136                            if v.is_nan() {
137                                Value::Double(f64::NAN)
138                            } else if *v > 0.0 {
139                                Value::Double(1.0)
140                            } else if *v < 0.0 {
141                                Value::Double(-1.0)
142                            } else {
143                                Value::Double(0.0)
144                            }
145                        }
146                        _ => Value::error(EvalError::invalid_argument("expected double")),
147                    }),
148            ),
149    );
150
151    // math.isNaN, isInf, isFinite
152    funcs.push(
153        FunctionDecl::new("math.isNaN").with_overload(
154            OverloadDecl::function("math_isnan_double", vec![CelType::Double], CelType::Bool)
155                .with_impl(|args| match &args[0] {
156                    Value::Double(v) => Value::Bool(v.is_nan()),
157                    _ => Value::error(EvalError::invalid_argument("expected double")),
158                }),
159        ),
160    );
161    funcs.push(
162        FunctionDecl::new("math.isInf").with_overload(
163            OverloadDecl::function("math_isinf_double", vec![CelType::Double], CelType::Bool)
164                .with_impl(|args| match &args[0] {
165                    Value::Double(v) => Value::Bool(v.is_infinite()),
166                    _ => Value::error(EvalError::invalid_argument("expected double")),
167                }),
168        ),
169    );
170    funcs.push(
171        FunctionDecl::new("math.isFinite").with_overload(
172            OverloadDecl::function("math_isfinite_double", vec![CelType::Double], CelType::Bool)
173                .with_impl(|args| match &args[0] {
174                    Value::Double(v) => Value::Bool(v.is_finite()),
175                    _ => Value::error(EvalError::invalid_argument("expected double")),
176                }),
177        ),
178    );
179
180    // Bit operations
181    add_bit_operations(&mut funcs);
182
183    funcs
184}
185
186/// Compare two numeric values, returning an ordering.
187/// Handles cross-type comparison between Int, UInt, and Double.
188fn compare_numeric(a: &Value, b: &Value) -> Option<Ordering> {
189    match (a, b) {
190        (Value::Int(x), Value::Int(y)) => x.partial_cmp(y),
191        (Value::UInt(x), Value::UInt(y)) => x.partial_cmp(y),
192        (Value::Double(x), Value::Double(y)) => x.partial_cmp(y),
193        (Value::Int(x), Value::UInt(y)) => {
194            if *x < 0 {
195                Some(Ordering::Less)
196            } else {
197                (*x as u64).partial_cmp(y)
198            }
199        }
200        (Value::UInt(x), Value::Int(y)) => {
201            if *y < 0 {
202                Some(Ordering::Greater)
203            } else {
204                x.partial_cmp(&(*y as u64))
205            }
206        }
207        (Value::Int(x), Value::Double(y)) => (*x as f64).partial_cmp(y),
208        (Value::Double(x), Value::Int(y)) => x.partial_cmp(&(*y as f64)),
209        (Value::UInt(x), Value::Double(y)) => (*x as f64).partial_cmp(y),
210        (Value::Double(x), Value::UInt(y)) => x.partial_cmp(&(*y as f64)),
211        _ => None,
212    }
213}
214
215/// Shared implementation for math.greatest.
216/// Works for any arity and for list arguments.
217fn greatest_impl(args: &[Value]) -> Value {
218    // If single arg is a list, operate on list elements
219    let values: &[Value] = if args.len() == 1 {
220        if let Value::List(list) = &args[0] {
221            list
222        } else {
223            // Single non-list value: identity
224            return args[0].clone();
225        }
226    } else {
227        args
228    };
229
230    if values.is_empty() {
231        return Value::error(EvalError::invalid_argument(
232            "math.greatest requires at least one argument",
233        ));
234    }
235
236    // Check for error values
237    for v in values {
238        if let Value::Error(_) = v {
239            return v.clone();
240        }
241    }
242
243    let mut best = &values[0];
244    for v in &values[1..] {
245        match compare_numeric(v, best) {
246            Some(Ordering::Greater) => best = v,
247            None => {
248                // NaN: if best is NaN, replace it; if v is NaN, keep best
249                if let Value::Double(d) = best {
250                    if d.is_nan() {
251                        best = v;
252                        continue;
253                    }
254                }
255                if let Value::Double(d) = v {
256                    if d.is_nan() {
257                        continue;
258                    }
259                }
260                return Value::error(EvalError::invalid_argument(
261                    "math.greatest: incomparable types",
262                ));
263            }
264            _ => {}
265        }
266    }
267    best.clone()
268}
269
270/// Shared implementation for math.least.
271/// Works for any arity and for list arguments.
272fn least_impl(args: &[Value]) -> Value {
273    // If single arg is a list, operate on list elements
274    let values: &[Value] = if args.len() == 1 {
275        if let Value::List(list) = &args[0] {
276            list
277        } else {
278            // Single non-list value: identity
279            return args[0].clone();
280        }
281    } else {
282        args
283    };
284
285    if values.is_empty() {
286        return Value::error(EvalError::invalid_argument(
287            "math.least requires at least one argument",
288        ));
289    }
290
291    // Check for error values
292    for v in values {
293        if let Value::Error(_) = v {
294            return v.clone();
295        }
296    }
297
298    let mut best = &values[0];
299    for v in &values[1..] {
300        match compare_numeric(v, best) {
301            Some(Ordering::Less) => best = v,
302            None => {
303                // NaN: if best is NaN, replace it; if v is NaN, keep best
304                if let Value::Double(d) = best {
305                    if d.is_nan() {
306                        best = v;
307                        continue;
308                    }
309                }
310                if let Value::Double(d) = v {
311                    if d.is_nan() {
312                        continue;
313                    }
314                }
315                return Value::error(EvalError::invalid_argument(
316                    "math.least: incomparable types",
317                ));
318            }
319            _ => {}
320        }
321    }
322    best.clone()
323}
324
325fn build_minmax_function(name: &str, is_greatest: bool) -> FunctionDecl {
326    let base = name.replace('.', "_");
327    let mut decl = FunctionDecl::new(name);
328
329    let shared_impl: fn(&[Value]) -> Value = if is_greatest {
330        greatest_impl
331    } else {
332        least_impl
333    };
334
335    // Unary (identity)
336    for (suffix, cel_type) in [
337        ("int", CelType::Int),
338        ("uint", CelType::UInt),
339        ("double", CelType::Double),
340    ] {
341        decl = decl.with_overload(
342            OverloadDecl::function(
343                format!("{}_{}", base, suffix),
344                vec![cel_type.clone()],
345                cel_type,
346            )
347            .with_impl(shared_impl),
348        );
349    }
350
351    // Binary same-type
352    for (suffix, cel_type) in [
353        ("int", CelType::Int),
354        ("uint", CelType::UInt),
355        ("double", CelType::Double),
356    ] {
357        decl = decl.with_overload(
358            OverloadDecl::function(
359                format!("{}_{}_{}", base, suffix, suffix),
360                vec![cel_type.clone(), cel_type.clone()],
361                cel_type,
362            )
363            .with_impl(shared_impl),
364        );
365    }
366
367    // Binary mixed-type -> Dyn
368    let types = [
369        ("int", CelType::Int),
370        ("uint", CelType::UInt),
371        ("double", CelType::Double),
372    ];
373    for (name1, type1) in &types {
374        for (name2, type2) in &types {
375            if name1 != name2 {
376                decl = decl.with_overload(
377                    OverloadDecl::function(
378                        format!("{}_{}_{}", base, name1, name2),
379                        vec![type1.clone(), type2.clone()],
380                        CelType::Dyn,
381                    )
382                    .with_impl(shared_impl),
383                );
384            }
385        }
386    }
387
388    // Ternary and higher arities (3-6 args) for common use cases
389    for arity in 3..=6 {
390        // All same type
391        for (suffix, cel_type) in [
392            ("int", CelType::Int),
393            ("uint", CelType::UInt),
394            ("double", CelType::Double),
395        ] {
396            decl = decl.with_overload(
397                OverloadDecl::function(
398                    format!("{}_{}{}", base, suffix, arity),
399                    vec![cel_type.clone(); arity],
400                    cel_type,
401                )
402                .with_impl(shared_impl),
403            );
404        }
405        // Mixed -> Dyn (just one overload for mixed types)
406        decl = decl.with_overload(
407            OverloadDecl::function(
408                format!("{}_dyn{}", base, arity),
409                vec![CelType::Dyn; arity],
410                CelType::Dyn,
411            )
412            .with_impl(shared_impl),
413        );
414    }
415
416    // List overloads
417    for (suffix, cel_type) in [
418        ("int", CelType::Int),
419        ("uint", CelType::UInt),
420        ("double", CelType::Double),
421    ] {
422        decl = decl.with_overload(
423            OverloadDecl::function(
424                format!("{}_list_{}", base, suffix),
425                vec![CelType::list(cel_type.clone())],
426                cel_type,
427            )
428            .with_impl(shared_impl),
429        );
430    }
431    decl = decl.with_overload(
432        OverloadDecl::function(
433            format!("{}_list_dyn", base),
434            vec![CelType::list(CelType::Dyn)],
435            CelType::Dyn,
436        )
437        .with_impl(shared_impl),
438    );
439
440    decl
441}
442
443fn add_bit_operations(funcs: &mut Vec<FunctionDecl>) {
444    // math.bitAnd
445    funcs.push(
446        FunctionDecl::new("math.bitAnd")
447            .with_overload(
448                OverloadDecl::function(
449                    "math_bitand_int_int",
450                    vec![CelType::Int, CelType::Int],
451                    CelType::Int,
452                )
453                .with_impl(|args| match (&args[0], &args[1]) {
454                    (Value::Int(a), Value::Int(b)) => Value::Int(a & b),
455                    _ => Value::error(EvalError::invalid_argument("expected int, int")),
456                }),
457            )
458            .with_overload(
459                OverloadDecl::function(
460                    "math_bitand_uint_uint",
461                    vec![CelType::UInt, CelType::UInt],
462                    CelType::UInt,
463                )
464                .with_impl(|args| match (&args[0], &args[1]) {
465                    (Value::UInt(a), Value::UInt(b)) => Value::UInt(a & b),
466                    _ => Value::error(EvalError::invalid_argument("expected uint, uint")),
467                }),
468            ),
469    );
470
471    // math.bitOr
472    funcs.push(
473        FunctionDecl::new("math.bitOr")
474            .with_overload(
475                OverloadDecl::function(
476                    "math_bitor_int_int",
477                    vec![CelType::Int, CelType::Int],
478                    CelType::Int,
479                )
480                .with_impl(|args| match (&args[0], &args[1]) {
481                    (Value::Int(a), Value::Int(b)) => Value::Int(a | b),
482                    _ => Value::error(EvalError::invalid_argument("expected int, int")),
483                }),
484            )
485            .with_overload(
486                OverloadDecl::function(
487                    "math_bitor_uint_uint",
488                    vec![CelType::UInt, CelType::UInt],
489                    CelType::UInt,
490                )
491                .with_impl(|args| match (&args[0], &args[1]) {
492                    (Value::UInt(a), Value::UInt(b)) => Value::UInt(a | b),
493                    _ => Value::error(EvalError::invalid_argument("expected uint, uint")),
494                }),
495            ),
496    );
497
498    // math.bitXor
499    funcs.push(
500        FunctionDecl::new("math.bitXor")
501            .with_overload(
502                OverloadDecl::function(
503                    "math_bitxor_int_int",
504                    vec![CelType::Int, CelType::Int],
505                    CelType::Int,
506                )
507                .with_impl(|args| match (&args[0], &args[1]) {
508                    (Value::Int(a), Value::Int(b)) => Value::Int(a ^ b),
509                    _ => Value::error(EvalError::invalid_argument("expected int, int")),
510                }),
511            )
512            .with_overload(
513                OverloadDecl::function(
514                    "math_bitxor_uint_uint",
515                    vec![CelType::UInt, CelType::UInt],
516                    CelType::UInt,
517                )
518                .with_impl(|args| match (&args[0], &args[1]) {
519                    (Value::UInt(a), Value::UInt(b)) => Value::UInt(a ^ b),
520                    _ => Value::error(EvalError::invalid_argument("expected uint, uint")),
521                }),
522            ),
523    );
524
525    // math.bitNot (unary)
526    funcs.push(
527        FunctionDecl::new("math.bitNot")
528            .with_overload(
529                OverloadDecl::function("math_bitnot_int", vec![CelType::Int], CelType::Int)
530                    .with_impl(|args| match &args[0] {
531                        Value::Int(a) => Value::Int(!a),
532                        _ => Value::error(EvalError::invalid_argument("expected int")),
533                    }),
534            )
535            .with_overload(
536                OverloadDecl::function("math_bitnot_uint", vec![CelType::UInt], CelType::UInt)
537                    .with_impl(|args| match &args[0] {
538                        Value::UInt(a) => Value::UInt(!a),
539                        _ => Value::error(EvalError::invalid_argument("expected uint")),
540                    }),
541            ),
542    );
543
544    // math.bitShiftLeft
545    funcs.push(
546        FunctionDecl::new("math.bitShiftLeft")
547            .with_overload(
548                OverloadDecl::function(
549                    "math_bitshiftleft_int_int",
550                    vec![CelType::Int, CelType::Int],
551                    CelType::Int,
552                )
553                .with_impl(|args| match (&args[0], &args[1]) {
554                    (Value::Int(a), Value::Int(b)) => {
555                        if *b < 0 {
556                            return Value::error(EvalError::invalid_argument(
557                                "math.bitShiftLeft: negative shift amount",
558                            ));
559                        }
560                        if *b >= 64 {
561                            return Value::Int(0);
562                        }
563                        Value::Int((*a as u64).wrapping_shl(*b as u32) as i64)
564                    }
565                    (Value::UInt(a), Value::Int(b)) => {
566                        if *b < 0 {
567                            return Value::error(EvalError::invalid_argument(
568                                "math.bitShiftLeft: negative shift amount",
569                            ));
570                        }
571                        if *b >= 64 {
572                            return Value::UInt(0);
573                        }
574                        Value::UInt(a.wrapping_shl(*b as u32))
575                    }
576                    _ => Value::error(EvalError::invalid_argument("expected int/uint, int")),
577                }),
578            )
579            .with_overload(
580                OverloadDecl::function(
581                    "math_bitshiftleft_uint_int",
582                    vec![CelType::UInt, CelType::Int],
583                    CelType::UInt,
584                )
585                .with_impl(|args| match (&args[0], &args[1]) {
586                    (Value::UInt(a), Value::Int(b)) => {
587                        if *b < 0 {
588                            return Value::error(EvalError::invalid_argument(
589                                "math.bitShiftLeft: negative shift amount",
590                            ));
591                        }
592                        if *b >= 64 {
593                            return Value::UInt(0);
594                        }
595                        Value::UInt(a.wrapping_shl(*b as u32))
596                    }
597                    _ => Value::error(EvalError::invalid_argument("expected uint, int")),
598                }),
599            ),
600    );
601
602    // math.bitShiftRight
603    funcs.push(
604        FunctionDecl::new("math.bitShiftRight")
605            .with_overload(
606                OverloadDecl::function(
607                    "math_bitshiftright_int_int",
608                    vec![CelType::Int, CelType::Int],
609                    CelType::Int,
610                )
611                .with_impl(|args| match (&args[0], &args[1]) {
612                    (Value::Int(a), Value::Int(b)) => {
613                        if *b < 0 {
614                            return Value::error(EvalError::invalid_argument(
615                                "math.bitShiftRight: negative shift amount",
616                            ));
617                        }
618                        if *b >= 64 {
619                            return Value::Int(0);
620                        }
621                        // Logical (unsigned) right shift per CEL spec
622                        Value::Int((*a as u64).wrapping_shr(*b as u32) as i64)
623                    }
624                    (Value::UInt(a), Value::Int(b)) => {
625                        if *b < 0 {
626                            return Value::error(EvalError::invalid_argument(
627                                "math.bitShiftRight: negative shift amount",
628                            ));
629                        }
630                        if *b >= 64 {
631                            return Value::UInt(0);
632                        }
633                        Value::UInt(a.wrapping_shr(*b as u32))
634                    }
635                    _ => Value::error(EvalError::invalid_argument("expected int/uint, int")),
636                }),
637            )
638            .with_overload(
639                OverloadDecl::function(
640                    "math_bitshiftright_uint_int",
641                    vec![CelType::UInt, CelType::Int],
642                    CelType::UInt,
643                )
644                .with_impl(|args| match (&args[0], &args[1]) {
645                    (Value::UInt(a), Value::Int(b)) => {
646                        if *b < 0 {
647                            return Value::error(EvalError::invalid_argument(
648                                "math.bitShiftRight: negative shift amount",
649                            ));
650                        }
651                        if *b >= 64 {
652                            return Value::UInt(0);
653                        }
654                        Value::UInt(a.wrapping_shr(*b as u32))
655                    }
656                    _ => Value::error(EvalError::invalid_argument("expected uint, int")),
657                }),
658            ),
659    );
660}
661
662#[cfg(test)]
663mod tests {
664    use super::*;
665
666    #[test]
667    fn test_math_extension_has_functions() {
668        let funcs = math_extension();
669        // Should have: greatest, least, ceil, floor, round, trunc, abs, sign,
670        // isNaN, isInf, isFinite, bitAnd, bitOr, bitXor, bitNot, bitShiftLeft, bitShiftRight
671        assert!(funcs.len() >= 17);
672    }
673
674    #[test]
675    fn test_math_greatest_overloads() {
676        let funcs = math_extension();
677        let greatest = funcs.iter().find(|f| f.name == "math.greatest").unwrap();
678
679        // Check unary overloads exist
680        assert!(greatest
681            .overloads
682            .iter()
683            .any(|o| o.id == "math_greatest_int" && o.params.len() == 1));
684        assert!(greatest
685            .overloads
686            .iter()
687            .any(|o| o.id == "math_greatest_double" && o.params.len() == 1));
688
689        // Check binary same-type overloads
690        assert!(greatest
691            .overloads
692            .iter()
693            .any(|o| o.id == "math_greatest_int_int" && o.params.len() == 2));
694
695        // Check ternary overloads
696        assert!(greatest
697            .overloads
698            .iter()
699            .any(|o| o.id == "math_greatest_int3" && o.params.len() == 3));
700
701        // Check list overloads
702        assert!(greatest
703            .overloads
704            .iter()
705            .any(|o| o.id == "math_greatest_list_int"));
706    }
707
708    #[test]
709    fn test_math_abs_overloads() {
710        let funcs = math_extension();
711        let abs = funcs.iter().find(|f| f.name == "math.abs").unwrap();
712        assert_eq!(abs.overloads.len(), 3); // int, uint, double
713    }
714
715    #[test]
716    fn test_bit_operations() {
717        let funcs = math_extension();
718
719        let bit_and = funcs.iter().find(|f| f.name == "math.bitAnd").unwrap();
720        assert_eq!(bit_and.overloads.len(), 2); // int, uint
721
722        let bit_not = funcs.iter().find(|f| f.name == "math.bitNot").unwrap();
723        assert_eq!(bit_not.overloads.len(), 2); // int, uint
724    }
725
726    #[test]
727    fn test_all_functions_are_standalone() {
728        let funcs = math_extension();
729        for func in &funcs {
730            for overload in &func.overloads {
731                assert!(
732                    !overload.is_member,
733                    "Expected {} to be standalone, but it's a member function",
734                    overload.id
735                );
736            }
737        }
738    }
739
740    #[test]
741    fn test_ceil_impl() {
742        let result = (math_extension()
743            .iter()
744            .find(|f| f.name == "math.ceil")
745            .unwrap()
746            .overloads[0]
747            .implementation
748            .as_ref()
749            .unwrap())(&[Value::Double(1.5)]);
750        assert_eq!(result, Value::Double(2.0));
751    }
752
753    #[test]
754    fn test_floor_impl() {
755        let result = (math_extension()
756            .iter()
757            .find(|f| f.name == "math.floor")
758            .unwrap()
759            .overloads[0]
760            .implementation
761            .as_ref()
762            .unwrap())(&[Value::Double(1.5)]);
763        assert_eq!(result, Value::Double(1.0));
764    }
765
766    #[test]
767    fn test_round_half_away_from_zero() {
768        let round_fn = math_extension()
769            .iter()
770            .find(|f| f.name == "math.round")
771            .unwrap()
772            .overloads[0]
773            .implementation
774            .clone()
775            .unwrap();
776
777        assert_eq!(round_fn(&[Value::Double(2.5)]), Value::Double(3.0));
778        assert_eq!(round_fn(&[Value::Double(-2.5)]), Value::Double(-3.0));
779        assert_eq!(round_fn(&[Value::Double(1.4)]), Value::Double(1.0));
780        assert_eq!(round_fn(&[Value::Double(-1.4)]), Value::Double(-1.0));
781    }
782
783    #[test]
784    fn test_abs_overflow() {
785        let abs_fn = math_extension()
786            .iter()
787            .find(|f| f.name == "math.abs")
788            .unwrap()
789            .overloads
790            .iter()
791            .find(|o| o.id == "math_abs_int")
792            .unwrap()
793            .implementation
794            .clone()
795            .unwrap();
796
797        // Normal case
798        assert_eq!(abs_fn(&[Value::Int(-5)]), Value::Int(5));
799        // Overflow case
800        assert!(matches!(abs_fn(&[Value::Int(i64::MIN)]), Value::Error(_)));
801    }
802
803    #[test]
804    fn test_greatest_basic() {
805        assert_eq!(
806            greatest_impl(&[Value::Int(1), Value::Int(3), Value::Int(2)]),
807            Value::Int(3)
808        );
809        assert_eq!(
810            greatest_impl(&[Value::Double(1.5), Value::Double(2.5)]),
811            Value::Double(2.5)
812        );
813    }
814
815    #[test]
816    fn test_least_basic() {
817        assert_eq!(
818            least_impl(&[Value::Int(1), Value::Int(3), Value::Int(2)]),
819            Value::Int(1)
820        );
821        assert_eq!(
822            least_impl(&[Value::UInt(5), Value::UInt(2)]),
823            Value::UInt(2)
824        );
825    }
826
827    #[test]
828    fn test_greatest_mixed_types() {
829        assert_eq!(
830            greatest_impl(&[Value::Int(1), Value::UInt(5)]),
831            Value::UInt(5)
832        );
833        assert_eq!(
834            greatest_impl(&[Value::Int(-1), Value::UInt(0)]),
835            Value::UInt(0)
836        );
837    }
838
839    #[test]
840    fn test_greatest_empty_list() {
841        use std::sync::Arc;
842        let empty: Arc<[Value]> = Arc::from(vec![]);
843        assert!(matches!(
844            greatest_impl(&[Value::List(empty)]),
845            Value::Error(_)
846        ));
847    }
848
849    #[test]
850    fn test_bitshift_negative() {
851        let shift_fn = math_extension()
852            .iter()
853            .find(|f| f.name == "math.bitShiftLeft")
854            .unwrap()
855            .overloads
856            .iter()
857            .find(|o| o.id == "math_bitshiftleft_int_int")
858            .unwrap()
859            .implementation
860            .clone()
861            .unwrap();
862
863        assert!(matches!(
864            shift_fn(&[Value::Int(1), Value::Int(-1)]),
865            Value::Error(_)
866        ));
867    }
868}