blots_core/
functions.rs

1use crate::{
2    environment::Environment,
3    error::RuntimeError,
4    expressions::evaluate_ast,
5    heap::{Heap, HeapPointer, IterablePointer},
6    units,
7    values::{FunctionArity, LambdaArg, LambdaDef, ReifiedValue, Value},
8};
9use anyhow::Result as AnyhowResult;
10use dyn_fmt::AsStrFormatExt;
11use indexmap::IndexMap;
12use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::LazyLock};
13
14#[cfg(not(target_arch = "wasm32"))]
15use std::sync::Mutex;
16
17#[cfg(not(target_arch = "wasm32"))]
18use crate::stats::FunctionCallStats;
19
20#[cfg(not(target_arch = "wasm32"))]
21pub static FUNCTION_CALLS: LazyLock<Mutex<Vec<FunctionCallStats>>> =
22    LazyLock::new(|| Mutex::new(Vec::new()));
23
24#[cfg(not(target_arch = "wasm32"))]
25pub fn get_function_call_stats() -> Vec<FunctionCallStats> {
26    FUNCTION_CALLS.lock().unwrap().clone()
27}
28
29#[cfg(not(target_arch = "wasm32"))]
30pub fn clear_function_call_stats() {
31    FUNCTION_CALLS.lock().unwrap().clear();
32}
33
34// Cache built-in function names since they're static
35pub static BUILTIN_FUNCTION_NAMES: LazyLock<Vec<&'static str>> =
36    LazyLock::new(BuiltInFunction::all_names);
37
38#[derive(
39    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
40)]
41pub enum BuiltInFunction {
42    // Math functions
43    Sqrt,
44    Sin,
45    Cos,
46    Tan,
47    Asin,
48    Acos,
49    Atan,
50    Log,
51    Log10,
52    Exp,
53    Abs,
54    Floor,
55    Ceil,
56    Round,
57    Trunc,
58    Random,
59
60    // Aggregate functions
61    Min,
62    Max,
63    Avg,
64    Sum,
65    Prod,
66    Median,
67    Percentile,
68
69    // List functions
70    Range,
71    Len,
72    Head,
73    Tail,
74    Slice,
75    Concat,
76    Dot,
77    Unique,
78    Sort,
79    SortBy,
80    Reverse,
81    Any,
82    All,
83
84    // Higher-order functions
85    Map,
86    Reduce,
87    Filter,
88    Every,
89    Some,
90
91    // String functions
92    Split,
93    Join,
94    Replace,
95    Trim,
96    Uppercase,
97    Lowercase,
98    Includes,
99    Format,
100
101    // Type functions
102    Typeof,
103    Arity,
104
105    // Record functions
106    Keys,
107    Values,
108    Entries,
109
110    // List utility functions
111    GroupBy,
112    CountBy,
113    Flatten,
114    Zip,
115    Chunk,
116
117    // Conversion functions
118    ToString,
119    ToNumber,
120    ToBool,
121    Convert,
122
123    // Unchecked comparison functions
124    Ugt,
125    Ult,
126    Ugte,
127    Ulte,
128
129    // Platform-specific functions
130    #[cfg(not(target_arch = "wasm32"))]
131    Print,
132    #[cfg(not(target_arch = "wasm32"))]
133    TimeNow,
134}
135
136#[derive(Debug, Clone)]
137pub enum FunctionDef {
138    BuiltIn(BuiltInFunction),
139    Lambda(LambdaDef),
140}
141
142impl BuiltInFunction {
143    pub fn from_ident(ident: &str) -> Option<Self> {
144        match ident {
145            "sqrt" => Some(Self::Sqrt),
146            "sin" => Some(Self::Sin),
147            "cos" => Some(Self::Cos),
148            "tan" => Some(Self::Tan),
149            "asin" => Some(Self::Asin),
150            "acos" => Some(Self::Acos),
151            "atan" => Some(Self::Atan),
152            "log" => Some(Self::Log),
153            "log10" => Some(Self::Log10),
154            "exp" => Some(Self::Exp),
155            "abs" => Some(Self::Abs),
156            "floor" => Some(Self::Floor),
157            "ceil" => Some(Self::Ceil),
158            "round" => Some(Self::Round),
159            "trunc" => Some(Self::Trunc),
160            "random" => Some(Self::Random),
161            "min" => Some(Self::Min),
162            "max" => Some(Self::Max),
163            "avg" => Some(Self::Avg),
164            "sum" => Some(Self::Sum),
165            "prod" => Some(Self::Prod),
166            "median" => Some(Self::Median),
167            "percentile" => Some(Self::Percentile),
168            "range" => Some(Self::Range),
169            "any" => Some(Self::Any),
170            "all" => Some(Self::All),
171            "len" => Some(Self::Len),
172            "head" => Some(Self::Head),
173            "tail" => Some(Self::Tail),
174            "slice" => Some(Self::Slice),
175            "concat" => Some(Self::Concat),
176            "dot" => Some(Self::Dot),
177            "unique" => Some(Self::Unique),
178            "sort" => Some(Self::Sort),
179            "sort_by" => Some(Self::SortBy),
180            "reverse" => Some(Self::Reverse),
181            "map" => Some(Self::Map),
182            "reduce" => Some(Self::Reduce),
183            "filter" => Some(Self::Filter),
184            "every" => Some(Self::Every),
185            "some" => Some(Self::Some),
186            "split" => Some(Self::Split),
187            "join" => Some(Self::Join),
188            "replace" => Some(Self::Replace),
189            "trim" => Some(Self::Trim),
190            "uppercase" => Some(Self::Uppercase),
191            "lowercase" => Some(Self::Lowercase),
192            "to_string" => Some(Self::ToString),
193            "to_number" => Some(Self::ToNumber),
194            "to_bool" => Some(Self::ToBool),
195            "convert" => Some(Self::Convert),
196            "includes" => Some(Self::Includes),
197            "format" => Some(Self::Format),
198            "typeof" => Some(Self::Typeof),
199            "arity" => Some(Self::Arity),
200            "keys" => Some(Self::Keys),
201            "values" => Some(Self::Values),
202            "entries" => Some(Self::Entries),
203            "group_by" => Some(Self::GroupBy),
204            "count_by" => Some(Self::CountBy),
205            "flatten" => Some(Self::Flatten),
206            "zip" => Some(Self::Zip),
207            "chunk" => Some(Self::Chunk),
208            "ugt" => Some(Self::Ugt),
209            "ult" => Some(Self::Ult),
210            "ugte" => Some(Self::Ugte),
211            "ulte" => Some(Self::Ulte),
212            #[cfg(not(target_arch = "wasm32"))]
213            "print" => Some(Self::Print),
214            #[cfg(not(target_arch = "wasm32"))]
215            "time_now" => Some(Self::TimeNow),
216            _ => None,
217        }
218    }
219
220    pub fn name(&self) -> &'static str {
221        match self {
222            Self::Sqrt => "sqrt",
223            Self::Sin => "sin",
224            Self::Cos => "cos",
225            Self::Tan => "tan",
226            Self::Asin => "asin",
227            Self::Acos => "acos",
228            Self::Atan => "atan",
229            Self::Log => "log",
230            Self::Log10 => "log10",
231            Self::Exp => "exp",
232            Self::Abs => "abs",
233            Self::Floor => "floor",
234            Self::Ceil => "ceil",
235            Self::Round => "round",
236            Self::Trunc => "trunc",
237            Self::Random => "random",
238            Self::Min => "min",
239            Self::Max => "max",
240            Self::Avg => "avg",
241            Self::Sum => "sum",
242            Self::Prod => "prod",
243            Self::Median => "median",
244            Self::Percentile => "percentile",
245            Self::Range => "range",
246            Self::Any => "any",
247            Self::All => "all",
248            Self::Len => "len",
249            Self::Head => "head",
250            Self::Tail => "tail",
251            Self::Slice => "slice",
252            Self::Concat => "concat",
253            Self::Dot => "dot",
254            Self::Unique => "unique",
255            Self::Sort => "sort",
256            Self::SortBy => "sort_by",
257            Self::Reverse => "reverse",
258            Self::Map => "map",
259            Self::Reduce => "reduce",
260            Self::Filter => "filter",
261            Self::Every => "every",
262            Self::Some => "some",
263            Self::Split => "split",
264            Self::Join => "join",
265            Self::Replace => "replace",
266            Self::Trim => "trim",
267            Self::Uppercase => "uppercase",
268            Self::Lowercase => "lowercase",
269            Self::Includes => "includes",
270            Self::Format => "format",
271            Self::Typeof => "typeof",
272            Self::Arity => "arity",
273            Self::Keys => "keys",
274            Self::Values => "values",
275            Self::Entries => "entries",
276            Self::GroupBy => "group_by",
277            Self::CountBy => "count_by",
278            Self::Flatten => "flatten",
279            Self::Zip => "zip",
280            Self::Chunk => "chunk",
281            Self::ToString => "to_string",
282            Self::ToNumber => "to_number",
283            Self::ToBool => "to_bool",
284            Self::Convert => "convert",
285            Self::Ugt => "ugt",
286            Self::Ult => "ult",
287            Self::Ugte => "ugte",
288            Self::Ulte => "ulte",
289            #[cfg(not(target_arch = "wasm32"))]
290            Self::Print => "print",
291            #[cfg(not(target_arch = "wasm32"))]
292            Self::TimeNow => "time_now",
293        }
294    }
295
296    pub fn arity(&self) -> FunctionArity {
297        match self {
298            // Math functions - single argument
299            Self::Sqrt
300            | Self::Sin
301            | Self::Cos
302            | Self::Tan
303            | Self::Asin
304            | Self::Acos
305            | Self::Atan
306            | Self::Log
307            | Self::Log10
308            | Self::Exp
309            | Self::Abs
310            | Self::Floor
311            | Self::Ceil
312            | Self::Trunc
313            | Self::Random => FunctionArity::Exact(1),
314
315            // Round can take 1 or 2 arguments
316            Self::Round => FunctionArity::Between(1, 2),
317
318            // Aggregate functions
319            Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
320                FunctionArity::AtLeast(1)
321            }
322
323            // Range can take 1 or 2 arguments
324            Self::Range => FunctionArity::Between(1, 2),
325
326            // List functions
327            Self::Len
328            | Self::Head
329            | Self::Tail
330            | Self::Unique
331            | Self::Sort
332            | Self::Reverse
333            | Self::Any
334            | Self::All => FunctionArity::Exact(1),
335            Self::Slice => FunctionArity::Exact(3),
336            Self::Concat => FunctionArity::AtLeast(2),
337            Self::Dot | Self::Percentile => FunctionArity::Exact(2),
338
339            // Higher-order functions
340            Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
341                FunctionArity::Exact(2)
342            }
343            Self::Reduce => FunctionArity::Exact(3),
344
345            // String functions
346            Self::Split | Self::Join | Self::Includes => FunctionArity::Exact(2),
347            Self::Replace => FunctionArity::Exact(3),
348            Self::Trim
349            | Self::Uppercase
350            | Self::Lowercase
351            | Self::ToString
352            | Self::ToNumber
353            | Self::ToBool => FunctionArity::Exact(1),
354            Self::Convert => FunctionArity::Exact(3),
355            Self::Format => FunctionArity::AtLeast(1),
356
357            // Type functions
358            Self::Typeof | Self::Arity => FunctionArity::Exact(1),
359
360            // Record functions
361            Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
362
363            // List utility functions
364            Self::GroupBy | Self::CountBy => FunctionArity::Exact(2),
365            Self::Flatten => FunctionArity::Exact(1),
366            Self::Zip => FunctionArity::AtLeast(2),
367            Self::Chunk => FunctionArity::Exact(2),
368
369            // Unchecked comparison functions
370            Self::Ugt | Self::Ult | Self::Ugte | Self::Ulte => FunctionArity::Exact(2),
371
372            // Platform-specific functions
373            #[cfg(not(target_arch = "wasm32"))]
374            Self::Print => FunctionArity::AtLeast(1),
375            #[cfg(not(target_arch = "wasm32"))]
376            Self::TimeNow => FunctionArity::Exact(0),
377        }
378    }
379
380    pub fn call(
381        &self,
382        args: Vec<Value>,
383        heap: Rc<RefCell<Heap>>,
384        bindings: Rc<Environment>,
385        call_depth: usize,
386        source: &str,
387    ) -> Result<Value, RuntimeError> {
388        match self {
389            // Math functions
390            Self::Sqrt => Ok(Value::Number(args[0].as_number()?.sqrt())),
391            Self::Sin => Ok(Value::Number(args[0].as_number()?.sin())),
392            Self::Cos => Ok(Value::Number(args[0].as_number()?.cos())),
393            Self::Tan => Ok(Value::Number(args[0].as_number()?.tan())),
394            Self::Asin => Ok(Value::Number(args[0].as_number()?.asin())),
395            Self::Acos => Ok(Value::Number(args[0].as_number()?.acos())),
396            Self::Atan => Ok(Value::Number(args[0].as_number()?.atan())),
397            Self::Log => Ok(Value::Number(args[0].as_number()?.ln())),
398            Self::Log10 => Ok(Value::Number(args[0].as_number()?.log10())),
399            Self::Exp => Ok(Value::Number(args[0].as_number()?.exp())),
400            Self::Abs => Ok(Value::Number(args[0].as_number()?.abs())),
401            Self::Floor => Ok(Value::Number(args[0].as_number()?.floor())),
402            Self::Ceil => Ok(Value::Number(args[0].as_number()?.ceil())),
403            Self::Trunc => Ok(Value::Number(args[0].as_number()?.trunc())),
404
405            Self::Random => {
406                let seed = args[0].as_number()? as u64;
407                let mut rng = fastrand::Rng::with_seed(seed);
408                Ok(Value::Number(rng.f64()))
409            }
410
411            Self::Round => {
412                let num = args[0].as_number()?;
413                if args.len() == 1 {
414                    Ok(Value::Number(num.round()))
415                } else {
416                    let decimal_places = args[1].as_number()? as i32;
417                    let multiplier = 10_f64.powi(decimal_places);
418                    Ok(Value::Number((num * multiplier).round() / multiplier))
419                }
420            }
421
422            // Aggregate functions
423            Self::Min => {
424                let nums = if args.len() == 1 {
425                    // Check if single argument is a list
426                    match &args[0] {
427                        Value::List(_) => {
428                            let borrowed_heap = heap.borrow();
429                            let list = args[0].as_list(&borrowed_heap)?;
430                            list.iter()
431                                .map(|a| a.as_number())
432                                .collect::<AnyhowResult<Vec<f64>>>()?
433                        }
434                        _ => vec![args[0].as_number()?],
435                    }
436                } else {
437                    args.iter()
438                        .map(|a| a.as_number())
439                        .collect::<AnyhowResult<Vec<f64>>>()?
440                };
441
442                if nums.is_empty() {
443                    return Err(RuntimeError::from("min requires at least one number"));
444                }
445
446                Ok(Value::Number(
447                    nums.iter().copied().fold(f64::INFINITY, f64::min),
448                ))
449            }
450
451            Self::Max => {
452                let nums = if args.len() == 1 {
453                    // Check if single argument is a list
454                    match &args[0] {
455                        Value::List(_) => {
456                            let borrowed_heap = heap.borrow();
457                            let list = args[0].as_list(&borrowed_heap)?;
458                            list.iter()
459                                .map(|a| a.as_number())
460                                .collect::<AnyhowResult<Vec<f64>>>()?
461                        }
462                        _ => vec![args[0].as_number()?],
463                    }
464                } else {
465                    args.iter()
466                        .map(|a| a.as_number())
467                        .collect::<AnyhowResult<Vec<f64>>>()?
468                };
469
470                if nums.is_empty() {
471                    return Err(RuntimeError::from("max requires at least one number"));
472                }
473
474                Ok(Value::Number(
475                    nums.iter().copied().fold(f64::NEG_INFINITY, f64::max),
476                ))
477            }
478
479            Self::Avg => {
480                let nums = if args.len() == 1 {
481                    match &args[0] {
482                        Value::List(_) => {
483                            let borrowed_heap = heap.borrow();
484                            let list = args[0].as_list(&borrowed_heap)?;
485                            list.iter()
486                                .map(|a| a.as_number())
487                                .collect::<AnyhowResult<Vec<f64>>>()?
488                        }
489                        _ => vec![args[0].as_number()?],
490                    }
491                } else {
492                    args.iter()
493                        .map(|a| a.as_number())
494                        .collect::<AnyhowResult<Vec<f64>>>()?
495                };
496                if nums.is_empty() {
497                    return Err(RuntimeError::from("avg requires at least one number"));
498                }
499                Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
500            }
501
502            Self::Prod => {
503                let nums = if args.len() == 1 {
504                    match &args[0] {
505                        Value::List(_) => {
506                            let borrowed_heap = heap.borrow();
507                            let list = args[0].as_list(&borrowed_heap)?;
508                            list.iter()
509                                .map(|a| a.as_number())
510                                .collect::<AnyhowResult<Vec<f64>>>()?
511                        }
512                        _ => vec![args[0].as_number()?],
513                    }
514                } else {
515                    args.iter()
516                        .map(|a| a.as_number())
517                        .collect::<AnyhowResult<Vec<f64>>>()?
518                };
519                if nums.is_empty() {
520                    return Err(RuntimeError::from("prod requires at least one number"));
521                }
522                Ok(Value::Number(nums.iter().product()))
523            }
524
525            Self::Range => {
526                let (start, end) = match args[..] {
527                    [Value::Number(start)] => (0.0, start),
528                    [Value::Number(start), Value::Number(end)] => (start, end),
529                    _ => return Err(RuntimeError::from("range requires 1 or 2 numbers")),
530                };
531
532                if start > end {
533                    return Err(RuntimeError::from(
534                        "range requires start to be less than or equal to end"
535                    ));
536                }
537
538                if !f64::is_finite(start) || !f64::is_finite(end) {
539                    return Err(RuntimeError::from("range requires finite numbers"));
540                }
541
542                let start_i64 = start as i64;
543                let end_i64 = end as i64;
544                let length = end_i64 - start_i64;
545
546                if length > u32::MAX as i64 {
547                    return Err(RuntimeError::new(format!(
548                        "list would be longer than the maximum length of {}",
549                        u32::MAX
550                    )));
551                }
552
553                let values = (start_i64..end_i64)
554                    .map(|e| Value::Number(e as f64))
555                    .collect();
556                let list = heap.borrow_mut().insert_list(values);
557
558                Ok(list)
559            }
560
561            Self::Sum => {
562                let nums = if args.len() == 1 {
563                    match &args[0] {
564                        Value::List(_) => {
565                            let borrowed_heap = heap.borrow();
566                            let list = args[0].as_list(&borrowed_heap)?;
567                            list.iter()
568                                .map(|a| a.as_number())
569                                .collect::<AnyhowResult<Vec<f64>>>()?
570                        }
571                        _ => vec![args[0].as_number()?],
572                    }
573                } else {
574                    args.iter()
575                        .map(|a| a.as_number())
576                        .collect::<AnyhowResult<Vec<f64>>>()?
577                };
578                if nums.is_empty() {
579                    return Err(RuntimeError::from("sum requires at least one number"));
580                }
581                Ok(Value::Number(nums.iter().sum()))
582            }
583
584            // Remaining aggregate functions
585            Self::Median => {
586                let mut nums = if args.len() == 1 {
587                    match &args[0] {
588                        Value::List(_) => {
589                            let borrowed_heap = heap.borrow();
590                            let list = args[0].as_list(&borrowed_heap)?;
591                            list.iter()
592                                .map(|a| a.as_number())
593                                .collect::<AnyhowResult<Vec<f64>>>()?
594                        }
595                        _ => vec![args[0].as_number()?],
596                    }
597                } else {
598                    args.into_iter()
599                        .map(|a| a.as_number())
600                        .collect::<AnyhowResult<Vec<f64>>>()?
601                };
602
603                if nums.is_empty() {
604                    return Err(RuntimeError::from("median requires at least one number"));
605                }
606
607                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
608                let len = nums.len();
609                if len % 2 == 0 {
610                    Ok(Value::Number((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
611                } else {
612                    Ok(Value::Number(nums[len / 2]))
613                }
614            }
615
616            Self::Percentile => {
617                let p = args[1].as_number()?;
618                let heap = &heap.borrow();
619                let list = args[0].as_list(heap)?;
620
621                if !(0.0..=100.0).contains(&p) {
622                    return Err(RuntimeError::from("percentile must be between 0 and 100"));
623                }
624
625                let mut nums = list
626                    .iter()
627                    .map(|a| a.as_number())
628                    .collect::<AnyhowResult<Vec<f64>>>()?;
629
630                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
631                let index = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
632
633                Ok(Value::Number(nums[index]))
634            }
635
636            // List functions
637            Self::Len => match &args[0] {
638                Value::List(l) => Ok(Value::Number(
639                    l.reify(&heap.borrow()).as_list()?.len() as f64
640                )),
641                Value::String(s) => Ok(Value::Number(
642                    s.reify(&heap.borrow()).as_string()?.len() as f64
643                )),
644                _ => Err(RuntimeError::from("argument must be a list or string")),
645            },
646
647            Self::Head => match &args[0] {
648                Value::List(p) => Ok(p
649                    .reify(&heap.borrow())
650                    .as_list()?
651                    .first()
652                    .copied()
653                    .unwrap_or(Value::Null)),
654                Value::String(p) => {
655                    let val = {
656                        p.reify(&heap.borrow())
657                            .as_string()?
658                            .get(0..1)
659                            .unwrap_or("")
660                            .to_string()
661                    };
662
663                    Ok(heap.borrow_mut().insert_string(val))
664                }
665                _ => Err(RuntimeError::from("argument must be a list or string")),
666            },
667
668            Self::Tail => match &args[0] {
669                Value::List(p) => {
670                    let val = {
671                        p.reify(&heap.borrow())
672                            .as_list()?
673                            .get(1..)
674                            .unwrap_or([].as_slice())
675                            .to_vec()
676                    };
677
678                    Ok(heap.borrow_mut().insert_list(val))
679                }
680                Value::String(s) => {
681                    let val = {
682                        s.reify(&heap.borrow())
683                            .as_string()?
684                            .get(1..)
685                            .unwrap_or("")
686                            .to_string()
687                    };
688
689                    Ok(heap.borrow_mut().insert_string(val))
690                }
691                _ => Err(RuntimeError::from("argument must be a list or string")),
692            },
693
694            Self::Slice => {
695                let start = args[1].as_number()? as usize;
696                let end = args[2].as_number()? as usize;
697
698                match args[0] {
699                    Value::List(_) => {
700                        let slice = {
701                            let borrowed_heap = heap.borrow();
702                            let list = args[0].as_list(&borrowed_heap)?;
703                            list.get(start..end)
704                                .ok_or_else(|| RuntimeError::from("index out of bounds"))?
705                                .to_vec()
706                        };
707
708                        Ok(heap.borrow_mut().insert_list(slice))
709                    }
710                    Value::String(_) => {
711                        let s = {
712                            let borrowed_heap = &heap.borrow();
713                            args[0].as_string(borrowed_heap)?.to_string()
714                        };
715
716                        s.get(start..end)
717                            .map_or(Err(RuntimeError::from("index out of bounds")), |s| {
718                                Ok(heap.borrow_mut().insert_string(s.to_string()))
719                            })
720                    }
721                    _ => Err(RuntimeError::from("argument must be a list or string")),
722                }
723            }
724
725            Self::Concat => {
726                let mut list = vec![];
727
728                for arg in args {
729                    match arg {
730                        Value::List(p) => {
731                            let borrowed_heap = heap.borrow();
732                            let values = p.reify(&borrowed_heap).as_list()?;
733                            list.extend(values.iter().copied());
734                        }
735                        Value::Spread(IterablePointer::List(p)) => {
736                            let borrowed_heap = heap.borrow();
737                            let values = p.reify(&borrowed_heap).as_list()?;
738                            list.extend(values.iter().copied());
739                        }
740                        Value::Spread(IterablePointer::String(p)) => {
741                            let string = {
742                                let borrowed_heap = &heap.borrow();
743                                p.reify(borrowed_heap).as_string()?.to_string()
744                            };
745
746                            list.extend(
747                                string
748                                    .chars()
749                                    .map(|c| heap.borrow_mut().insert_string(c.to_string())),
750                            );
751                        }
752                        _ => list.push(arg),
753                    }
754                }
755
756                Ok(heap.borrow_mut().insert_list(list))
757            }
758
759            Self::Dot => {
760                let borrowed_heap = heap.borrow();
761                let a = args[0].as_list(&borrowed_heap)?;
762                let b = args[1].as_list(&borrowed_heap)?;
763
764                if a.len() != b.len() {
765                    return Err(RuntimeError::from(
766                        "cannot calculate dot product of lists with different lengths"
767                    ));
768                }
769
770                let mut sum = 0.0;
771                for (a, b) in a.iter().zip(b.iter()) {
772                    sum += a.as_number()? * b.as_number()?;
773                }
774                Ok(Value::Number(sum))
775            }
776
777            Self::Unique => {
778                let mut unique_list = vec![];
779                let borrowed_heap = heap.borrow();
780                let list = args[0].as_list(&borrowed_heap)?;
781
782                for item in list.iter() {
783                    let mut is_duplicate = false;
784                    for existing in &unique_list {
785                        if item.equals(existing, &borrowed_heap)? {
786                            is_duplicate = true;
787                            break;
788                        }
789                    }
790                    if !is_duplicate {
791                        unique_list.push(*item);
792                    }
793                }
794
795                drop(borrowed_heap);
796                Ok(heap.borrow_mut().insert_list(unique_list))
797            }
798
799            Self::Sort => {
800                let mut list = {
801                    let borrowed_heap = &heap.borrow();
802                    args[0].as_list(borrowed_heap)?.clone()
803                };
804                let borrowed_heap = heap.borrow();
805                list.sort_by(|a, b| {
806                    a.compare(b, &borrowed_heap)
807                        .unwrap_or(None)
808                        .unwrap_or(std::cmp::Ordering::Equal)
809                });
810                drop(borrowed_heap);
811                Ok(heap.borrow_mut().insert_list(list))
812            }
813
814            Self::Reverse => {
815                let mut list = { args[0].as_list(&heap.borrow())?.clone() };
816                list.reverse();
817                Ok(heap.borrow_mut().insert_list(list))
818            }
819
820            Self::Any => {
821                let borrowed_heap = heap.borrow();
822                let list = args[0].as_list(&borrowed_heap)?;
823                Ok(Value::Bool(
824                    list.iter().any(|v| v.as_bool().unwrap_or(false)),
825                ))
826            }
827
828            Self::All => {
829                let borrowed_heap = heap.borrow();
830                let list = args[0].as_list(&borrowed_heap)?;
831                Ok(Value::Bool(
832                    list.iter().all(|v| v.as_bool().unwrap_or(false)),
833                ))
834            }
835
836            // String functions
837            Self::Split => {
838                let (s, delimeter) = {
839                    let borrowed_heap = &heap.borrow();
840                    (
841                        args[0].as_string(borrowed_heap)?.to_string(),
842                        args[1].as_string(borrowed_heap)?.to_string(),
843                    )
844                };
845
846                let list = s
847                    .split(&delimeter)
848                    .map(|s| heap.borrow_mut().insert_string(s.to_string()))
849                    .collect();
850
851                Ok(heap.borrow_mut().insert_list(list))
852            }
853
854            Self::Join => {
855                let joined_string = {
856                    let borrowed_heap = &heap.borrow();
857                    let delimeter = args[1].as_string(borrowed_heap)?;
858                    let list = args[0].as_list(borrowed_heap)?;
859                    list.iter()
860                        .map(|v| v.stringify_internal(borrowed_heap))
861                        .collect::<Vec<String>>()
862                        .join(delimeter)
863                };
864
865                Ok(heap.borrow_mut().insert_string(joined_string))
866            }
867
868            Self::Replace => {
869                let string = {
870                    let borrowed_heap = heap.borrow();
871                    let old = args[1].as_string(&borrowed_heap)?.to_string();
872                    let new = args[2].as_string(&borrowed_heap)?.to_string();
873                    let s = args[0].as_string(&borrowed_heap)?.to_string();
874                    s.replace(&old, &new)
875                };
876
877                Ok(heap.borrow_mut().insert_string(string))
878            }
879
880            Self::Trim => {
881                let string = {
882                    let borrowed_heap = heap.borrow();
883                    args[0].as_string(&borrowed_heap)?.trim().to_string()
884                };
885
886                Ok(heap.borrow_mut().insert_string(string))
887            }
888
889            Self::Uppercase => {
890                let string = args[0].as_string(&heap.borrow())?.to_uppercase();
891                Ok(heap.borrow_mut().insert_string(string))
892            }
893
894            Self::Lowercase => {
895                let string = args[0].as_string(&heap.borrow())?.to_lowercase();
896                Ok(heap.borrow_mut().insert_string(string))
897            }
898
899            Self::Includes => {
900                match &args[0].reify(&heap.borrow())? {
901                    // If haystack is a list, check for structural equality with any-type needle
902                    ReifiedValue::List(l, _) => {
903                        let borrowed_heap = heap.borrow();
904                        for item in (*l).iter() {
905                            if item.equals(&args[1], &borrowed_heap)? {
906                                return Ok(Value::Bool(true));
907                            }
908                        }
909                        Ok(Value::Bool(false))
910                    }
911                    // If haystack is a string, require needle to be a string and do substring search
912                    ReifiedValue::String(s, _) => {
913                        let needle = {
914                            let borrowed_heap = &heap.borrow();
915                            args[1]
916                                .as_string(borrowed_heap)
917                                .map_err(|_| RuntimeError::from("second argument must be a string"))?
918                                .to_string()
919                        };
920                        Ok(Value::Bool(s.contains(&needle)))
921                    }
922                    _ => Err(RuntimeError::from("first argument must be a list or string")),
923                }
924            }
925
926            Self::Format => {
927                let format_str = {
928                    let borrowed_heap = &heap.borrow();
929                    args[0]
930                        .as_string(borrowed_heap)
931                        .map_err(|_| RuntimeError::from("first argument must be a string"))?
932                        .to_string()
933                };
934
935                let format_args = {
936                    let borrowed_heap = &heap.borrow();
937                    &args[1..]
938                        .iter()
939                        .map(|v| v.stringify_for_display(borrowed_heap))
940                        .collect::<Vec<String>>()
941                };
942
943                Ok(heap
944                    .borrow_mut()
945                    .insert_string(format_str.format(format_args)))
946            }
947
948            // Type functions
949            Self::Typeof => Ok(heap
950                .borrow_mut()
951                .insert_string(args[0].get_type().to_string())),
952
953            Self::Arity => {
954                let arity = match args[0] {
955                    Value::Lambda(p) => p.reify(&heap.borrow()).as_lambda()?.get_arity(),
956                    Value::BuiltIn(built_in) => built_in.arity(),
957                    _ => return Err(RuntimeError::from("argument must be a function or built-in function")),
958                };
959
960                Ok(Value::Number(match arity {
961                    FunctionArity::Exact(n) => n as f64,
962                    FunctionArity::AtLeast(n) => n as f64,
963                    FunctionArity::Between(min, _max) => min as f64,
964                }))
965            }
966
967            // Record functions
968            Self::Keys => {
969                let key_strings = {
970                    let borrowed_heap = heap.borrow();
971                    args[0]
972                        .as_record(&borrowed_heap)?
973                        .keys()
974                        .cloned()
975                        .collect::<Vec<String>>()
976                };
977                let mut borrowed_heap = heap.borrow_mut();
978                let keys = key_strings
979                    .into_iter()
980                    .map(|k| borrowed_heap.insert_string(k))
981                    .collect();
982
983                Ok(borrowed_heap.insert_list(keys))
984            }
985
986            Self::Values => {
987                let values = {
988                    let borrowed_heap = heap.borrow();
989                    args[0]
990                        .as_record(&borrowed_heap)?
991                        .values()
992                        .copied()
993                        .collect::<Vec<Value>>()
994                };
995
996                Ok(heap.borrow_mut().insert_list(values))
997            }
998
999            Self::Entries => {
1000                let entries = {
1001                    let borrowed_heap = heap.borrow();
1002                    args[0]
1003                        .as_record(&borrowed_heap)?
1004                        .iter()
1005                        .map(|(k, v)| (k.clone(), *v))
1006                        .collect::<Vec<(String, Value)>>()
1007                };
1008                let mut borrowed_heap = heap.borrow_mut();
1009                let entry_values = entries
1010                    .into_iter()
1011                    .map(|(k, v)| {
1012                        let key = borrowed_heap.insert_string(k);
1013                        borrowed_heap.insert_list(vec![key, v])
1014                    })
1015                    .collect();
1016
1017                Ok(borrowed_heap.insert_list(entry_values))
1018            }
1019
1020            // List utility functions
1021            Self::GroupBy => {
1022                let func = &args[1];
1023                let list_ptr = args[0].as_list_pointer()?;
1024                let func_def = {
1025                    let borrowed_heap = heap.borrow();
1026                    get_function_def(func, &borrowed_heap)
1027                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1028                };
1029                let list_len = {
1030                    let borrowed_heap = heap.borrow();
1031                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1032                };
1033
1034                let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
1035                for idx in 0..list_len {
1036                    let item = {
1037                        let borrowed_heap = heap.borrow();
1038                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1039                        list[idx]
1040                    };
1041                    let key_result = func_def.call(
1042                        Value::Null,
1043                        vec![item],
1044                        Rc::clone(&heap),
1045                        Rc::clone(&bindings),
1046                        call_depth + 1,
1047                        source,
1048                    )?;
1049
1050                    let key = match key_result {
1051                        Value::String(_) => key_result.as_string(&heap.borrow())?.to_string(),
1052                        _ => {
1053                            return Err(RuntimeError::new(format!(
1054                                "group_by key function must return a string, but got a {}",
1055                                key_result.get_type()
1056                            )));
1057                        }
1058                    };
1059
1060                    groups.entry(key).or_default().push(item);
1061                }
1062
1063                // Convert groups to record of lists
1064                let record: IndexMap<String, Value> = groups
1065                    .into_iter()
1066                    .map(|(k, v)| (k, heap.borrow_mut().insert_list(v)))
1067                    .collect();
1068
1069                Ok(heap.borrow_mut().insert_record(record))
1070            }
1071
1072            Self::CountBy => {
1073                let func = &args[1];
1074                let list_ptr = args[0].as_list_pointer()?;
1075                let func_def = {
1076                    let borrowed_heap = heap.borrow();
1077                    get_function_def(func, &borrowed_heap)
1078                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1079                };
1080                let list_len = {
1081                    let borrowed_heap = heap.borrow();
1082                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1083                };
1084
1085                let mut counts: IndexMap<String, f64> = IndexMap::new();
1086                for idx in 0..list_len {
1087                    let item = {
1088                        let borrowed_heap = heap.borrow();
1089                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1090                        list[idx]
1091                    };
1092                    let key_result = func_def.call(
1093                        Value::Null,
1094                        vec![item],
1095                        Rc::clone(&heap),
1096                        Rc::clone(&bindings),
1097                        call_depth + 1,
1098                        source,
1099                    )?;
1100
1101                    let key = match key_result {
1102                        Value::String(_) => key_result.as_string(&heap.borrow())?.to_string(),
1103                        _ => {
1104                            return Err(RuntimeError::new(format!(
1105                                "count_by key function must return a string, but got a {}",
1106                                key_result.get_type()
1107                            )));
1108                        }
1109                    };
1110
1111                    *counts.entry(key).or_insert(0.0) += 1.0;
1112                }
1113
1114                // Convert counts to record of numbers
1115                let record: IndexMap<String, Value> = counts
1116                    .into_iter()
1117                    .map(|(k, v)| (k, Value::Number(v)))
1118                    .collect();
1119
1120                Ok(heap.borrow_mut().insert_record(record))
1121            }
1122
1123            Self::Flatten => {
1124                let mut result = vec![];
1125                let borrowed_heap = heap.borrow();
1126                let list = args[0].as_list(&borrowed_heap)?;
1127
1128                for item in list.iter().copied() {
1129                    match item {
1130                        Value::List(pointer) => {
1131                            let inner = pointer.reify(&borrowed_heap).as_list()?;
1132                            result.extend(inner.iter().copied());
1133                        }
1134                        _ => result.push(item),
1135                    }
1136                }
1137
1138                drop(borrowed_heap);
1139                Ok(heap.borrow_mut().insert_list(result))
1140            }
1141
1142            Self::Zip => {
1143                // Extract all lists from args
1144                let list_ptrs = args
1145                    .iter()
1146                    .map(|arg| {
1147                        arg.as_list_pointer()
1148                            .map_err(|_| RuntimeError::from("all arguments to zip must be lists"))
1149                    })
1150                    .collect::<Result<Vec<_>, RuntimeError>>()?;
1151
1152                // Find max length
1153                let max_len = {
1154                    let borrowed_heap = heap.borrow();
1155                    let mut max_len = 0;
1156                    for ptr in &list_ptrs {
1157                        let len = ptr.reify(&borrowed_heap).as_list()?.len();
1158                        max_len = max_len.max(len);
1159                    }
1160                    max_len
1161                };
1162
1163                // Build tuples
1164                let mut result = Vec::with_capacity(max_len);
1165                for i in 0..max_len {
1166                    let tuple = {
1167                        let borrowed_heap = heap.borrow();
1168                        let mut tuple = Vec::with_capacity(list_ptrs.len());
1169                        for ptr in &list_ptrs {
1170                            let list = ptr.reify(&borrowed_heap).as_list()?;
1171                            tuple.push(list.get(i).copied().unwrap_or(Value::Null));
1172                        }
1173                        tuple
1174                    };
1175                    result.push(heap.borrow_mut().insert_list(tuple));
1176                }
1177
1178                Ok(heap.borrow_mut().insert_list(result))
1179            }
1180
1181            Self::Chunk => {
1182                let n = args[1].as_number()? as usize;
1183
1184                if n == 0 {
1185                    return Err(RuntimeError::from("chunk size must be greater than 0"));
1186                }
1187
1188                let chunk_values = {
1189                    let borrowed_heap = heap.borrow();
1190                    let list = args[0].as_list(&borrowed_heap)?;
1191                    list.chunks(n)
1192                        .map(|chunk| chunk.to_vec())
1193                        .collect::<Vec<_>>()
1194                };
1195                let mut borrowed_heap = heap.borrow_mut();
1196                let chunks = chunk_values
1197                    .into_iter()
1198                    .map(|chunk| borrowed_heap.insert_list(chunk))
1199                    .collect();
1200
1201                Ok(borrowed_heap.insert_list(chunks))
1202            }
1203
1204            // Conversion functions
1205            Self::ToString => match args[0] {
1206                Value::String(_) => Ok(args[0]), // If it's already a string, just return it
1207                _ => {
1208                    let string = args[0].stringify_internal(&heap.borrow());
1209                    Ok(heap.borrow_mut().insert_string(string))
1210                }
1211            },
1212
1213            Self::ToNumber => match args[0] {
1214                Value::Number(_) => Ok(args[0]), // If it's already a number, just return it
1215                Value::Bool(b) => Ok(Value::Number(if b { 1.0 } else { 0.0 })),
1216                _ => {
1217                    let s = args[0].as_string(&heap.borrow())?.to_string();
1218                    let n: f64 = s.parse().map_err(|_| {
1219                        RuntimeError::new(format!("cannot convert '{}' to a number", s))
1220                    })?;
1221                    Ok(Value::Number(n))
1222                }
1223            },
1224
1225            Self::ToBool => match args[0] {
1226                Value::Bool(_) => Ok(args[0]), // If it's already a boolean, just return it
1227                Value::Number(_) => Ok(Value::Bool(args[0].as_number()? != 0.0)),
1228                _ => Err(RuntimeError::new(format!(
1229                    "expected a boolean or number, but got a {}",
1230                    args[0].get_type()
1231                ))),
1232            },
1233
1234            Self::Convert => {
1235                let value = args[0].as_number()?;
1236                let from_unit = {
1237                    let borrowed_heap = &heap.borrow();
1238                    args[1].as_string(borrowed_heap)?.to_string()
1239                };
1240                let to_unit = {
1241                    let borrowed_heap = &heap.borrow();
1242                    args[2].as_string(borrowed_heap)?.to_string()
1243                };
1244
1245                let result = units::convert(value, &from_unit, &to_unit)?;
1246                Ok(Value::Number(result))
1247            }
1248
1249            // Platform-specific functions
1250            #[cfg(not(target_arch = "wasm32"))]
1251            Self::Print => {
1252                let borrowed_heap = &heap.borrow();
1253
1254                let output = if args.len() == 1 {
1255                    args[0].stringify_internal(borrowed_heap)
1256                } else {
1257                    let format_str = args[0].as_string(borrowed_heap).map_err(|_| {
1258                        RuntimeError::from("first argument must be a formatting string if multiple arguments are given")
1259                    })?;
1260                    let format_args = &args[1..]
1261                        .iter()
1262                        .map(|v| v.stringify_internal(borrowed_heap))
1263                        .collect::<Vec<String>>();
1264                    format_str.format(format_args)
1265                };
1266
1267                // Print to stderr, to not pollute stdout
1268                eprintln!("{}", output);
1269
1270                Ok(Value::Null)
1271            }
1272
1273            #[cfg(not(target_arch = "wasm32"))]
1274            Self::TimeNow => Ok(Value::Number(
1275                std::time::SystemTime::now()
1276                    .duration_since(std::time::UNIX_EPOCH)
1277                    .unwrap()
1278                    .as_secs_f64(),
1279            )),
1280
1281            // Higher-order functions
1282            Self::Map => {
1283                let func = &args[1];
1284                let list_ptr = args[0].as_list_pointer()?;
1285                let func_def = {
1286                    let borrowed_heap = heap.borrow();
1287                    get_function_def(func, &borrowed_heap)
1288                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1289                };
1290                let list_len = {
1291                    let borrowed_heap = heap.borrow();
1292                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1293                };
1294                let func_accepts_two_args = func_def.arity().can_accept(2);
1295
1296                let mut mapped_list = Vec::with_capacity(list_len);
1297                for idx in 0..list_len {
1298                    let item = {
1299                        let borrowed_heap = heap.borrow();
1300                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1301                        list[idx]
1302                    };
1303                    let args = if func_accepts_two_args {
1304                        vec![item, Value::Number(idx as f64)]
1305                    } else {
1306                        vec![item]
1307                    };
1308
1309                    let result = func_def.call(
1310                        Value::Null,
1311                        args,
1312                        Rc::clone(&heap),
1313                        Rc::clone(&bindings),
1314                        call_depth + 1,
1315                        source,
1316                    )?;
1317                    mapped_list.push(result);
1318                }
1319
1320                Ok(heap.borrow_mut().insert_list(mapped_list))
1321            }
1322
1323            Self::Filter => {
1324                let func = &args[1];
1325                let list_ptr = args[0].as_list_pointer()?;
1326                let func_def = {
1327                    let borrowed_heap = heap.borrow();
1328                    get_function_def(func, &borrowed_heap)
1329                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1330                };
1331                let list_len = {
1332                    let borrowed_heap = heap.borrow();
1333                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1334                };
1335                let func_accepts_two_args = func_def.arity().can_accept(2);
1336
1337                let mut filtered_list = vec![];
1338                for idx in 0..list_len {
1339                    let item = {
1340                        let borrowed_heap = heap.borrow();
1341                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1342                        list[idx]
1343                    };
1344                    let args = if func_accepts_two_args {
1345                        vec![item, Value::Number(idx as f64)]
1346                    } else {
1347                        vec![item]
1348                    };
1349
1350                    let result = func_def.call(
1351                        Value::Null,
1352                        args,
1353                        Rc::clone(&heap),
1354                        Rc::clone(&bindings),
1355                        call_depth + 1,
1356                        source,
1357                    )?;
1358                    if result.as_bool()? {
1359                        filtered_list.push(item);
1360                    }
1361                }
1362
1363                Ok(heap.borrow_mut().insert_list(filtered_list))
1364            }
1365
1366            Self::Reduce => {
1367                let func = &args[1];
1368                let initial = args[2];
1369                let list_ptr = args[0].as_list_pointer()?;
1370                let func_def = {
1371                    let borrowed_heap = heap.borrow();
1372                    get_function_def(func, &borrowed_heap)
1373                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1374                };
1375                let list_len = {
1376                    let borrowed_heap = heap.borrow();
1377                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1378                };
1379                let func_accepts_three_args = func_def.arity().can_accept(3);
1380
1381                let mut accumulator = initial;
1382                for idx in 0..list_len {
1383                    let item = {
1384                        let borrowed_heap = heap.borrow();
1385                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1386                        list[idx]
1387                    };
1388                    let args = if func_accepts_three_args {
1389                        vec![accumulator, item, Value::Number(idx as f64)]
1390                    } else {
1391                        vec![accumulator, item]
1392                    };
1393
1394                    accumulator = func_def.call(
1395                        Value::Null,
1396                        args,
1397                        Rc::clone(&heap),
1398                        Rc::clone(&bindings),
1399                        call_depth + 1,
1400                        source,
1401                    )?;
1402                }
1403
1404                Ok(accumulator)
1405            }
1406
1407            Self::Every => {
1408                let func = &args[1];
1409                let list_ptr = args[0].as_list_pointer()?;
1410                let func_def = {
1411                    let borrowed_heap = heap.borrow();
1412                    get_function_def(func, &borrowed_heap)
1413                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1414                };
1415                let list_len = {
1416                    let borrowed_heap = heap.borrow();
1417                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1418                };
1419                let func_accepts_two_args = func_def.arity().can_accept(2);
1420
1421                for idx in 0..list_len {
1422                    let item = {
1423                        let borrowed_heap = heap.borrow();
1424                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1425                        list[idx]
1426                    };
1427                    let args = if func_accepts_two_args {
1428                        vec![item, Value::Number(idx as f64)]
1429                    } else {
1430                        vec![item]
1431                    };
1432
1433                    let result = func_def.call(
1434                        Value::Null,
1435                        args,
1436                        Rc::clone(&heap),
1437                        Rc::clone(&bindings),
1438                        call_depth + 1,
1439                        source,
1440                    )?;
1441                    if !result.as_bool()? {
1442                        return Ok(Value::Bool(false));
1443                    }
1444                }
1445
1446                Ok(Value::Bool(true))
1447            }
1448
1449            Self::Some => {
1450                let func = &args[1];
1451                let list_ptr = args[0].as_list_pointer()?;
1452                let func_def = {
1453                    let borrowed_heap = heap.borrow();
1454                    get_function_def(func, &borrowed_heap)
1455                        .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1456                };
1457                let list_len = {
1458                    let borrowed_heap = heap.borrow();
1459                    list_ptr.reify(&borrowed_heap).as_list()?.len()
1460                };
1461                let func_accepts_two_args = func_def.arity().can_accept(2);
1462
1463                for idx in 0..list_len {
1464                    let item = {
1465                        let borrowed_heap = heap.borrow();
1466                        let list = list_ptr.reify(&borrowed_heap).as_list()?;
1467                        list[idx]
1468                    };
1469                    let args = if func_accepts_two_args {
1470                        vec![item, Value::Number(idx as f64)]
1471                    } else {
1472                        vec![item]
1473                    };
1474
1475                    let result = func_def.call(
1476                        Value::Null,
1477                        args,
1478                        Rc::clone(&heap),
1479                        Rc::clone(&bindings),
1480                        call_depth + 1,
1481                        source,
1482                    )?;
1483                    if result.as_bool()? {
1484                        return Ok(Value::Bool(true));
1485                    }
1486                }
1487
1488                Ok(Value::Bool(false))
1489            }
1490
1491            Self::SortBy => {
1492                let func = &args[1];
1493                let mut list = {
1494                    let borrowed_heap = &heap.borrow();
1495                    args[0].as_list(borrowed_heap)?.clone()
1496                };
1497
1498                list.sort_by(|a, b| {
1499                    // Only look up the function once, not twice
1500                    let func_def = get_function_def(func, &heap.borrow());
1501
1502                    match func_def {
1503                        Some(fd) => {
1504                            let result_a = fd.call(
1505                                Value::Null,
1506                                vec![*a],
1507                                Rc::clone(&heap),
1508                                Rc::clone(&bindings),
1509                                call_depth + 1,
1510                                source,
1511                            );
1512                            let result_b = fd.call(
1513                                Value::Null,
1514                                vec![*b],
1515                                Rc::clone(&heap),
1516                                Rc::clone(&bindings),
1517                                call_depth + 1,
1518                                source,
1519                            );
1520
1521                            match (result_a, result_b) {
1522                                (Ok(val_a), Ok(val_b)) => val_a
1523                                    .compare(&val_b, &heap.borrow())
1524                                    .unwrap_or(None)
1525                                    .unwrap_or(std::cmp::Ordering::Equal),
1526                                _ => std::cmp::Ordering::Equal,
1527                            }
1528                        }
1529                        _ => std::cmp::Ordering::Equal,
1530                    }
1531                });
1532
1533                Ok(heap.borrow_mut().insert_list(list))
1534            }
1535
1536            // Unchecked comparison functions - return false instead of error for type mismatches
1537            Self::Ugt => match args[0].compare(&args[1], &heap.borrow())? {
1538                Some(std::cmp::Ordering::Greater) => Ok(Value::Bool(true)),
1539                _ => Ok(Value::Bool(false)),
1540            },
1541            Self::Ult => match args[0].compare(&args[1], &heap.borrow())? {
1542                Some(std::cmp::Ordering::Less) => Ok(Value::Bool(true)),
1543                _ => Ok(Value::Bool(false)),
1544            },
1545            Self::Ugte => match args[0].compare(&args[1], &heap.borrow())? {
1546                Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal) => {
1547                    Ok(Value::Bool(true))
1548                }
1549                _ => Ok(Value::Bool(false)),
1550            },
1551            Self::Ulte => match args[0].compare(&args[1], &heap.borrow())? {
1552                Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal) => Ok(Value::Bool(true)),
1553                _ => Ok(Value::Bool(false)),
1554            },
1555        }
1556    }
1557
1558    pub fn all() -> Vec<Self> {
1559        vec![
1560            Self::Sqrt,
1561            Self::Sin,
1562            Self::Cos,
1563            Self::Tan,
1564            Self::Asin,
1565            Self::Acos,
1566            Self::Atan,
1567            Self::Log,
1568            Self::Log10,
1569            Self::Exp,
1570            Self::Abs,
1571            Self::Floor,
1572            Self::Ceil,
1573            Self::Round,
1574            Self::Trunc,
1575            Self::Random,
1576            Self::Min,
1577            Self::Max,
1578            Self::Avg,
1579            Self::Sum,
1580            Self::Prod,
1581            Self::Median,
1582            Self::Percentile,
1583            Self::Range,
1584            Self::Len,
1585            Self::Head,
1586            Self::Tail,
1587            Self::Slice,
1588            Self::Concat,
1589            Self::Dot,
1590            Self::Unique,
1591            Self::Sort,
1592            Self::SortBy,
1593            Self::Reverse,
1594            Self::Any,
1595            Self::All,
1596            Self::Map,
1597            Self::Reduce,
1598            Self::Filter,
1599            Self::Every,
1600            Self::Some,
1601            Self::Split,
1602            Self::Join,
1603            Self::Replace,
1604            Self::Trim,
1605            Self::Uppercase,
1606            Self::Lowercase,
1607            Self::ToString,
1608            Self::ToNumber,
1609            Self::ToBool,
1610            Self::Convert,
1611            Self::Includes,
1612            Self::Format,
1613            Self::Typeof,
1614            Self::Arity,
1615            Self::Keys,
1616            Self::Values,
1617            Self::Entries,
1618            Self::GroupBy,
1619            Self::CountBy,
1620            Self::Flatten,
1621            Self::Zip,
1622            Self::Chunk,
1623            Self::Ugt,
1624            Self::Ult,
1625            Self::Ugte,
1626            Self::Ulte,
1627            #[cfg(not(target_arch = "wasm32"))]
1628            Self::Print,
1629            #[cfg(not(target_arch = "wasm32"))]
1630            Self::TimeNow,
1631        ]
1632    }
1633
1634    pub fn all_names() -> Vec<&'static str> {
1635        Self::all().iter().map(|f| f.name()).collect()
1636    }
1637}
1638
1639impl FunctionDef {
1640    pub fn get_name(&self) -> String {
1641        match self {
1642            FunctionDef::BuiltIn(built_in) => {
1643                format!("built-in function \"{}\"", built_in.name())
1644            }
1645            FunctionDef::Lambda(LambdaDef { name, .. }) => name
1646                .clone()
1647                .map_or(String::from("anonymous function"), |n| {
1648                    format!("function \"{}\"", n)
1649                }),
1650        }
1651    }
1652
1653    pub fn arity(&self) -> FunctionArity {
1654        match self {
1655            FunctionDef::BuiltIn(built_in) => built_in.arity(),
1656            FunctionDef::Lambda(lambda_def) => lambda_def.get_arity(),
1657        }
1658    }
1659
1660    pub fn check_arity(&self, arg_count: usize) -> Result<(), RuntimeError> {
1661        match self {
1662            FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1663                FunctionArity::Exact(expected) => {
1664                    if arg_count == expected {
1665                        Ok(())
1666                    } else {
1667                        Err(RuntimeError::new(format!(
1668                            "{} takes exactly {} arguments, but {} were given",
1669                            self.get_name(),
1670                            expected,
1671                            arg_count
1672                        )))
1673                    }
1674                }
1675                FunctionArity::AtLeast(expected) => {
1676                    if arg_count >= expected {
1677                        Ok(())
1678                    } else {
1679                        Err(RuntimeError::new(format!(
1680                            "{} takes at least {} arguments, but {} were given",
1681                            self.get_name(),
1682                            expected,
1683                            arg_count
1684                        )))
1685                    }
1686                }
1687                FunctionArity::Between(min, max) => {
1688                    if arg_count >= min && arg_count <= max {
1689                        Ok(())
1690                    } else {
1691                        Err(RuntimeError::new(format!(
1692                            "{} takes between {} and {} arguments, but {} were given",
1693                            self.get_name(),
1694                            min,
1695                            max,
1696                            arg_count
1697                        )))
1698                    }
1699                }
1700            },
1701            FunctionDef::Lambda(def) => {
1702                let arity = def.get_arity();
1703
1704                match arity {
1705                    FunctionArity::Exact(expected) => {
1706                        if arg_count == expected {
1707                            Ok(())
1708                        } else {
1709                            Err(RuntimeError::new(format!(
1710                                "{} takes exactly {} arguments, but {} were given",
1711                                self.get_name(),
1712                                expected,
1713                                arg_count
1714                            )))
1715                        }
1716                    }
1717                    FunctionArity::AtLeast(expected) => {
1718                        if arg_count >= expected {
1719                            Ok(())
1720                        } else {
1721                            Err(RuntimeError::new(format!(
1722                                "{} takes at least {} arguments, but {} were given",
1723                                self.get_name(),
1724                                expected,
1725                                arg_count
1726                            )))
1727                        }
1728                    }
1729                    FunctionArity::Between(min, max) => {
1730                        if arg_count >= min && arg_count <= max {
1731                            Ok(())
1732                        } else {
1733                            Err(RuntimeError::new(format!(
1734                                "{} takes between {} and {} arguments, but {} were given",
1735                                self.get_name(),
1736                                min,
1737                                max,
1738                                arg_count
1739                            )))
1740                        }
1741                    }
1742                }
1743            }
1744        }
1745    }
1746
1747    pub fn call(
1748        &self,
1749        this_value: Value,
1750        args: Vec<Value>,
1751        heap: Rc<RefCell<Heap>>,
1752        bindings: Rc<Environment>,
1753        call_depth: usize,
1754        source: &str,
1755    ) -> Result<Value, RuntimeError> {
1756        #[cfg(not(target_arch = "wasm32"))]
1757        let start = std::time::Instant::now();
1758
1759        self.check_arity(args.len())?;
1760
1761        if call_depth > 1000 {
1762            return Err(RuntimeError::new(format!(
1763                "in {}: maximum call depth of 1000 exceeded",
1764                self.get_name()
1765            )));
1766        }
1767
1768        match self {
1769            FunctionDef::Lambda(LambdaDef {
1770                name,
1771                args: expected_args,
1772                body,
1773                scope,
1774                source: lambda_source,
1775            }) => {
1776                #[cfg(not(target_arch = "wasm32"))]
1777                let start_var_env = std::time::Instant::now();
1778
1779                // Build local bindings for this call (O(1) - no clone of parent environment!)
1780                let mut local_bindings = HashMap::new();
1781
1782                // Add self-reference if named
1783                if let Some(fn_name) = name {
1784                    local_bindings.insert(fn_name.clone(), this_value);
1785                }
1786
1787                // Preserve inputs if present in parent
1788                if let Some(inputs) = bindings.get("inputs") {
1789                    local_bindings.insert(String::from("inputs"), inputs);
1790                }
1791
1792                // Add function arguments (highest precedence)
1793                for (idx, expected_arg) in expected_args.iter().enumerate() {
1794                    match expected_arg {
1795                        LambdaArg::Required(arg_name) => {
1796                            local_bindings.insert(arg_name.clone(), args[idx]);
1797                        }
1798                        LambdaArg::Optional(arg_name) => {
1799                            local_bindings.insert(
1800                                arg_name.clone(),
1801                                args.get(idx).copied().unwrap_or(Value::Null),
1802                            );
1803                        }
1804                        LambdaArg::Rest(arg_name) => {
1805                            local_bindings.insert(
1806                                arg_name.clone(),
1807                                heap.borrow_mut()
1808                                    .insert_list(args.iter().skip(idx).copied().collect()),
1809                            );
1810                        }
1811                    }
1812                }
1813
1814                #[cfg(not(target_arch = "wasm32"))]
1815                let end_var_env = std::time::Instant::now();
1816
1817                let parent_env = if scope.is_empty() {
1818                    Rc::clone(&bindings)
1819                } else {
1820                    Rc::new(Environment::extend_shared(
1821                        Rc::clone(&bindings),
1822                        scope.as_rc(),
1823                    ))
1824                };
1825                // Create new environment that extends captured scope (O(1) instead of O(n) clone!)
1826                let new_env = Rc::new(Environment::extend_with(parent_env, local_bindings));
1827
1828                let return_value = evaluate_ast(
1829                    body,
1830                    Rc::clone(&heap),
1831                    new_env,
1832                    call_depth + 1,
1833                    lambda_source.clone(),
1834                )
1835                .map_err(|error| error.with_function_context(&self.get_name()));
1836
1837                #[cfg(not(target_arch = "wasm32"))]
1838                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1839                    name: self.get_name(),
1840                    start,
1841                    end: std::time::Instant::now(),
1842                    start_var_env: Some(start_var_env),
1843                    end_var_env: Some(end_var_env),
1844                });
1845
1846                return_value
1847            }
1848            FunctionDef::BuiltIn(built_in) => {
1849                let return_value = built_in
1850                    .call(args, heap, bindings, call_depth + 1, source)
1851                    .map_err(|error| error.with_function_context(&self.get_name()));
1852
1853                #[cfg(not(target_arch = "wasm32"))]
1854                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1855                    name: self.get_name(),
1856                    start,
1857                    end: std::time::Instant::now(),
1858                    start_var_env: None,
1859                    end_var_env: None,
1860                });
1861
1862                return_value
1863            }
1864        }
1865    }
1866}
1867
1868pub fn is_built_in_function(ident: &str) -> bool {
1869    BuiltInFunction::from_ident(ident).is_some()
1870}
1871
1872pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1873    BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1874}
1875
1876pub fn get_built_in_function_idents() -> Vec<&'static str> {
1877    BuiltInFunction::all_names()
1878}
1879
1880pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1881    match value {
1882        Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1883            pointer.reify(heap).as_lambda().ok()?.clone(),
1884        )),
1885        Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1886        _ => None,
1887    }
1888}
1889
1890#[cfg(test)]
1891mod tests {
1892    use super::*;
1893    use crate::environment::Environment;
1894    use std::cell::RefCell;
1895    use std::rc::Rc;
1896
1897    #[test]
1898    fn test_range_function() {
1899        // Test single argument
1900        let heap = Rc::new(RefCell::new(Heap::new()));
1901        let bindings = Rc::new(Environment::new());
1902        let range_fn = BuiltInFunction::Range;
1903
1904        // Test range(4) - exclusive, so [0, 1, 2, 3]
1905        let args = vec![Value::Number(4.0)];
1906        let result = range_fn
1907            .call(args, heap.clone(), bindings.clone(), 0, "")
1908            .unwrap();
1909
1910        let heap_borrow = heap.borrow();
1911        let list = result.as_list(&heap_borrow).unwrap();
1912        assert_eq!(list.len(), 4);
1913        assert_eq!(list[0], Value::Number(0.0));
1914        assert_eq!(list[3], Value::Number(3.0));
1915    }
1916
1917    #[test]
1918    fn test_range_function_two_args() {
1919        // Test two arguments
1920        let heap = Rc::new(RefCell::new(Heap::new()));
1921        let bindings = Rc::new(Environment::new());
1922        let range_fn = BuiltInFunction::Range;
1923
1924        // Test range(4, 10) - exclusive, so [4, 5, 6, 7, 8, 9]
1925        let args = vec![Value::Number(4.0), Value::Number(10.0)];
1926        let result = range_fn
1927            .call(args, heap.clone(), bindings.clone(), 0, "")
1928            .unwrap();
1929
1930        let heap_borrow = heap.borrow();
1931        let list = result.as_list(&heap_borrow).unwrap();
1932        assert_eq!(list.len(), 6);
1933        assert_eq!(list[0], Value::Number(4.0));
1934        assert_eq!(list[5], Value::Number(9.0));
1935    }
1936
1937    #[test]
1938    fn test_round_function_single_arg() {
1939        let heap = Rc::new(RefCell::new(Heap::new()));
1940        let bindings = Rc::new(Environment::new());
1941        let round_fn = BuiltInFunction::Round;
1942
1943        // Test basic rounding
1944        let test_cases = vec![
1945            (42.4, 42.0),
1946            (42.5, 43.0),
1947            (42.6, 43.0),
1948            (-42.4, -42.0),
1949            (-42.5, -43.0),
1950            (-42.6, -43.0),
1951            (0.0, 0.0),
1952            (1.999, 2.0),
1953            (-1.999, -2.0),
1954        ];
1955
1956        for (input, expected) in test_cases {
1957            let args = vec![Value::Number(input)];
1958            let result = round_fn
1959                .call(args, heap.clone(), bindings.clone(), 0, "")
1960                .unwrap();
1961            assert_eq!(
1962                result,
1963                Value::Number(expected),
1964                "round({}) should be {}",
1965                input,
1966                expected
1967            );
1968        }
1969    }
1970
1971    #[test]
1972    fn test_round_function_with_decimal_places() {
1973        let heap = Rc::new(RefCell::new(Heap::new()));
1974        let bindings = Rc::new(Environment::new());
1975        let round_fn = BuiltInFunction::Round;
1976
1977        // Test rounding to decimal places
1978        let test_cases = vec![
1979            (42.4543, 0.0, 42.0),
1980            (42.4543, 1.0, 42.5),
1981            (42.4543, 2.0, 42.45),
1982            (42.4543, 3.0, 42.454),
1983            (42.4543, 4.0, 42.4543),
1984            (4.14159, 4.0, 4.1416),
1985            (0.005, 2.0, 0.01),
1986            (0.995, 2.0, 1.0),
1987            // Note: 9.995 has floating point representation issues
1988            // In binary, it's actually slightly less than 9.995
1989            (9.995, 2.0, 9.99),
1990            (-9.995, 2.0, -9.99),
1991        ];
1992
1993        for (input, places, expected) in test_cases {
1994            let args = vec![Value::Number(input), Value::Number(places)];
1995            let result = round_fn
1996                .call(args, heap.clone(), bindings.clone(), 0, "")
1997                .unwrap();
1998
1999            // Use approximate comparison for floating point
2000            if let Value::Number(result_num) = result {
2001                assert!(
2002                    (result_num - expected).abs() < 1e-10,
2003                    "round({}, {}) = {} should be close to {}",
2004                    input,
2005                    places,
2006                    result_num,
2007                    expected
2008                );
2009            } else {
2010                panic!("Expected number result");
2011            }
2012        }
2013    }
2014
2015    #[test]
2016    fn test_round_function_negative_decimal_places() {
2017        let heap = Rc::new(RefCell::new(Heap::new()));
2018        let bindings = Rc::new(Environment::new());
2019        let round_fn = BuiltInFunction::Round;
2020
2021        // Test rounding to tens, hundreds, etc.
2022        let test_cases = vec![
2023            (1234.567, -1.0, 1230.0),
2024            (1234.567, -2.0, 1200.0),
2025            (1234.567, -3.0, 1000.0),
2026            (1234.567, -4.0, 0.0),
2027            (5678.9, -1.0, 5680.0),
2028            (5678.9, -2.0, 5700.0),
2029            (5678.9, -3.0, 6000.0),
2030            (-1234.567, -1.0, -1230.0),
2031            (-1234.567, -2.0, -1200.0),
2032            (1500.0, -3.0, 2000.0),
2033            (-1500.0, -3.0, -2000.0),
2034        ];
2035
2036        for (input, places, expected) in test_cases {
2037            let args = vec![Value::Number(input), Value::Number(places)];
2038            let result = round_fn
2039                .call(args, heap.clone(), bindings.clone(), 0, "")
2040                .unwrap();
2041            assert_eq!(
2042                result,
2043                Value::Number(expected),
2044                "round({}, {}) should be {}",
2045                input,
2046                places,
2047                expected
2048            );
2049        }
2050    }
2051
2052    #[test]
2053    fn test_round_function_edge_cases() {
2054        let heap = Rc::new(RefCell::new(Heap::new()));
2055        let bindings = Rc::new(Environment::new());
2056        let round_fn = BuiltInFunction::Round;
2057
2058        // Test edge cases
2059        let test_cases = vec![
2060            (f64::INFINITY, 0.0, f64::INFINITY),
2061            (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
2062            (0.0, 5.0, 0.0),
2063            (-0.0, 5.0, -0.0),
2064            (1e-10, 5.0, 0.0),
2065            (1e-10, 15.0, 1e-10),
2066        ];
2067
2068        for (input, places, expected) in test_cases {
2069            let args = vec![Value::Number(input), Value::Number(places)];
2070            let result = round_fn
2071                .call(args, heap.clone(), bindings.clone(), 0, "")
2072                .unwrap();
2073
2074            if let Value::Number(result_num) = result {
2075                if expected.is_infinite() {
2076                    assert!(
2077                        result_num.is_infinite()
2078                            && result_num.is_sign_positive() == expected.is_sign_positive(),
2079                        "round({}, {}) should be {}",
2080                        input,
2081                        places,
2082                        expected
2083                    );
2084                } else if expected == 0.0 || expected == -0.0 {
2085                    assert!(
2086                        result_num.abs() < 1e-10,
2087                        "round({}, {}) = {} should be close to 0",
2088                        input,
2089                        places,
2090                        result_num
2091                    );
2092                } else {
2093                    assert!(
2094                        (result_num - expected).abs() < 1e-15,
2095                        "round({}, {}) = {} should be close to {}",
2096                        input,
2097                        places,
2098                        result_num,
2099                        expected
2100                    );
2101                }
2102            } else {
2103                panic!("Expected number result");
2104            }
2105        }
2106    }
2107
2108    #[test]
2109    fn test_random_function_deterministic() {
2110        let heap = Rc::new(RefCell::new(Heap::new()));
2111        let bindings = Rc::new(Environment::new());
2112        let random_fn = BuiltInFunction::Random;
2113
2114        // Test that same seed produces same result
2115        let seed = 42.0;
2116        let args1 = vec![Value::Number(seed)];
2117        let result1 = random_fn
2118            .call(args1, heap.clone(), bindings.clone(), 0, "")
2119            .unwrap();
2120
2121        let args2 = vec![Value::Number(seed)];
2122        let result2 = random_fn
2123            .call(args2, heap.clone(), bindings.clone(), 0, "")
2124            .unwrap();
2125
2126        assert_eq!(result1, result2, "Same seed should produce same result");
2127    }
2128
2129    #[test]
2130    fn test_random_function_different_seeds() {
2131        let heap = Rc::new(RefCell::new(Heap::new()));
2132        let bindings = Rc::new(Environment::new());
2133        let random_fn = BuiltInFunction::Random;
2134
2135        // Test that different seeds produce different results
2136        let args1 = vec![Value::Number(42.0)];
2137        let result1 = random_fn
2138            .call(args1, heap.clone(), bindings.clone(), 0, "")
2139            .unwrap();
2140
2141        let args2 = vec![Value::Number(100.0)];
2142        let result2 = random_fn
2143            .call(args2, heap.clone(), bindings.clone(), 0, "")
2144            .unwrap();
2145
2146        assert_ne!(
2147            result1, result2,
2148            "Different seeds should produce different results"
2149        );
2150    }
2151
2152    #[test]
2153    fn test_random_function_range() {
2154        let heap = Rc::new(RefCell::new(Heap::new()));
2155        let bindings = Rc::new(Environment::new());
2156        let random_fn = BuiltInFunction::Random;
2157
2158        // Test that output is in range [0, 1)
2159        for seed in [0.0, 1.0, 42.0, 100.0, 999.0, 12345.0] {
2160            let args = vec![Value::Number(seed)];
2161            let result = random_fn
2162                .call(args, heap.clone(), bindings.clone(), 0, "")
2163                .unwrap();
2164
2165            if let Value::Number(num) = result {
2166                assert!(
2167                    (0.0..1.0).contains(&num),
2168                    "Random value {} should be in range [0, 1)",
2169                    num
2170                );
2171            } else {
2172                panic!("Expected number result");
2173            }
2174        }
2175    }
2176
2177    #[test]
2178    fn test_random_function_negative_seed() {
2179        let heap = Rc::new(RefCell::new(Heap::new()));
2180        let bindings = Rc::new(Environment::new());
2181        let random_fn = BuiltInFunction::Random;
2182
2183        // Test with negative seeds (they get cast to u64)
2184        let args = vec![Value::Number(-42.0)];
2185        let result = random_fn
2186            .call(args, heap.clone(), bindings.clone(), 0, "")
2187            .unwrap();
2188
2189        if let Value::Number(num) = result {
2190            assert!(
2191                (0.0..1.0).contains(&num),
2192                "Random value {} should be in range [0, 1)",
2193                num
2194            );
2195        } else {
2196            panic!("Expected number result");
2197        }
2198    }
2199
2200    #[test]
2201    fn test_map_with_index() {
2202        use crate::values::{CapturedScope, LambdaDef};
2203
2204        let heap = Rc::new(RefCell::new(Heap::new()));
2205        let bindings = Rc::new(Environment::new());
2206
2207        // Create a list [10, 20, 30]
2208        let list = heap.borrow_mut().insert_list(vec![
2209            Value::Number(10.0),
2210            Value::Number(20.0),
2211            Value::Number(30.0),
2212        ]);
2213
2214        // Create a lambda (x, i) => x + i
2215        let lambda = LambdaDef {
2216            name: None,
2217            args: vec![
2218                crate::values::LambdaArg::Required("x".to_string()),
2219                crate::values::LambdaArg::Required("i".to_string()),
2220            ],
2221            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2222                op: crate::ast::BinaryOp::Add,
2223                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2224                    "x".to_string(),
2225                ))),
2226                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2227                    "i".to_string(),
2228                ))),
2229            }),
2230            scope: CapturedScope::default(),
2231            source: Rc::from(""),
2232        };
2233        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2234
2235        // Call map
2236        let result = BuiltInFunction::Map
2237            .call(
2238                vec![list, lambda_value],
2239                heap.clone(),
2240                bindings.clone(),
2241                0,
2242                "",
2243            )
2244            .unwrap();
2245
2246        // Verify result is [10, 21, 32]
2247        let heap_borrow = heap.borrow();
2248        let result_list = result.as_list(&heap_borrow).unwrap();
2249        assert_eq!(result_list.len(), 3);
2250        assert_eq!(result_list[0], Value::Number(10.0)); // 10 + 0
2251        assert_eq!(result_list[1], Value::Number(21.0)); // 20 + 1
2252        assert_eq!(result_list[2], Value::Number(32.0)); // 30 + 2
2253    }
2254
2255    #[test]
2256    fn test_filter_with_index() {
2257        use crate::values::{CapturedScope, LambdaDef};
2258
2259        let heap = Rc::new(RefCell::new(Heap::new()));
2260        let bindings = Rc::new(Environment::new());
2261
2262        // Create a list [10, 20, 30, 40]
2263        let list = heap.borrow_mut().insert_list(vec![
2264            Value::Number(10.0),
2265            Value::Number(20.0),
2266            Value::Number(30.0),
2267            Value::Number(40.0),
2268        ]);
2269
2270        // Create a lambda (x, i) => i > 1
2271        let lambda = LambdaDef {
2272            name: None,
2273            args: vec![
2274                crate::values::LambdaArg::Required("x".to_string()),
2275                crate::values::LambdaArg::Required("i".to_string()),
2276            ],
2277            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2278                op: crate::ast::BinaryOp::Greater,
2279                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2280                    "i".to_string(),
2281                ))),
2282                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(1.0))),
2283            }),
2284            scope: CapturedScope::default(),
2285            source: Rc::from(""),
2286        };
2287        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2288
2289        // Call filter
2290        let result = BuiltInFunction::Filter
2291            .call(
2292                vec![list, lambda_value],
2293                heap.clone(),
2294                bindings.clone(),
2295                0,
2296                "",
2297            )
2298            .unwrap();
2299
2300        // Verify result is [30, 40] (indices 2 and 3)
2301        let heap_borrow = heap.borrow();
2302        let result_list = result.as_list(&heap_borrow).unwrap();
2303        assert_eq!(result_list.len(), 2);
2304        assert_eq!(result_list[0], Value::Number(30.0));
2305        assert_eq!(result_list[1], Value::Number(40.0));
2306    }
2307
2308    #[test]
2309    fn test_reduce_with_index() {
2310        use crate::values::{CapturedScope, LambdaDef};
2311
2312        let heap = Rc::new(RefCell::new(Heap::new()));
2313        let bindings = Rc::new(Environment::new());
2314
2315        // Create a list [10, 20, 30]
2316        let list = heap.borrow_mut().insert_list(vec![
2317            Value::Number(10.0),
2318            Value::Number(20.0),
2319            Value::Number(30.0),
2320        ]);
2321
2322        // Create a lambda (acc, x, i) => acc + x + i
2323        let lambda = LambdaDef {
2324            name: None,
2325            args: vec![
2326                crate::values::LambdaArg::Required("acc".to_string()),
2327                crate::values::LambdaArg::Required("x".to_string()),
2328                crate::values::LambdaArg::Required("i".to_string()),
2329            ],
2330            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2331                op: crate::ast::BinaryOp::Add,
2332                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2333                    op: crate::ast::BinaryOp::Add,
2334                    left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2335                        "acc".to_string(),
2336                    ))),
2337                    right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2338                        "x".to_string(),
2339                    ))),
2340                })),
2341                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2342                    "i".to_string(),
2343                ))),
2344            }),
2345            scope: CapturedScope::default(),
2346            source: Rc::from(""),
2347        };
2348        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2349
2350        // Call reduce with initial value 0
2351        let result = BuiltInFunction::Reduce
2352            .call(
2353                vec![list, lambda_value, Value::Number(0.0)],
2354                heap.clone(),
2355                bindings.clone(),
2356                0,
2357                "",
2358            )
2359            .unwrap();
2360
2361        // Verify result is 63 (0 + 10+0 + 20+1 + 30+2)
2362        assert_eq!(result, Value::Number(63.0));
2363    }
2364
2365    #[test]
2366    fn test_map_backward_compatible() {
2367        use crate::values::{CapturedScope, LambdaDef};
2368
2369        let heap = Rc::new(RefCell::new(Heap::new()));
2370        let bindings = Rc::new(Environment::new());
2371
2372        // Create a list [10, 20, 30]
2373        let list = heap.borrow_mut().insert_list(vec![
2374            Value::Number(10.0),
2375            Value::Number(20.0),
2376            Value::Number(30.0),
2377        ]);
2378
2379        // Create a lambda x => x * 2 (only one argument)
2380        let lambda = LambdaDef {
2381            name: None,
2382            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2383            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2384                op: crate::ast::BinaryOp::Multiply,
2385                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2386                    "x".to_string(),
2387                ))),
2388                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(2.0))),
2389            }),
2390            scope: CapturedScope::default(),
2391            source: Rc::from(""),
2392        };
2393        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2394
2395        // Call map
2396        let result = BuiltInFunction::Map
2397            .call(
2398                vec![list, lambda_value],
2399                heap.clone(),
2400                bindings.clone(),
2401                0,
2402                "",
2403            )
2404            .unwrap();
2405
2406        // Verify result is [20, 40, 60] - backward compatible, no index passed
2407        let heap_borrow = heap.borrow();
2408        let result_list = result.as_list(&heap_borrow).unwrap();
2409        assert_eq!(result_list.len(), 3);
2410        assert_eq!(result_list[0], Value::Number(20.0));
2411        assert_eq!(result_list[1], Value::Number(40.0));
2412        assert_eq!(result_list[2], Value::Number(60.0));
2413    }
2414
2415    #[test]
2416    fn test_convert_length() {
2417        let heap = Rc::new(RefCell::new(Heap::new()));
2418        let bindings = Rc::new(Environment::new());
2419        let convert_fn = BuiltInFunction::Convert;
2420
2421        // Test km to m
2422        let from_unit = heap.borrow_mut().insert_string("km".to_string());
2423        let to_unit = heap.borrow_mut().insert_string("m".to_string());
2424        let args = vec![Value::Number(1.0), from_unit, to_unit];
2425        let result = convert_fn
2426            .call(args, heap.clone(), bindings.clone(), 0, "")
2427            .unwrap();
2428        assert_eq!(result, Value::Number(1000.0));
2429
2430        // Test miles to km
2431        let from_unit = heap.borrow_mut().insert_string("miles".to_string());
2432        let to_unit = heap.borrow_mut().insert_string("km".to_string());
2433        let args = vec![Value::Number(1.0), from_unit, to_unit];
2434        let result = convert_fn
2435            .call(args, heap.clone(), bindings.clone(), 0, "")
2436            .unwrap();
2437        if let Value::Number(n) = result {
2438            assert!((n - 1.609344).abs() < 1e-6);
2439        } else {
2440            panic!("Expected number");
2441        }
2442    }
2443
2444    #[test]
2445    fn test_convert_temperature() {
2446        let heap = Rc::new(RefCell::new(Heap::new()));
2447        let bindings = Rc::new(Environment::new());
2448        let convert_fn = BuiltInFunction::Convert;
2449
2450        // Test 0°C to °F (should be 32°F)
2451        let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2452        let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2453        let args = vec![Value::Number(0.0), from_unit, to_unit];
2454        let result = convert_fn
2455            .call(args, heap.clone(), bindings.clone(), 0, "")
2456            .unwrap();
2457        if let Value::Number(n) = result {
2458            assert!((n - 32.0).abs() < 1e-10);
2459        }
2460
2461        // Test 100°C to °F (should be 212°F)
2462        let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2463        let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2464        let args = vec![Value::Number(100.0), from_unit, to_unit];
2465        let result = convert_fn
2466            .call(args, heap.clone(), bindings.clone(), 0, "")
2467            .unwrap();
2468        if let Value::Number(n) = result {
2469            assert!((n - 212.0).abs() < 1e-10);
2470        }
2471    }
2472
2473    #[test]
2474    fn test_convert_mass() {
2475        let heap = Rc::new(RefCell::new(Heap::new()));
2476        let bindings = Rc::new(Environment::new());
2477        let convert_fn = BuiltInFunction::Convert;
2478
2479        // Test 1 kg to lbs
2480        let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2481        let to_unit = heap.borrow_mut().insert_string("lbs".to_string());
2482        let args = vec![Value::Number(1.0), from_unit, to_unit];
2483        let result = convert_fn
2484            .call(args, heap.clone(), bindings.clone(), 0, "")
2485            .unwrap();
2486        if let Value::Number(n) = result {
2487            assert!((n - 2.20462).abs() < 0.001);
2488        }
2489    }
2490
2491    #[test]
2492    fn test_convert_information_storage() {
2493        let heap = Rc::new(RefCell::new(Heap::new()));
2494        let bindings = Rc::new(Environment::new());
2495        let convert_fn = BuiltInFunction::Convert;
2496
2497        // Test 1 kibibyte to bytes
2498        let from_unit = heap.borrow_mut().insert_string("kibibytes".to_string());
2499        let to_unit = heap.borrow_mut().insert_string("bytes".to_string());
2500        let args = vec![Value::Number(1.0), from_unit, to_unit];
2501        let result = convert_fn
2502            .call(args, heap.clone(), bindings.clone(), 0, "")
2503            .unwrap();
2504        assert_eq!(result, Value::Number(1024.0));
2505    }
2506
2507    #[test]
2508    fn test_convert_same_unit() {
2509        let heap = Rc::new(RefCell::new(Heap::new()));
2510        let bindings = Rc::new(Environment::new());
2511        let convert_fn = BuiltInFunction::Convert;
2512
2513        // Test converting to same unit
2514        let from_unit = heap.borrow_mut().insert_string("meters".to_string());
2515        let to_unit = heap.borrow_mut().insert_string("m".to_string());
2516        let args = vec![Value::Number(42.0), from_unit, to_unit];
2517        let result = convert_fn
2518            .call(args, heap.clone(), bindings.clone(), 0, "")
2519            .unwrap();
2520        assert_eq!(result, Value::Number(42.0));
2521    }
2522
2523    #[test]
2524    fn test_convert_incompatible_units() {
2525        let heap = Rc::new(RefCell::new(Heap::new()));
2526        let bindings = Rc::new(Environment::new());
2527        let convert_fn = BuiltInFunction::Convert;
2528
2529        // Test incompatible units
2530        let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2531        let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2532        let args = vec![Value::Number(1.0), from_unit, to_unit];
2533        let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2534        assert!(result.is_err());
2535        assert!(result.unwrap_err().to_string().contains("Cannot convert"));
2536    }
2537
2538    #[test]
2539    fn test_convert_unknown_unit() {
2540        let heap = Rc::new(RefCell::new(Heap::new()));
2541        let bindings = Rc::new(Environment::new());
2542        let convert_fn = BuiltInFunction::Convert;
2543
2544        // Test unknown unit
2545        let from_unit = heap.borrow_mut().insert_string("foobar".to_string());
2546        let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2547        let args = vec![Value::Number(1.0), from_unit, to_unit];
2548        let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2549        assert!(result.is_err());
2550        assert!(result.unwrap_err().to_string().contains("Unknown unit"));
2551    }
2552
2553    #[test]
2554    fn test_convert_case_insensitive() {
2555        let heap = Rc::new(RefCell::new(Heap::new()));
2556        let bindings = Rc::new(Environment::new());
2557        let convert_fn = BuiltInFunction::Convert;
2558
2559        // Test case insensitivity
2560        let from_unit = heap.borrow_mut().insert_string("KM".to_string());
2561        let to_unit = heap.borrow_mut().insert_string("M".to_string());
2562        let args = vec![Value::Number(1.0), from_unit, to_unit];
2563        let result = convert_fn
2564            .call(args, heap.clone(), bindings.clone(), 0, "")
2565            .unwrap();
2566        assert_eq!(result, Value::Number(1000.0));
2567    }
2568
2569    #[test]
2570    fn test_flatten() {
2571        let heap = Rc::new(RefCell::new(Heap::new()));
2572        let bindings = Rc::new(Environment::new());
2573
2574        // Create [[1, 2], [3, 4]]
2575        let inner1 = heap
2576            .borrow_mut()
2577            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2578        let inner2 = heap
2579            .borrow_mut()
2580            .insert_list(vec![Value::Number(3.0), Value::Number(4.0)]);
2581        let list = heap.borrow_mut().insert_list(vec![inner1, inner2]);
2582
2583        let result = BuiltInFunction::Flatten
2584            .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2585            .unwrap();
2586
2587        let heap_borrow = heap.borrow();
2588        let result_list = result.as_list(&heap_borrow).unwrap();
2589        assert_eq!(result_list.len(), 4);
2590        assert_eq!(result_list[0], Value::Number(1.0));
2591        assert_eq!(result_list[1], Value::Number(2.0));
2592        assert_eq!(result_list[2], Value::Number(3.0));
2593        assert_eq!(result_list[3], Value::Number(4.0));
2594    }
2595
2596    #[test]
2597    fn test_flatten_with_non_list_elements() {
2598        let heap = Rc::new(RefCell::new(Heap::new()));
2599        let bindings = Rc::new(Environment::new());
2600
2601        // Create [[1, 2], 3, [4, 5]]
2602        let inner1 = heap
2603            .borrow_mut()
2604            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2605        let inner2 = heap
2606            .borrow_mut()
2607            .insert_list(vec![Value::Number(4.0), Value::Number(5.0)]);
2608        let list = heap
2609            .borrow_mut()
2610            .insert_list(vec![inner1, Value::Number(3.0), inner2]);
2611
2612        let result = BuiltInFunction::Flatten
2613            .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2614            .unwrap();
2615
2616        let heap_borrow = heap.borrow();
2617        let result_list = result.as_list(&heap_borrow).unwrap();
2618        assert_eq!(result_list.len(), 5);
2619        assert_eq!(result_list[0], Value::Number(1.0));
2620        assert_eq!(result_list[1], Value::Number(2.0));
2621        assert_eq!(result_list[2], Value::Number(3.0));
2622        assert_eq!(result_list[3], Value::Number(4.0));
2623        assert_eq!(result_list[4], Value::Number(5.0));
2624    }
2625
2626    #[test]
2627    fn test_flatten_one_level_only() {
2628        let heap = Rc::new(RefCell::new(Heap::new()));
2629        let bindings = Rc::new(Environment::new());
2630
2631        // Create [[1, [2, 3]], [4]]
2632        let deep_inner = heap
2633            .borrow_mut()
2634            .insert_list(vec![Value::Number(2.0), Value::Number(3.0)]);
2635        let inner1 = heap
2636            .borrow_mut()
2637            .insert_list(vec![Value::Number(1.0), deep_inner]);
2638        let inner2 = heap.borrow_mut().insert_list(vec![Value::Number(4.0)]);
2639        let list = heap.borrow_mut().insert_list(vec![inner1, inner2]);
2640
2641        let result = BuiltInFunction::Flatten
2642            .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2643            .unwrap();
2644
2645        let heap_borrow = heap.borrow();
2646        let result_list = result.as_list(&heap_borrow).unwrap();
2647        // Should be [1, [2, 3], 4] - the nested list stays nested
2648        assert_eq!(result_list.len(), 3);
2649        assert_eq!(result_list[0], Value::Number(1.0));
2650        // result_list[1] should still be a list
2651        assert!(matches!(result_list[1], Value::List(_)));
2652        assert_eq!(result_list[2], Value::Number(4.0));
2653    }
2654
2655    #[test]
2656    fn test_zip_equal_lengths() {
2657        let heap = Rc::new(RefCell::new(Heap::new()));
2658        let bindings = Rc::new(Environment::new());
2659
2660        let list1 = heap
2661            .borrow_mut()
2662            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2663        let str_a = heap.borrow_mut().insert_string("a".to_string());
2664        let str_b = heap.borrow_mut().insert_string("b".to_string());
2665        let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b]);
2666
2667        let result = BuiltInFunction::Zip
2668            .call(vec![list1, list2], heap.clone(), bindings.clone(), 0, "")
2669            .unwrap();
2670
2671        let heap_borrow = heap.borrow();
2672        let result_list = result.as_list(&heap_borrow).unwrap();
2673        assert_eq!(result_list.len(), 2);
2674
2675        let pair1 = result_list[0].as_list(&heap_borrow).unwrap();
2676        assert_eq!(pair1.len(), 2);
2677        assert_eq!(pair1[0], Value::Number(1.0));
2678
2679        let pair2 = result_list[1].as_list(&heap_borrow).unwrap();
2680        assert_eq!(pair2.len(), 2);
2681        assert_eq!(pair2[0], Value::Number(2.0));
2682    }
2683
2684    #[test]
2685    fn test_zip_unequal_lengths_pads_with_null() {
2686        let heap = Rc::new(RefCell::new(Heap::new()));
2687        let bindings = Rc::new(Environment::new());
2688
2689        let list1 = heap.borrow_mut().insert_list(vec![Value::Number(1.0)]);
2690        let str_a = heap.borrow_mut().insert_string("a".to_string());
2691        let str_b = heap.borrow_mut().insert_string("b".to_string());
2692        let str_c = heap.borrow_mut().insert_string("c".to_string());
2693        let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b, str_c]);
2694
2695        let result = BuiltInFunction::Zip
2696            .call(vec![list1, list2], heap.clone(), bindings.clone(), 0, "")
2697            .unwrap();
2698
2699        let heap_borrow = heap.borrow();
2700        let result_list = result.as_list(&heap_borrow).unwrap();
2701        assert_eq!(result_list.len(), 3); // Pads to longest
2702
2703        let pair1 = result_list[0].as_list(&heap_borrow).unwrap();
2704        assert_eq!(pair1[0], Value::Number(1.0));
2705
2706        let pair2 = result_list[1].as_list(&heap_borrow).unwrap();
2707        assert_eq!(pair2[0], Value::Null); // Padded with null
2708
2709        let pair3 = result_list[2].as_list(&heap_borrow).unwrap();
2710        assert_eq!(pair3[0], Value::Null); // Padded with null
2711    }
2712
2713    #[test]
2714    fn test_zip_three_lists() {
2715        let heap = Rc::new(RefCell::new(Heap::new()));
2716        let bindings = Rc::new(Environment::new());
2717
2718        let list1 = heap
2719            .borrow_mut()
2720            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2721        let str_a = heap.borrow_mut().insert_string("a".to_string());
2722        let str_b = heap.borrow_mut().insert_string("b".to_string());
2723        let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b]);
2724        let list3 = heap
2725            .borrow_mut()
2726            .insert_list(vec![Value::Bool(true), Value::Bool(false)]);
2727
2728        let result = BuiltInFunction::Zip
2729            .call(
2730                vec![list1, list2, list3],
2731                heap.clone(),
2732                bindings.clone(),
2733                0,
2734                "",
2735            )
2736            .unwrap();
2737
2738        let heap_borrow = heap.borrow();
2739        let result_list = result.as_list(&heap_borrow).unwrap();
2740        assert_eq!(result_list.len(), 2);
2741
2742        let tuple1 = result_list[0].as_list(&heap_borrow).unwrap();
2743        assert_eq!(tuple1.len(), 3);
2744        assert_eq!(tuple1[0], Value::Number(1.0));
2745        assert_eq!(tuple1[2], Value::Bool(true));
2746    }
2747
2748    #[test]
2749    fn test_chunk() {
2750        let heap = Rc::new(RefCell::new(Heap::new()));
2751        let bindings = Rc::new(Environment::new());
2752
2753        let list = heap.borrow_mut().insert_list(vec![
2754            Value::Number(1.0),
2755            Value::Number(2.0),
2756            Value::Number(3.0),
2757            Value::Number(4.0),
2758            Value::Number(5.0),
2759        ]);
2760
2761        let result = BuiltInFunction::Chunk
2762            .call(
2763                vec![list, Value::Number(2.0)],
2764                heap.clone(),
2765                bindings.clone(),
2766                0,
2767                "",
2768            )
2769            .unwrap();
2770
2771        let heap_borrow = heap.borrow();
2772        let result_list = result.as_list(&heap_borrow).unwrap();
2773        assert_eq!(result_list.len(), 3);
2774
2775        let chunk1 = result_list[0].as_list(&heap_borrow).unwrap();
2776        assert_eq!(chunk1.len(), 2);
2777        assert_eq!(chunk1[0], Value::Number(1.0));
2778        assert_eq!(chunk1[1], Value::Number(2.0));
2779
2780        let chunk2 = result_list[1].as_list(&heap_borrow).unwrap();
2781        assert_eq!(chunk2.len(), 2);
2782        assert_eq!(chunk2[0], Value::Number(3.0));
2783        assert_eq!(chunk2[1], Value::Number(4.0));
2784
2785        let chunk3 = result_list[2].as_list(&heap_borrow).unwrap();
2786        assert_eq!(chunk3.len(), 1); // Last chunk smaller
2787        assert_eq!(chunk3[0], Value::Number(5.0));
2788    }
2789
2790    #[test]
2791    fn test_chunk_zero_size_error() {
2792        let heap = Rc::new(RefCell::new(Heap::new()));
2793        let bindings = Rc::new(Environment::new());
2794
2795        let list = heap
2796            .borrow_mut()
2797            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2798
2799        let result = BuiltInFunction::Chunk.call(
2800            vec![list, Value::Number(0.0)],
2801            heap.clone(),
2802            bindings.clone(),
2803            0,
2804            "",
2805        );
2806
2807        assert!(result.is_err());
2808        assert!(
2809            result
2810                .unwrap_err()
2811                .to_string()
2812                .contains("chunk size must be greater than 0")
2813        );
2814    }
2815
2816    #[test]
2817    fn test_chunk_empty_list() {
2818        let heap = Rc::new(RefCell::new(Heap::new()));
2819        let bindings = Rc::new(Environment::new());
2820
2821        let list = heap.borrow_mut().insert_list(vec![]);
2822
2823        let result = BuiltInFunction::Chunk
2824            .call(
2825                vec![list, Value::Number(2.0)],
2826                heap.clone(),
2827                bindings.clone(),
2828                0,
2829                "",
2830            )
2831            .unwrap();
2832
2833        let heap_borrow = heap.borrow();
2834        let result_list = result.as_list(&heap_borrow).unwrap();
2835        assert_eq!(result_list.len(), 0);
2836    }
2837
2838    #[test]
2839    fn test_group_by() {
2840        use crate::values::{CapturedScope, LambdaDef};
2841
2842        let heap = Rc::new(RefCell::new(Heap::new()));
2843        let bindings = Rc::new(Environment::new());
2844
2845        // Use strings like "a", "b", "a" as input with identity lambda
2846        let str_a1 = heap.borrow_mut().insert_string("a".to_string());
2847        let str_b = heap.borrow_mut().insert_string("b".to_string());
2848        let str_a2 = heap.borrow_mut().insert_string("a".to_string());
2849        let simple_list = heap.borrow_mut().insert_list(vec![str_a1, str_b, str_a2]);
2850
2851        // Identity lambda: x => x
2852        let lambda = LambdaDef {
2853            name: None,
2854            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2855            body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2856            scope: CapturedScope::default(),
2857            source: Rc::from(""),
2858        };
2859        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2860
2861        let result = BuiltInFunction::GroupBy
2862            .call(
2863                vec![simple_list, lambda_value],
2864                heap.clone(),
2865                bindings.clone(),
2866                0,
2867                "",
2868            )
2869            .unwrap();
2870
2871        let heap_borrow = heap.borrow();
2872        let record = result.as_record(&heap_borrow).unwrap();
2873        assert_eq!(record.len(), 2); // "a" and "b"
2874        assert!(record.contains_key("a"));
2875        assert!(record.contains_key("b"));
2876
2877        let group_a = record.get("a").unwrap().as_list(&heap_borrow).unwrap();
2878        assert_eq!(group_a.len(), 2); // "a" appears twice
2879
2880        let group_b = record.get("b").unwrap().as_list(&heap_borrow).unwrap();
2881        assert_eq!(group_b.len(), 1); // "b" appears once
2882    }
2883
2884    #[test]
2885    fn test_group_by_non_string_key_error() {
2886        use crate::values::{CapturedScope, LambdaDef};
2887
2888        let heap = Rc::new(RefCell::new(Heap::new()));
2889        let bindings = Rc::new(Environment::new());
2890
2891        let list = heap
2892            .borrow_mut()
2893            .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2894
2895        // Lambda that returns a number: x => x
2896        let lambda = LambdaDef {
2897            name: None,
2898            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2899            body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2900            scope: CapturedScope::default(),
2901            source: Rc::from(""),
2902        };
2903        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2904
2905        let result = BuiltInFunction::GroupBy.call(
2906            vec![list, lambda_value],
2907            heap.clone(),
2908            bindings.clone(),
2909            0,
2910            "",
2911        );
2912
2913        assert!(result.is_err());
2914        assert!(
2915            result
2916                .unwrap_err()
2917                .to_string()
2918                .contains("must return a string")
2919        );
2920    }
2921
2922    #[test]
2923    fn test_count_by() {
2924        use crate::values::{CapturedScope, LambdaDef};
2925
2926        let heap = Rc::new(RefCell::new(Heap::new()));
2927        let bindings = Rc::new(Environment::new());
2928
2929        // Use strings as input with identity lambda
2930        let str_a1 = heap.borrow_mut().insert_string("a".to_string());
2931        let str_b = heap.borrow_mut().insert_string("b".to_string());
2932        let str_a2 = heap.borrow_mut().insert_string("a".to_string());
2933        let str_a3 = heap.borrow_mut().insert_string("a".to_string());
2934        let list = heap
2935            .borrow_mut()
2936            .insert_list(vec![str_a1, str_b, str_a2, str_a3]);
2937
2938        // Identity lambda: x => x
2939        let lambda = LambdaDef {
2940            name: None,
2941            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2942            body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2943            scope: CapturedScope::default(),
2944            source: Rc::from(""),
2945        };
2946        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2947
2948        let result = BuiltInFunction::CountBy
2949            .call(
2950                vec![list, lambda_value],
2951                heap.clone(),
2952                bindings.clone(),
2953                0,
2954                "",
2955            )
2956            .unwrap();
2957
2958        let heap_borrow = heap.borrow();
2959        let record = result.as_record(&heap_borrow).unwrap();
2960        assert_eq!(record.len(), 2); // "a" and "b"
2961
2962        assert_eq!(*record.get("a").unwrap(), Value::Number(3.0)); // "a" appears 3 times
2963        assert_eq!(*record.get("b").unwrap(), Value::Number(1.0)); // "b" appears 1 time
2964    }
2965
2966    #[test]
2967    fn test_count_by_empty_list() {
2968        use crate::values::{CapturedScope, LambdaDef};
2969
2970        let heap = Rc::new(RefCell::new(Heap::new()));
2971        let bindings = Rc::new(Environment::new());
2972
2973        let list = heap.borrow_mut().insert_list(vec![]);
2974
2975        // Identity lambda: x => x
2976        let lambda = LambdaDef {
2977            name: None,
2978            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2979            body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2980            scope: CapturedScope::default(),
2981            source: Rc::from(""),
2982        };
2983        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2984
2985        let result = BuiltInFunction::CountBy
2986            .call(
2987                vec![list, lambda_value],
2988                heap.clone(),
2989                bindings.clone(),
2990                0,
2991                "",
2992            )
2993            .unwrap();
2994
2995        let heap_borrow = heap.borrow();
2996        let record = result.as_record(&heap_borrow).unwrap();
2997        assert_eq!(record.len(), 0); // Empty record
2998    }
2999
3000    #[test]
3001    fn test_ugt_same_types() {
3002        let heap = Rc::new(RefCell::new(Heap::new()));
3003        let bindings = Rc::new(Environment::new());
3004
3005        // 5 > 3 = true
3006        let result = BuiltInFunction::Ugt
3007            .call(
3008                vec![Value::Number(5.0), Value::Number(3.0)],
3009                heap.clone(),
3010                bindings.clone(),
3011                0,
3012                "",
3013            )
3014            .unwrap();
3015        assert_eq!(result, Value::Bool(true));
3016
3017        // 3 > 5 = false
3018        let result = BuiltInFunction::Ugt
3019            .call(
3020                vec![Value::Number(3.0), Value::Number(5.0)],
3021                heap.clone(),
3022                bindings.clone(),
3023                0,
3024                "",
3025            )
3026            .unwrap();
3027        assert_eq!(result, Value::Bool(false));
3028
3029        // 5 > 5 = false
3030        let result = BuiltInFunction::Ugt
3031            .call(
3032                vec![Value::Number(5.0), Value::Number(5.0)],
3033                heap.clone(),
3034                bindings.clone(),
3035                0,
3036                "",
3037            )
3038            .unwrap();
3039        assert_eq!(result, Value::Bool(false));
3040    }
3041
3042    #[test]
3043    fn test_ult_same_types() {
3044        let heap = Rc::new(RefCell::new(Heap::new()));
3045        let bindings = Rc::new(Environment::new());
3046
3047        // 3 < 5 = true
3048        let result = BuiltInFunction::Ult
3049            .call(
3050                vec![Value::Number(3.0), Value::Number(5.0)],
3051                heap.clone(),
3052                bindings.clone(),
3053                0,
3054                "",
3055            )
3056            .unwrap();
3057        assert_eq!(result, Value::Bool(true));
3058
3059        // 5 < 3 = false
3060        let result = BuiltInFunction::Ult
3061            .call(
3062                vec![Value::Number(5.0), Value::Number(3.0)],
3063                heap.clone(),
3064                bindings.clone(),
3065                0,
3066                "",
3067            )
3068            .unwrap();
3069        assert_eq!(result, Value::Bool(false));
3070    }
3071
3072    #[test]
3073    fn test_ugte_same_types() {
3074        let heap = Rc::new(RefCell::new(Heap::new()));
3075        let bindings = Rc::new(Environment::new());
3076
3077        // 5 >= 3 = true
3078        let result = BuiltInFunction::Ugte
3079            .call(
3080                vec![Value::Number(5.0), Value::Number(3.0)],
3081                heap.clone(),
3082                bindings.clone(),
3083                0,
3084                "",
3085            )
3086            .unwrap();
3087        assert_eq!(result, Value::Bool(true));
3088
3089        // 5 >= 5 = true
3090        let result = BuiltInFunction::Ugte
3091            .call(
3092                vec![Value::Number(5.0), Value::Number(5.0)],
3093                heap.clone(),
3094                bindings.clone(),
3095                0,
3096                "",
3097            )
3098            .unwrap();
3099        assert_eq!(result, Value::Bool(true));
3100
3101        // 3 >= 5 = false
3102        let result = BuiltInFunction::Ugte
3103            .call(
3104                vec![Value::Number(3.0), Value::Number(5.0)],
3105                heap.clone(),
3106                bindings.clone(),
3107                0,
3108                "",
3109            )
3110            .unwrap();
3111        assert_eq!(result, Value::Bool(false));
3112    }
3113
3114    #[test]
3115    fn test_ulte_same_types() {
3116        let heap = Rc::new(RefCell::new(Heap::new()));
3117        let bindings = Rc::new(Environment::new());
3118
3119        // 3 <= 5 = true
3120        let result = BuiltInFunction::Ulte
3121            .call(
3122                vec![Value::Number(3.0), Value::Number(5.0)],
3123                heap.clone(),
3124                bindings.clone(),
3125                0,
3126                "",
3127            )
3128            .unwrap();
3129        assert_eq!(result, Value::Bool(true));
3130
3131        // 5 <= 5 = true
3132        let result = BuiltInFunction::Ulte
3133            .call(
3134                vec![Value::Number(5.0), Value::Number(5.0)],
3135                heap.clone(),
3136                bindings.clone(),
3137                0,
3138                "",
3139            )
3140            .unwrap();
3141        assert_eq!(result, Value::Bool(true));
3142
3143        // 5 <= 3 = false
3144        let result = BuiltInFunction::Ulte
3145            .call(
3146                vec![Value::Number(5.0), Value::Number(3.0)],
3147                heap.clone(),
3148                bindings.clone(),
3149                0,
3150                "",
3151            )
3152            .unwrap();
3153        assert_eq!(result, Value::Bool(false));
3154    }
3155
3156    #[test]
3157    fn test_unchecked_comparisons_mixed_types_return_false() {
3158        let heap = Rc::new(RefCell::new(Heap::new()));
3159        let bindings = Rc::new(Environment::new());
3160
3161        // null > 0 = false (not an error)
3162        let result = BuiltInFunction::Ugt
3163            .call(
3164                vec![Value::Null, Value::Number(0.0)],
3165                heap.clone(),
3166                bindings.clone(),
3167                0,
3168                "",
3169            )
3170            .unwrap();
3171        assert_eq!(result, Value::Bool(false));
3172
3173        // null < 5 = false (not an error)
3174        let result = BuiltInFunction::Ult
3175            .call(
3176                vec![Value::Null, Value::Number(5.0)],
3177                heap.clone(),
3178                bindings.clone(),
3179                0,
3180                "",
3181            )
3182            .unwrap();
3183        assert_eq!(result, Value::Bool(false));
3184
3185        // null >= 0 = false (not an error)
3186        let result = BuiltInFunction::Ugte
3187            .call(
3188                vec![Value::Null, Value::Number(0.0)],
3189                heap.clone(),
3190                bindings.clone(),
3191                0,
3192                "",
3193            )
3194            .unwrap();
3195        assert_eq!(result, Value::Bool(false));
3196
3197        // null <= 0 = false (not an error)
3198        let result = BuiltInFunction::Ulte
3199            .call(
3200                vec![Value::Null, Value::Number(0.0)],
3201                heap.clone(),
3202                bindings.clone(),
3203                0,
3204                "",
3205            )
3206            .unwrap();
3207        assert_eq!(result, Value::Bool(false));
3208
3209        // string vs number: "hello" > 5 = false (not an error)
3210        let str_val = heap.borrow_mut().insert_string("hello".to_string());
3211        let result = BuiltInFunction::Ugt
3212            .call(
3213                vec![str_val, Value::Number(5.0)],
3214                heap.clone(),
3215                bindings.clone(),
3216                0,
3217                "",
3218            )
3219            .unwrap();
3220        assert_eq!(result, Value::Bool(false));
3221
3222        // bool vs number: true > 5 = false (not an error)
3223        let result = BuiltInFunction::Ugt
3224            .call(
3225                vec![Value::Bool(true), Value::Number(5.0)],
3226                heap.clone(),
3227                bindings.clone(),
3228                0,
3229                "",
3230            )
3231            .unwrap();
3232        assert_eq!(result, Value::Bool(false));
3233
3234        // list vs number: [1,2,3] > 5 = false (not an error)
3235        let list = heap.borrow_mut().insert_list(vec![
3236            Value::Number(1.0),
3237            Value::Number(2.0),
3238            Value::Number(3.0),
3239        ]);
3240        let result = BuiltInFunction::Ugt
3241            .call(
3242                vec![list, Value::Number(5.0)],
3243                heap.clone(),
3244                bindings.clone(),
3245                0,
3246                "",
3247            )
3248            .unwrap();
3249        assert_eq!(result, Value::Bool(false));
3250    }
3251}