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