blots_core/
functions.rs

1use crate::{
2    expressions::evaluate_ast,
3    heap::{Heap, HeapPointer, IterablePointer},
4    values::{FunctionArity, LambdaArg, LambdaDef, ReifiedValue, Value},
5};
6use anyhow::{Result, anyhow};
7use dyn_fmt::AsStrFormatExt;
8use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::LazyLock};
9
10#[cfg(not(target_arch = "wasm32"))]
11use std::sync::Mutex;
12
13#[cfg(not(target_arch = "wasm32"))]
14use crate::stats::FunctionCallStats;
15
16#[cfg(not(target_arch = "wasm32"))]
17pub static FUNCTION_CALLS: LazyLock<Mutex<Vec<FunctionCallStats>>> =
18    LazyLock::new(|| Mutex::new(Vec::new()));
19
20// Cache built-in function names since they're static
21pub static BUILTIN_FUNCTION_NAMES: LazyLock<Vec<&'static str>> =
22    LazyLock::new(BuiltInFunction::all_names);
23
24#[derive(
25    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
26)]
27pub enum BuiltInFunction {
28    // Math functions
29    Sqrt,
30    Sin,
31    Cos,
32    Tan,
33    Asin,
34    Acos,
35    Atan,
36    Log,
37    Log10,
38    Exp,
39    Abs,
40    Floor,
41    Ceil,
42    Round,
43    Trunc,
44
45    // Aggregate functions
46    Min,
47    Max,
48    Avg,
49    Sum,
50    Prod,
51    Median,
52    Percentile,
53
54    // List functions
55    Range,
56    Len,
57    Head,
58    Tail,
59    Slice,
60    Concat,
61    Dot,
62    Unique,
63    Sort,
64    SortBy,
65    Reverse,
66
67    // Higher-order functions
68    Map,
69    Reduce,
70    Filter,
71    Every,
72    Some,
73
74    // String functions
75    Split,
76    Join,
77    Replace,
78    Trim,
79    Uppercase,
80    Lowercase,
81    ToString,
82    ToNumber,
83    Includes,
84    Format,
85
86    // Type functions
87    Typeof,
88    Arity,
89
90    // Record functions
91    Keys,
92    Values,
93    Entries,
94
95    // Platform-specific functions
96    #[cfg(not(target_arch = "wasm32"))]
97    Print,
98    #[cfg(not(target_arch = "wasm32"))]
99    TimeNow,
100}
101
102#[derive(Debug, Clone)]
103pub enum FunctionDef {
104    BuiltIn(BuiltInFunction),
105    Lambda(LambdaDef),
106}
107
108impl BuiltInFunction {
109    pub fn from_ident(ident: &str) -> Option<Self> {
110        match ident {
111            "sqrt" => Some(Self::Sqrt),
112            "sin" => Some(Self::Sin),
113            "cos" => Some(Self::Cos),
114            "tan" => Some(Self::Tan),
115            "asin" => Some(Self::Asin),
116            "acos" => Some(Self::Acos),
117            "atan" => Some(Self::Atan),
118            "log" => Some(Self::Log),
119            "log10" => Some(Self::Log10),
120            "exp" => Some(Self::Exp),
121            "abs" => Some(Self::Abs),
122            "floor" => Some(Self::Floor),
123            "ceil" => Some(Self::Ceil),
124            "round" => Some(Self::Round),
125            "trunc" => Some(Self::Trunc),
126            "min" => Some(Self::Min),
127            "max" => Some(Self::Max),
128            "avg" => Some(Self::Avg),
129            "sum" => Some(Self::Sum),
130            "prod" => Some(Self::Prod),
131            "median" => Some(Self::Median),
132            "percentile" => Some(Self::Percentile),
133            "range" => Some(Self::Range),
134            "len" => Some(Self::Len),
135            "head" => Some(Self::Head),
136            "tail" => Some(Self::Tail),
137            "slice" => Some(Self::Slice),
138            "concat" => Some(Self::Concat),
139            "dot" => Some(Self::Dot),
140            "unique" => Some(Self::Unique),
141            "sort" => Some(Self::Sort),
142            "sort_by" => Some(Self::SortBy),
143            "reverse" => Some(Self::Reverse),
144            "map" => Some(Self::Map),
145            "reduce" => Some(Self::Reduce),
146            "filter" => Some(Self::Filter),
147            "every" => Some(Self::Every),
148            "some" => Some(Self::Some),
149            "split" => Some(Self::Split),
150            "join" => Some(Self::Join),
151            "replace" => Some(Self::Replace),
152            "trim" => Some(Self::Trim),
153            "uppercase" => Some(Self::Uppercase),
154            "lowercase" => Some(Self::Lowercase),
155            "to_string" => Some(Self::ToString),
156            "to_number" => Some(Self::ToNumber),
157            "includes" => Some(Self::Includes),
158            "format" => Some(Self::Format),
159            "typeof" => Some(Self::Typeof),
160            "arity" => Some(Self::Arity),
161            "keys" => Some(Self::Keys),
162            "values" => Some(Self::Values),
163            "entries" => Some(Self::Entries),
164            #[cfg(not(target_arch = "wasm32"))]
165            "print" => Some(Self::Print),
166            #[cfg(not(target_arch = "wasm32"))]
167            "time_now" => Some(Self::TimeNow),
168            _ => None,
169        }
170    }
171
172    pub fn name(&self) -> &'static str {
173        match self {
174            Self::Sqrt => "sqrt",
175            Self::Sin => "sin",
176            Self::Cos => "cos",
177            Self::Tan => "tan",
178            Self::Asin => "asin",
179            Self::Acos => "acos",
180            Self::Atan => "atan",
181            Self::Log => "log",
182            Self::Log10 => "log10",
183            Self::Exp => "exp",
184            Self::Abs => "abs",
185            Self::Floor => "floor",
186            Self::Ceil => "ceil",
187            Self::Round => "round",
188            Self::Trunc => "trunc",
189            Self::Min => "min",
190            Self::Max => "max",
191            Self::Avg => "avg",
192            Self::Sum => "sum",
193            Self::Prod => "prod",
194            Self::Median => "median",
195            Self::Percentile => "percentile",
196            Self::Range => "range",
197            Self::Len => "len",
198            Self::Head => "head",
199            Self::Tail => "tail",
200            Self::Slice => "slice",
201            Self::Concat => "concat",
202            Self::Dot => "dot",
203            Self::Unique => "unique",
204            Self::Sort => "sort",
205            Self::SortBy => "sort_by",
206            Self::Reverse => "reverse",
207            Self::Map => "map",
208            Self::Reduce => "reduce",
209            Self::Filter => "filter",
210            Self::Every => "every",
211            Self::Some => "some",
212            Self::Split => "split",
213            Self::Join => "join",
214            Self::Replace => "replace",
215            Self::Trim => "trim",
216            Self::Uppercase => "uppercase",
217            Self::Lowercase => "lowercase",
218            Self::ToString => "to_string",
219            Self::ToNumber => "to_number",
220            Self::Includes => "includes",
221            Self::Format => "format",
222            Self::Typeof => "typeof",
223            Self::Arity => "arity",
224            Self::Keys => "keys",
225            Self::Values => "values",
226            Self::Entries => "entries",
227            #[cfg(not(target_arch = "wasm32"))]
228            Self::Print => "print",
229            #[cfg(not(target_arch = "wasm32"))]
230            Self::TimeNow => "time_now",
231        }
232    }
233
234    pub fn arity(&self) -> FunctionArity {
235        match self {
236            // Math functions - single argument
237            Self::Sqrt
238            | Self::Sin
239            | Self::Cos
240            | Self::Tan
241            | Self::Asin
242            | Self::Acos
243            | Self::Atan
244            | Self::Log
245            | Self::Log10
246            | Self::Exp
247            | Self::Abs
248            | Self::Floor
249            | Self::Ceil
250            | Self::Trunc => FunctionArity::Exact(1),
251
252            // Round can take 1 or 2 arguments
253            Self::Round => FunctionArity::Between(1, 2),
254
255            // Aggregate functions
256            Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
257                FunctionArity::AtLeast(1)
258            }
259
260            // Range can take 1 or 2 arguments
261            Self::Range => FunctionArity::Between(1, 2),
262
263            // List functions
264            Self::Len | Self::Head | Self::Tail | Self::Unique | Self::Sort | Self::Reverse => {
265                FunctionArity::Exact(1)
266            }
267            Self::Slice => FunctionArity::Exact(3),
268            Self::Concat => FunctionArity::AtLeast(2),
269            Self::Dot | Self::Percentile => FunctionArity::Exact(2),
270
271            // Higher-order functions
272            Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
273                FunctionArity::Exact(2)
274            }
275            Self::Reduce => FunctionArity::Exact(3),
276
277            // String functions
278            Self::Split | Self::Join | Self::Includes => FunctionArity::Exact(2),
279            Self::Replace => FunctionArity::Exact(3),
280            Self::Trim | Self::Uppercase | Self::Lowercase | Self::ToString | Self::ToNumber => {
281                FunctionArity::Exact(1)
282            }
283            Self::Format => FunctionArity::AtLeast(1),
284
285            // Type functions
286            Self::Typeof | Self::Arity => FunctionArity::Exact(1),
287
288            // Record functions
289            Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
290
291            // Platform-specific functions
292            #[cfg(not(target_arch = "wasm32"))]
293            Self::Print => FunctionArity::AtLeast(1),
294            #[cfg(not(target_arch = "wasm32"))]
295            Self::TimeNow => FunctionArity::Exact(0),
296        }
297    }
298
299    pub fn call(
300        &self,
301        args: Vec<Value>,
302        heap: Rc<RefCell<Heap>>,
303        bindings: Rc<RefCell<HashMap<String, Value>>>,
304        call_depth: usize,
305    ) -> Result<Value> {
306        match self {
307            // Math functions
308            Self::Sqrt => Ok(Value::Number(args[0].as_number()?.sqrt())),
309            Self::Sin => Ok(Value::Number(args[0].as_number()?.sin())),
310            Self::Cos => Ok(Value::Number(args[0].as_number()?.cos())),
311            Self::Tan => Ok(Value::Number(args[0].as_number()?.tan())),
312            Self::Asin => Ok(Value::Number(args[0].as_number()?.asin())),
313            Self::Acos => Ok(Value::Number(args[0].as_number()?.acos())),
314            Self::Atan => Ok(Value::Number(args[0].as_number()?.atan())),
315            Self::Log => Ok(Value::Number(args[0].as_number()?.ln())),
316            Self::Log10 => Ok(Value::Number(args[0].as_number()?.log10())),
317            Self::Exp => Ok(Value::Number(args[0].as_number()?.exp())),
318            Self::Abs => Ok(Value::Number(args[0].as_number()?.abs())),
319            Self::Floor => Ok(Value::Number(args[0].as_number()?.floor())),
320            Self::Ceil => Ok(Value::Number(args[0].as_number()?.ceil())),
321            Self::Trunc => Ok(Value::Number(args[0].as_number()?.trunc())),
322
323            Self::Round => {
324                let num = args[0].as_number()?;
325                if args.len() == 1 {
326                    Ok(Value::Number(num.round()))
327                } else {
328                    let decimal_places = args[1].as_number()? as i32;
329                    let multiplier = 10_f64.powi(decimal_places);
330                    Ok(Value::Number((num * multiplier).round() / multiplier))
331                }
332            }
333
334            // Aggregate functions
335            Self::Min => {
336                let nums = if args.len() == 1 {
337                    // Check if single argument is a list
338                    match &args[0] {
339                        Value::List(_) => {
340                            let borrowed_heap = heap.borrow();
341                            let list = args[0].as_list(&borrowed_heap)?;
342                            list.iter()
343                                .map(|a| a.as_number())
344                                .collect::<Result<Vec<f64>>>()?
345                        }
346                        _ => vec![args[0].as_number()?],
347                    }
348                } else {
349                    args.iter()
350                        .map(|a| a.as_number())
351                        .collect::<Result<Vec<f64>>>()?
352                };
353
354                if nums.is_empty() {
355                    return Err(anyhow!("min requires at least one number"));
356                }
357
358                Ok(Value::Number(
359                    nums.iter().copied().fold(f64::INFINITY, f64::min),
360                ))
361            }
362
363            Self::Max => {
364                let nums = if args.len() == 1 {
365                    // Check if single argument is a list
366                    match &args[0] {
367                        Value::List(_) => {
368                            let borrowed_heap = heap.borrow();
369                            let list = args[0].as_list(&borrowed_heap)?;
370                            list.iter()
371                                .map(|a| a.as_number())
372                                .collect::<Result<Vec<f64>>>()?
373                        }
374                        _ => vec![args[0].as_number()?],
375                    }
376                } else {
377                    args.iter()
378                        .map(|a| a.as_number())
379                        .collect::<Result<Vec<f64>>>()?
380                };
381
382                if nums.is_empty() {
383                    return Err(anyhow!("max requires at least one number"));
384                }
385
386                Ok(Value::Number(
387                    nums.iter().copied().fold(f64::NEG_INFINITY, f64::max),
388                ))
389            }
390
391            Self::Avg => {
392                let nums = if args.len() == 1 {
393                    match &args[0] {
394                        Value::List(_) => {
395                            let borrowed_heap = heap.borrow();
396                            let list = args[0].as_list(&borrowed_heap)?;
397                            list.iter()
398                                .map(|a| a.as_number())
399                                .collect::<Result<Vec<f64>>>()?
400                        }
401                        _ => vec![args[0].as_number()?],
402                    }
403                } else {
404                    args.iter()
405                        .map(|a| a.as_number())
406                        .collect::<Result<Vec<f64>>>()?
407                };
408                if nums.is_empty() {
409                    return Err(anyhow!("avg requires at least one number"));
410                }
411                Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
412            }
413
414            Self::Prod => {
415                let nums = if args.len() == 1 {
416                    match &args[0] {
417                        Value::List(_) => {
418                            let borrowed_heap = heap.borrow();
419                            let list = args[0].as_list(&borrowed_heap)?;
420                            list.iter()
421                                .map(|a| a.as_number())
422                                .collect::<Result<Vec<f64>>>()?
423                        }
424                        _ => vec![args[0].as_number()?],
425                    }
426                } else {
427                    args.iter()
428                        .map(|a| a.as_number())
429                        .collect::<Result<Vec<f64>>>()?
430                };
431                if nums.is_empty() {
432                    return Err(anyhow!("prod requires at least one number"));
433                }
434                Ok(Value::Number(nums.iter().product()))
435            }
436
437            Self::Range => {
438                let (start, end) = match args[..] {
439                    [Value::Number(start)] => (0.0, start),
440                    [Value::Number(start), Value::Number(end)] => (start, end),
441                    _ => return Err(anyhow!("range requires 1 or 2 numbers")),
442                };
443
444                if start > end {
445                    return Err(anyhow!(
446                        "range requires start to be less than or equal to end"
447                    ));
448                }
449
450                if !f64::is_finite(start) || !f64::is_finite(end) {
451                    return Err(anyhow!("range requires finite numbers"));
452                }
453
454                let start_i64 = start as i64;
455                let end_i64 = end as i64;
456                let length = end_i64 - start_i64;
457
458                if length > u32::MAX as i64 {
459                    return Err(anyhow!(
460                        "list would be longer than the maximum length of {}",
461                        u32::MAX
462                    ));
463                }
464
465                let values = (start_i64..end_i64)
466                    .map(|e| Value::Number(e as f64))
467                    .collect();
468                let list = heap.borrow_mut().insert_list(values);
469
470                Ok(list)
471            }
472
473            Self::Sum => {
474                let nums = if args.len() == 1 {
475                    match &args[0] {
476                        Value::List(_) => {
477                            let borrowed_heap = heap.borrow();
478                            let list = args[0].as_list(&borrowed_heap)?;
479                            list.iter()
480                                .map(|a| a.as_number())
481                                .collect::<Result<Vec<f64>>>()?
482                        }
483                        _ => vec![args[0].as_number()?],
484                    }
485                } else {
486                    args.iter()
487                        .map(|a| a.as_number())
488                        .collect::<Result<Vec<f64>>>()?
489                };
490                if nums.is_empty() {
491                    return Err(anyhow!("sum requires at least one number"));
492                }
493                Ok(Value::Number(nums.iter().sum()))
494            }
495
496            // Remaining aggregate functions
497            Self::Median => {
498                let mut nums = if args.len() == 1 {
499                    match &args[0] {
500                        Value::List(_) => {
501                            let borrowed_heap = heap.borrow();
502                            let list = args[0].as_list(&borrowed_heap)?;
503                            list.iter()
504                                .map(|a| a.as_number())
505                                .collect::<Result<Vec<f64>>>()?
506                        }
507                        _ => vec![args[0].as_number()?],
508                    }
509                } else {
510                    args.into_iter()
511                        .map(|a| a.as_number())
512                        .collect::<Result<Vec<f64>>>()?
513                };
514
515                if nums.is_empty() {
516                    return Err(anyhow!("median requires at least one number"));
517                }
518
519                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
520                let len = nums.len();
521                if len % 2 == 0 {
522                    Ok(Value::Number((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
523                } else {
524                    Ok(Value::Number(nums[len / 2]))
525                }
526            }
527
528            Self::Percentile => {
529                let p = args[1].as_number()?;
530                let heap = &heap.borrow();
531                let list = args[0].as_list(heap)?;
532
533                if !(0.0..=100.0).contains(&p) {
534                    return Err(anyhow!("percentile must be between 0 and 100"));
535                }
536
537                let mut nums = list
538                    .iter()
539                    .map(|a| a.as_number())
540                    .collect::<Result<Vec<f64>>>()?;
541
542                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
543                let index = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
544
545                Ok(Value::Number(nums[index]))
546            }
547
548            // List functions
549            Self::Len => match &args[0] {
550                Value::List(l) => Ok(Value::Number(
551                    l.reify(&heap.borrow()).as_list()?.len() as f64
552                )),
553                Value::String(s) => Ok(Value::Number(
554                    s.reify(&heap.borrow()).as_string()?.len() as f64
555                )),
556                _ => Err(anyhow!("argument must be a list or string")),
557            },
558
559            Self::Head => match &args[0] {
560                Value::List(p) => Ok(p
561                    .reify(&heap.borrow())
562                    .as_list()?
563                    .first()
564                    .copied()
565                    .unwrap_or(Value::Null)),
566                Value::String(p) => {
567                    let val = {
568                        p.reify(&heap.borrow())
569                            .as_string()?
570                            .get(0..1)
571                            .unwrap_or("")
572                            .to_string()
573                    };
574
575                    Ok(heap.borrow_mut().insert_string(val))
576                }
577                _ => Err(anyhow!("argument must be a list or string")),
578            },
579
580            Self::Tail => match &args[0] {
581                Value::List(p) => {
582                    let val = {
583                        p.reify(&heap.borrow())
584                            .as_list()?
585                            .get(1..)
586                            .unwrap_or([].as_slice())
587                            .to_vec()
588                    };
589
590                    Ok(heap.borrow_mut().insert_list(val))
591                }
592                Value::String(s) => {
593                    let val = {
594                        s.reify(&heap.borrow())
595                            .as_string()?
596                            .get(1..)
597                            .unwrap_or("")
598                            .to_string()
599                    };
600
601                    Ok(heap.borrow_mut().insert_string(val))
602                }
603                _ => Err(anyhow!("argument must be a list or string")),
604            },
605
606            Self::Slice => {
607                let start = args[1].as_number()? as usize;
608                let end = args[2].as_number()? as usize;
609
610                match args[0] {
611                    Value::List(_) => {
612                        let l = {
613                            let borrowed_heap = &heap.borrow();
614                            args[0].as_list(borrowed_heap)?.clone()
615                        };
616
617                        l.get(start..end)
618                            .map_or(Err(anyhow!("index out of bounds")), |l| {
619                                Ok(heap.borrow_mut().insert_list(l.to_vec()))
620                            })
621                    }
622                    Value::String(_) => {
623                        let s = {
624                            let borrowed_heap = &heap.borrow();
625                            args[0].as_string(borrowed_heap)?.to_string()
626                        };
627
628                        s.get(start..end)
629                            .map_or(Err(anyhow!("index out of bounds")), |s| {
630                                Ok(heap.borrow_mut().insert_string(s.to_string()))
631                            })
632                    }
633                    _ => Err(anyhow!("argument must be a list or string")),
634                }
635            }
636
637            Self::Concat => {
638                let mut list = vec![];
639
640                for arg in args {
641                    match arg {
642                        Value::List(p) => list.extend(p.reify(&heap.borrow()).as_list()?.clone()),
643                        Value::Spread(IterablePointer::List(p)) => {
644                            list.extend(p.reify(&heap.borrow()).as_list()?.clone())
645                        }
646                        Value::Spread(IterablePointer::String(p)) => {
647                            let string = {
648                                let borrowed_heap = &heap.borrow();
649                                p.reify(borrowed_heap).as_string()?.to_string()
650                            };
651
652                            list.extend(
653                                string
654                                    .chars()
655                                    .map(|c| heap.borrow_mut().insert_string(c.to_string())),
656                            );
657                        }
658                        _ => list.push(arg),
659                    }
660                }
661
662                Ok(heap.borrow_mut().insert_list(list))
663            }
664
665            Self::Dot => {
666                let (a, b) = {
667                    let borrowed_heap = &heap.borrow();
668                    (
669                        args[0].as_list(borrowed_heap)?.clone(),
670                        args[1].as_list(borrowed_heap)?.clone(),
671                    )
672                };
673
674                if a.len() != b.len() {
675                    return Err(anyhow!(
676                        "cannot calculate dot product of lists with different lengths"
677                    ));
678                }
679
680                Ok(Value::Number(
681                    a.iter()
682                        .zip(b.iter())
683                        .map(|(a, b)| {
684                            let a_num = a.as_number()?;
685                            let b_num = b.as_number()?;
686                            Ok(a_num * b_num)
687                        })
688                        .collect::<Result<Vec<f64>>>()?
689                        .iter()
690                        .sum(),
691                ))
692            }
693
694            Self::Unique => {
695                let list = {
696                    let borrowed_heap = &heap.borrow();
697                    args[0].as_list(borrowed_heap)?.clone()
698                };
699                let mut unique_list = vec![];
700                let borrowed_heap = heap.borrow();
701
702                for item in list {
703                    let mut is_duplicate = false;
704                    for existing in &unique_list {
705                        if item.equals(existing, &borrowed_heap)? {
706                            is_duplicate = true;
707                            break;
708                        }
709                    }
710                    if !is_duplicate {
711                        unique_list.push(item);
712                    }
713                }
714
715                drop(borrowed_heap);
716                Ok(heap.borrow_mut().insert_list(unique_list))
717            }
718
719            Self::Sort => {
720                let mut list = {
721                    let borrowed_heap = &heap.borrow();
722                    args[0].as_list(borrowed_heap)?.clone()
723                };
724                let borrowed_heap = heap.borrow();
725                list.sort_by(|a, b| {
726                    a.compare(b, &borrowed_heap)
727                        .unwrap_or(None)
728                        .unwrap_or(std::cmp::Ordering::Equal)
729                });
730                drop(borrowed_heap);
731                Ok(heap.borrow_mut().insert_list(list))
732            }
733
734            Self::Reverse => {
735                let mut list = { args[0].as_list(&heap.borrow())?.clone() };
736                list.reverse();
737                Ok(heap.borrow_mut().insert_list(list))
738            }
739
740            // String functions
741            Self::Split => {
742                let (s, delimeter) = {
743                    let borrowed_heap = &heap.borrow();
744                    (
745                        args[0].as_string(borrowed_heap)?.to_string(),
746                        args[1].as_string(borrowed_heap)?.to_string(),
747                    )
748                };
749
750                let list = s
751                    .split(&delimeter)
752                    .map(|s| heap.borrow_mut().insert_string(s.to_string()))
753                    .collect();
754
755                Ok(heap.borrow_mut().insert_list(list))
756            }
757
758            Self::Join => {
759                let joined_string = {
760                    let borrowed_heap = &heap.borrow();
761                    let delimeter = args[1].as_string(borrowed_heap)?;
762                    let list = args[0].as_list(borrowed_heap)?;
763                    list.iter()
764                        .map(|v| v.stringify_internal(borrowed_heap))
765                        .collect::<Vec<String>>()
766                        .join(delimeter)
767                };
768
769                Ok(heap.borrow_mut().insert_string(joined_string))
770            }
771
772            Self::Replace => {
773                let string = {
774                    let borrowed_heap = heap.borrow();
775                    let old = args[1].as_string(&borrowed_heap)?.to_string();
776                    let new = args[2].as_string(&borrowed_heap)?.to_string();
777                    let s = args[0].as_string(&borrowed_heap)?.to_string();
778                    s.replace(&old, &new)
779                };
780
781                Ok(heap.borrow_mut().insert_string(string))
782            }
783
784            Self::Trim => {
785                let string = {
786                    let borrowed_heap = heap.borrow();
787                    args[0].as_string(&borrowed_heap)?.trim().to_string()
788                };
789
790                Ok(heap.borrow_mut().insert_string(string))
791            }
792
793            Self::Uppercase => {
794                let string = args[0].as_string(&heap.borrow())?.to_uppercase();
795                Ok(heap.borrow_mut().insert_string(string))
796            }
797
798            Self::Lowercase => {
799                let string = args[0].as_string(&heap.borrow())?.to_lowercase();
800                Ok(heap.borrow_mut().insert_string(string))
801            }
802
803            Self::ToString => {
804                let string = args[0].stringify_internal(&heap.borrow());
805                Ok(heap.borrow_mut().insert_string(string))
806            }
807
808            Self::ToNumber => Ok(Value::Number(args[0].as_string(&heap.borrow())?.parse()?)),
809
810            Self::Includes => {
811                let needle = {
812                    let borrowed_heap = &heap.borrow();
813                    args[1].as_string(borrowed_heap)?.to_string()
814                };
815
816                match &args[0].reify(&heap.borrow())? {
817                    ReifiedValue::List(l, _) => Ok(Value::Bool(
818                        (*l).iter()
819                            .any(|v| v.as_string(&heap.borrow()).unwrap().eq(&needle)),
820                    )),
821                    ReifiedValue::String(s, _) => Ok(Value::Bool(s.contains(&needle))),
822                    _ => Err(anyhow!("second argument must be a list or string")),
823                }
824            }
825
826            Self::Format => {
827                let format_str = {
828                    let borrowed_heap = &heap.borrow();
829                    args[0]
830                        .as_string(borrowed_heap)
831                        .map_err(|_| anyhow!("first argument must be a string"))?
832                        .to_string()
833                };
834
835                let format_args = {
836                    let borrowed_heap = &heap.borrow();
837                    &args[1..]
838                        .iter()
839                        .map(|v| v.stringify_internal(borrowed_heap))
840                        .collect::<Vec<String>>()
841                };
842
843                Ok(heap
844                    .borrow_mut()
845                    .insert_string(format_str.format(format_args)))
846            }
847
848            // Type functions
849            Self::Typeof => Ok(heap
850                .borrow_mut()
851                .insert_string(args[0].get_type().to_string())),
852
853            Self::Arity => {
854                let arity = match args[0] {
855                    Value::Lambda(p) => p.reify(&heap.borrow()).as_lambda()?.get_arity(),
856                    Value::BuiltIn(built_in) => built_in.arity(),
857                    _ => return Err(anyhow!("argument must be a function or built-in function")),
858                };
859
860                Ok(Value::Number(match arity {
861                    FunctionArity::Exact(n) => n as f64,
862                    FunctionArity::AtLeast(n) => n as f64,
863                    FunctionArity::Between(min, _max) => min as f64,
864                }))
865            }
866
867            // Record functions
868            Self::Keys => {
869                let record = args[0].as_record(&heap.borrow())?.clone();
870                let keys = {
871                    let key_strings = record.keys().cloned().collect::<Vec<String>>();
872                    key_strings
873                        .iter()
874                        .map(|k| heap.borrow_mut().insert_string(k.to_string()))
875                        .collect()
876                };
877
878                Ok(heap.borrow_mut().insert_list(keys))
879            }
880
881            Self::Values => {
882                let record = args[0].as_record(&heap.borrow())?.clone();
883                let values = record.values().cloned().collect();
884
885                Ok(heap.borrow_mut().insert_list(values))
886            }
887
888            Self::Entries => {
889                let record = args[0].as_record(&heap.borrow())?.clone();
890                let entries = record
891                    .iter()
892                    .map(|(k, v)| {
893                        let entry = {
894                            let mut borrowed_heap = heap.borrow_mut();
895                            vec![borrowed_heap.insert_string(k.to_string()), *v]
896                        };
897                        heap.borrow_mut().insert_list(entry)
898                    })
899                    .collect();
900
901                Ok(heap.borrow_mut().insert_list(entries))
902            }
903
904            // Platform-specific functions
905            #[cfg(not(target_arch = "wasm32"))]
906            Self::Print => {
907                let borrowed_heap = &heap.borrow();
908
909                let output = if args.len() == 1 {
910                    args[0].stringify_internal(borrowed_heap)
911                } else {
912                    let format_str = args[0].as_string(borrowed_heap).map_err(|_| {
913                        anyhow!("first argument must be a formatting string if multiple arguments are given")
914                    })?;
915                    let format_args = &args[1..]
916                        .iter()
917                        .map(|v| v.stringify_internal(borrowed_heap))
918                        .collect::<Vec<String>>();
919                    format_str.format(format_args)
920                };
921
922                // Print to stderr, to not pollute stdout
923                eprintln!("{}", output);
924
925                Ok(Value::Null)
926            }
927
928            #[cfg(not(target_arch = "wasm32"))]
929            Self::TimeNow => Ok(Value::Number(
930                std::time::SystemTime::now()
931                    .duration_since(std::time::UNIX_EPOCH)
932                    .unwrap()
933                    .as_secs_f64(),
934            )),
935
936            // Higher-order functions
937            Self::Map => {
938                let func = &args[1];
939                let list = {
940                    let borrowed_heap = &heap.borrow();
941                    let list = args[0].as_list(borrowed_heap)?.clone();
942                    let func_def = get_function_def(func, borrowed_heap)
943                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
944                    drop(func_def);
945                    list
946                };
947
948                let mut mapped_list = vec![];
949                for item in list {
950                    let func_def = get_function_def(func, &heap.borrow())
951                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
952                    let result = func_def.call(
953                        Value::Null,
954                        vec![item],
955                        Rc::clone(&heap),
956                        Rc::clone(&bindings),
957                        call_depth + 1,
958                    )?;
959                    mapped_list.push(result);
960                }
961
962                Ok(heap.borrow_mut().insert_list(mapped_list))
963            }
964
965            Self::Filter => {
966                let func = &args[1];
967                let list = {
968                    let borrowed_heap = &heap.borrow();
969                    args[0].as_list(borrowed_heap)?.clone()
970                };
971
972                let mut filtered_list = vec![];
973                for item in list {
974                    let func_def = get_function_def(func, &heap.borrow())
975                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
976                    let result = func_def.call(
977                        Value::Null,
978                        vec![item],
979                        Rc::clone(&heap),
980                        Rc::clone(&bindings),
981                        call_depth + 1,
982                    )?;
983                    if result.as_bool()? {
984                        filtered_list.push(item);
985                    }
986                }
987
988                Ok(heap.borrow_mut().insert_list(filtered_list))
989            }
990
991            Self::Reduce => {
992                let func = &args[1];
993                let initial = args[2];
994                let list = {
995                    let borrowed_heap = &heap.borrow();
996                    args[0].as_list(borrowed_heap)?.clone()
997                };
998
999                let mut accumulator = initial;
1000                for item in list {
1001                    let func_def = get_function_def(func, &heap.borrow())
1002                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1003                    accumulator = func_def.call(
1004                        Value::Null,
1005                        vec![accumulator, item],
1006                        Rc::clone(&heap),
1007                        Rc::clone(&bindings),
1008                        call_depth + 1,
1009                    )?;
1010                }
1011
1012                Ok(accumulator)
1013            }
1014
1015            Self::Every => {
1016                let func = &args[1];
1017                let list = {
1018                    let borrowed_heap = &heap.borrow();
1019                    args[0].as_list(borrowed_heap)?.clone()
1020                };
1021
1022                for item in list {
1023                    let func_def = get_function_def(func, &heap.borrow())
1024                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1025                    let result = func_def.call(
1026                        Value::Null,
1027                        vec![item],
1028                        Rc::clone(&heap),
1029                        Rc::clone(&bindings),
1030                        call_depth + 1,
1031                    )?;
1032                    if !result.as_bool()? {
1033                        return Ok(Value::Bool(false));
1034                    }
1035                }
1036
1037                Ok(Value::Bool(true))
1038            }
1039
1040            Self::Some => {
1041                let func = &args[1];
1042                let list = {
1043                    let borrowed_heap = &heap.borrow();
1044                    args[0].as_list(borrowed_heap)?.clone()
1045                };
1046
1047                for item in list {
1048                    let func_def = get_function_def(func, &heap.borrow())
1049                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1050                    let result = func_def.call(
1051                        Value::Null,
1052                        vec![item],
1053                        Rc::clone(&heap),
1054                        Rc::clone(&bindings),
1055                        call_depth + 1,
1056                    )?;
1057                    if result.as_bool()? {
1058                        return Ok(Value::Bool(true));
1059                    }
1060                }
1061
1062                Ok(Value::Bool(false))
1063            }
1064
1065            Self::SortBy => {
1066                let func = &args[1];
1067                let mut list = {
1068                    let borrowed_heap = &heap.borrow();
1069                    args[0].as_list(borrowed_heap)?.clone()
1070                };
1071
1072                list.sort_by(|a, b| {
1073                    let func_def_a = get_function_def(func, &heap.borrow());
1074                    let func_def_b = get_function_def(func, &heap.borrow());
1075
1076                    match (func_def_a, func_def_b) {
1077                        (Some(fd_a), Some(fd_b)) => {
1078                            let result_a = fd_a.call(
1079                                Value::Null,
1080                                vec![*a],
1081                                Rc::clone(&heap),
1082                                Rc::clone(&bindings),
1083                                call_depth + 1,
1084                            );
1085                            let result_b = fd_b.call(
1086                                Value::Null,
1087                                vec![*b],
1088                                Rc::clone(&heap),
1089                                Rc::clone(&bindings),
1090                                call_depth + 1,
1091                            );
1092
1093                            match (result_a, result_b) {
1094                                (Ok(val_a), Ok(val_b)) => val_a
1095                                    .compare(&val_b, &heap.borrow())
1096                                    .unwrap_or(None)
1097                                    .unwrap_or(std::cmp::Ordering::Equal),
1098                                _ => std::cmp::Ordering::Equal,
1099                            }
1100                        }
1101                        _ => std::cmp::Ordering::Equal,
1102                    }
1103                });
1104
1105                Ok(heap.borrow_mut().insert_list(list))
1106            }
1107        }
1108    }
1109
1110    pub fn all() -> Vec<Self> {
1111        vec![
1112            Self::Sqrt,
1113            Self::Sin,
1114            Self::Cos,
1115            Self::Tan,
1116            Self::Asin,
1117            Self::Acos,
1118            Self::Atan,
1119            Self::Log,
1120            Self::Log10,
1121            Self::Exp,
1122            Self::Abs,
1123            Self::Floor,
1124            Self::Ceil,
1125            Self::Round,
1126            Self::Trunc,
1127            Self::Min,
1128            Self::Max,
1129            Self::Avg,
1130            Self::Sum,
1131            Self::Prod,
1132            Self::Median,
1133            Self::Percentile,
1134            Self::Range,
1135            Self::Len,
1136            Self::Head,
1137            Self::Tail,
1138            Self::Slice,
1139            Self::Concat,
1140            Self::Dot,
1141            Self::Unique,
1142            Self::Sort,
1143            Self::SortBy,
1144            Self::Reverse,
1145            Self::Map,
1146            Self::Reduce,
1147            Self::Filter,
1148            Self::Every,
1149            Self::Some,
1150            Self::Split,
1151            Self::Join,
1152            Self::Replace,
1153            Self::Trim,
1154            Self::Uppercase,
1155            Self::Lowercase,
1156            Self::ToString,
1157            Self::ToNumber,
1158            Self::Includes,
1159            Self::Format,
1160            Self::Typeof,
1161            Self::Arity,
1162            Self::Keys,
1163            Self::Values,
1164            Self::Entries,
1165            #[cfg(not(target_arch = "wasm32"))]
1166            Self::Print,
1167            #[cfg(not(target_arch = "wasm32"))]
1168            Self::TimeNow,
1169        ]
1170    }
1171
1172    pub fn all_names() -> Vec<&'static str> {
1173        Self::all().iter().map(|f| f.name()).collect()
1174    }
1175}
1176
1177impl FunctionDef {
1178    pub fn get_name(&self) -> String {
1179        match self {
1180            FunctionDef::BuiltIn(built_in) => {
1181                format!("built-in function \"{}\"", built_in.name())
1182            }
1183            FunctionDef::Lambda(LambdaDef { name, .. }) => name
1184                .clone()
1185                .map_or(String::from("anonymous function"), |n| {
1186                    format!("function \"{}\"", n)
1187                }),
1188        }
1189    }
1190
1191    pub fn check_arity(&self, arg_count: usize) -> Result<()> {
1192        match self {
1193            FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1194                FunctionArity::Exact(expected) => {
1195                    if arg_count == expected {
1196                        Ok(())
1197                    } else {
1198                        Err(anyhow!(
1199                            "{} takes exactly {} arguments, but {} were given",
1200                            self.get_name(),
1201                            expected,
1202                            arg_count
1203                        ))
1204                    }
1205                }
1206                FunctionArity::AtLeast(expected) => {
1207                    if arg_count >= expected {
1208                        Ok(())
1209                    } else {
1210                        Err(anyhow!(
1211                            "{} takes at least {} arguments, but {} were given",
1212                            self.get_name(),
1213                            expected,
1214                            arg_count
1215                        ))
1216                    }
1217                }
1218                FunctionArity::Between(min, max) => {
1219                    if arg_count >= min && arg_count <= max {
1220                        Ok(())
1221                    } else {
1222                        Err(anyhow!(
1223                            "{} takes between {} and {} arguments, but {} were given",
1224                            self.get_name(),
1225                            min,
1226                            max,
1227                            arg_count
1228                        ))
1229                    }
1230                }
1231            },
1232            FunctionDef::Lambda(def) => {
1233                let arity = def.get_arity();
1234
1235                match arity {
1236                    FunctionArity::Exact(expected) => {
1237                        if arg_count == expected {
1238                            Ok(())
1239                        } else {
1240                            Err(anyhow!(
1241                                "{} takes exactly {} arguments, but {} were given",
1242                                self.get_name(),
1243                                expected,
1244                                arg_count
1245                            ))
1246                        }
1247                    }
1248                    FunctionArity::AtLeast(expected) => {
1249                        if arg_count >= expected {
1250                            Ok(())
1251                        } else {
1252                            Err(anyhow!(
1253                                "{} takes at least {} arguments, but {} were given",
1254                                self.get_name(),
1255                                expected,
1256                                arg_count
1257                            ))
1258                        }
1259                    }
1260                    FunctionArity::Between(min, max) => {
1261                        if arg_count >= min && arg_count <= max {
1262                            Ok(())
1263                        } else {
1264                            Err(anyhow!(
1265                                "{} takes between {} and {} arguments, but {} were given",
1266                                self.get_name(),
1267                                min,
1268                                max,
1269                                arg_count
1270                            ))
1271                        }
1272                    }
1273                }
1274            }
1275        }
1276    }
1277
1278    pub fn call(
1279        &self,
1280        this_value: Value,
1281        args: Vec<Value>,
1282        heap: Rc<RefCell<Heap>>,
1283        bindings: Rc<RefCell<HashMap<String, Value>>>,
1284        call_depth: usize,
1285    ) -> Result<Value> {
1286        #[cfg(not(target_arch = "wasm32"))]
1287        let start = std::time::Instant::now();
1288
1289        self.check_arity(args.len())?;
1290
1291        if call_depth > 1000 {
1292            return Err(anyhow!(
1293                "in {}: maximum call depth of 1000 exceeded",
1294                self.get_name()
1295            ));
1296        }
1297
1298        match self {
1299            FunctionDef::Lambda(LambdaDef {
1300                name,
1301                args: expected_args,
1302                body,
1303                scope,
1304            }) => {
1305                #[cfg(not(target_arch = "wasm32"))]
1306                let start_var_env = std::time::Instant::now();
1307
1308                // Start with current environment as fallback for late binding
1309                let mut new_bindings = bindings.borrow().clone();
1310
1311                // Override with captured scope (captured variables take precedence)
1312                for (key, value) in scope {
1313                    new_bindings.insert(key.clone(), *value);
1314                }
1315
1316                // Add self-reference if named
1317                if let Some(fn_name) = name {
1318                    new_bindings.insert(fn_name.clone(), this_value);
1319                }
1320
1321                // Preserve inputs if present
1322                if let Some(inputs) = new_bindings.get("inputs").copied() {
1323                    new_bindings.insert(String::from("inputs"), inputs);
1324                }
1325
1326                // Add function arguments (highest precedence)
1327                for (idx, expected_arg) in expected_args.iter().enumerate() {
1328                    match expected_arg {
1329                        LambdaArg::Required(arg_name) => {
1330                            new_bindings.insert(arg_name.clone(), args[idx]);
1331                        }
1332                        LambdaArg::Optional(arg_name) => {
1333                            new_bindings.insert(
1334                                arg_name.clone(),
1335                                args.get(idx).copied().unwrap_or(Value::Null),
1336                            );
1337                        }
1338                        LambdaArg::Rest(arg_name) => {
1339                            new_bindings.insert(
1340                                arg_name.clone(),
1341                                heap.borrow_mut()
1342                                    .insert_list(args.iter().skip(idx).copied().collect()),
1343                            );
1344                        }
1345                    }
1346                }
1347
1348                #[cfg(not(target_arch = "wasm32"))]
1349                let end_var_env = std::time::Instant::now();
1350
1351                let return_value = evaluate_ast(
1352                    body,
1353                    Rc::clone(&heap),
1354                    Rc::new(RefCell::new(new_bindings)),
1355                    call_depth + 1,
1356                )
1357                .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1358
1359                #[cfg(not(target_arch = "wasm32"))]
1360                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1361                    name: self.get_name(),
1362                    start,
1363                    end: std::time::Instant::now(),
1364                    start_var_env: Some(start_var_env),
1365                    end_var_env: Some(end_var_env),
1366                });
1367
1368                return_value
1369            }
1370            FunctionDef::BuiltIn(built_in) => {
1371                let return_value = built_in
1372                    .call(args, heap, bindings, call_depth + 1)
1373                    .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1374
1375                #[cfg(not(target_arch = "wasm32"))]
1376                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1377                    name: self.get_name(),
1378                    start,
1379                    end: std::time::Instant::now(),
1380                    start_var_env: None,
1381                    end_var_env: None,
1382                });
1383
1384                return_value
1385            }
1386        }
1387    }
1388}
1389
1390pub fn is_built_in_function(ident: &str) -> bool {
1391    BuiltInFunction::from_ident(ident).is_some()
1392}
1393
1394pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1395    BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1396}
1397
1398pub fn get_built_in_function_idents() -> Vec<&'static str> {
1399    BuiltInFunction::all_names()
1400}
1401
1402pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1403    match value {
1404        Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1405            pointer.reify(heap).as_lambda().ok()?.clone(),
1406        )),
1407        Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1408        _ => None,
1409    }
1410}
1411
1412#[cfg(test)]
1413mod tests {
1414    use super::*;
1415    use std::cell::RefCell;
1416    use std::collections::HashMap;
1417    use std::rc::Rc;
1418
1419    #[test]
1420    fn test_range_function() {
1421        // Test single argument
1422        let heap = Rc::new(RefCell::new(Heap::new()));
1423        let bindings = Rc::new(RefCell::new(HashMap::new()));
1424        let range_fn = BuiltInFunction::Range;
1425
1426        // Test range(4) - exclusive, so [0, 1, 2, 3]
1427        let args = vec![Value::Number(4.0)];
1428        let result = range_fn
1429            .call(args, heap.clone(), bindings.clone(), 0)
1430            .unwrap();
1431
1432        let heap_borrow = heap.borrow();
1433        let list = result.as_list(&heap_borrow).unwrap();
1434        assert_eq!(list.len(), 4);
1435        assert_eq!(list[0], Value::Number(0.0));
1436        assert_eq!(list[3], Value::Number(3.0));
1437    }
1438
1439    #[test]
1440    fn test_range_function_two_args() {
1441        // Test two arguments
1442        let heap = Rc::new(RefCell::new(Heap::new()));
1443        let bindings = Rc::new(RefCell::new(HashMap::new()));
1444        let range_fn = BuiltInFunction::Range;
1445
1446        // Test range(4, 10) - exclusive, so [4, 5, 6, 7, 8, 9]
1447        let args = vec![Value::Number(4.0), Value::Number(10.0)];
1448        let result = range_fn
1449            .call(args, heap.clone(), bindings.clone(), 0)
1450            .unwrap();
1451
1452        let heap_borrow = heap.borrow();
1453        let list = result.as_list(&heap_borrow).unwrap();
1454        assert_eq!(list.len(), 6);
1455        assert_eq!(list[0], Value::Number(4.0));
1456        assert_eq!(list[5], Value::Number(9.0));
1457    }
1458
1459    #[test]
1460    fn test_round_function_single_arg() {
1461        let heap = Rc::new(RefCell::new(Heap::new()));
1462        let bindings = Rc::new(RefCell::new(HashMap::new()));
1463        let round_fn = BuiltInFunction::Round;
1464
1465        // Test basic rounding
1466        let test_cases = vec![
1467            (42.4, 42.0),
1468            (42.5, 43.0),
1469            (42.6, 43.0),
1470            (-42.4, -42.0),
1471            (-42.5, -43.0),
1472            (-42.6, -43.0),
1473            (0.0, 0.0),
1474            (1.999, 2.0),
1475            (-1.999, -2.0),
1476        ];
1477
1478        for (input, expected) in test_cases {
1479            let args = vec![Value::Number(input)];
1480            let result = round_fn
1481                .call(args, heap.clone(), bindings.clone(), 0)
1482                .unwrap();
1483            assert_eq!(
1484                result,
1485                Value::Number(expected),
1486                "round({}) should be {}",
1487                input,
1488                expected
1489            );
1490        }
1491    }
1492
1493    #[test]
1494    fn test_round_function_with_decimal_places() {
1495        let heap = Rc::new(RefCell::new(Heap::new()));
1496        let bindings = Rc::new(RefCell::new(HashMap::new()));
1497        let round_fn = BuiltInFunction::Round;
1498
1499        // Test rounding to decimal places
1500        let test_cases = vec![
1501            (42.4543, 0.0, 42.0),
1502            (42.4543, 1.0, 42.5),
1503            (42.4543, 2.0, 42.45),
1504            (42.4543, 3.0, 42.454),
1505            (42.4543, 4.0, 42.4543),
1506            (4.14159, 4.0, 4.1416),
1507            (0.005, 2.0, 0.01),
1508            (0.995, 2.0, 1.0),
1509            // Note: 9.995 has floating point representation issues
1510            // In binary, it's actually slightly less than 9.995
1511            (9.995, 2.0, 9.99),
1512            (-9.995, 2.0, -9.99),
1513        ];
1514
1515        for (input, places, expected) in test_cases {
1516            let args = vec![Value::Number(input), Value::Number(places)];
1517            let result = round_fn
1518                .call(args, heap.clone(), bindings.clone(), 0)
1519                .unwrap();
1520
1521            // Use approximate comparison for floating point
1522            if let Value::Number(result_num) = result {
1523                assert!(
1524                    (result_num - expected).abs() < 1e-10,
1525                    "round({}, {}) = {} should be close to {}",
1526                    input,
1527                    places,
1528                    result_num,
1529                    expected
1530                );
1531            } else {
1532                panic!("Expected number result");
1533            }
1534        }
1535    }
1536
1537    #[test]
1538    fn test_round_function_negative_decimal_places() {
1539        let heap = Rc::new(RefCell::new(Heap::new()));
1540        let bindings = Rc::new(RefCell::new(HashMap::new()));
1541        let round_fn = BuiltInFunction::Round;
1542
1543        // Test rounding to tens, hundreds, etc.
1544        let test_cases = vec![
1545            (1234.567, -1.0, 1230.0),
1546            (1234.567, -2.0, 1200.0),
1547            (1234.567, -3.0, 1000.0),
1548            (1234.567, -4.0, 0.0),
1549            (5678.9, -1.0, 5680.0),
1550            (5678.9, -2.0, 5700.0),
1551            (5678.9, -3.0, 6000.0),
1552            (-1234.567, -1.0, -1230.0),
1553            (-1234.567, -2.0, -1200.0),
1554            (1500.0, -3.0, 2000.0),
1555            (-1500.0, -3.0, -2000.0),
1556        ];
1557
1558        for (input, places, expected) in test_cases {
1559            let args = vec![Value::Number(input), Value::Number(places)];
1560            let result = round_fn
1561                .call(args, heap.clone(), bindings.clone(), 0)
1562                .unwrap();
1563            assert_eq!(
1564                result,
1565                Value::Number(expected),
1566                "round({}, {}) should be {}",
1567                input,
1568                places,
1569                expected
1570            );
1571        }
1572    }
1573
1574    #[test]
1575    fn test_round_function_edge_cases() {
1576        let heap = Rc::new(RefCell::new(Heap::new()));
1577        let bindings = Rc::new(RefCell::new(HashMap::new()));
1578        let round_fn = BuiltInFunction::Round;
1579
1580        // Test edge cases
1581        let test_cases = vec![
1582            (f64::INFINITY, 0.0, f64::INFINITY),
1583            (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
1584            (0.0, 5.0, 0.0),
1585            (-0.0, 5.0, -0.0),
1586            (1e-10, 5.0, 0.0),
1587            (1e-10, 15.0, 1e-10),
1588        ];
1589
1590        for (input, places, expected) in test_cases {
1591            let args = vec![Value::Number(input), Value::Number(places)];
1592            let result = round_fn
1593                .call(args, heap.clone(), bindings.clone(), 0)
1594                .unwrap();
1595
1596            if let Value::Number(result_num) = result {
1597                if expected.is_infinite() {
1598                    assert!(
1599                        result_num.is_infinite()
1600                            && result_num.is_sign_positive() == expected.is_sign_positive(),
1601                        "round({}, {}) should be {}",
1602                        input,
1603                        places,
1604                        expected
1605                    );
1606                } else if expected == 0.0 || expected == -0.0 {
1607                    assert!(
1608                        result_num.abs() < 1e-10,
1609                        "round({}, {}) = {} should be close to 0",
1610                        input,
1611                        places,
1612                        result_num
1613                    );
1614                } else {
1615                    assert!(
1616                        (result_num - expected).abs() < 1e-15,
1617                        "round({}, {}) = {} should be close to {}",
1618                        input,
1619                        places,
1620                        result_num,
1621                        expected
1622                    );
1623                }
1624            } else {
1625                panic!("Expected number result");
1626            }
1627        }
1628    }
1629}