Skip to main content

jpx_core/extensions/
array.rs

1//! Array manipulation functions.
2
3use std::collections::HashSet;
4
5use serde_json::{Number, Value};
6
7use crate::functions::{Function, custom_error};
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12/// Register only the array functions that are in the enabled set.
13pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
14    register_if_enabled(runtime, "unique", enabled, Box::new(UniqueFn::new()));
15    register_if_enabled(runtime, "zip", enabled, Box::new(ZipFn::new()));
16    register_if_enabled(runtime, "chunk", enabled, Box::new(ChunkFn::new()));
17    register_if_enabled(runtime, "take", enabled, Box::new(TakeFn::new()));
18    register_if_enabled(runtime, "drop", enabled, Box::new(DropFn::new()));
19    register_if_enabled(
20        runtime,
21        "flatten_deep",
22        enabled,
23        Box::new(FlattenDeepFn::new()),
24    );
25    register_if_enabled(runtime, "flatten", enabled, Box::new(FlattenFn::new()));
26    register_if_enabled(runtime, "compact", enabled, Box::new(CompactFn::new()));
27    register_if_enabled(runtime, "range", enabled, Box::new(RangeFn::new()));
28    register_if_enabled(runtime, "index_at", enabled, Box::new(IndexAtFn::new()));
29    register_if_enabled(runtime, "includes", enabled, Box::new(IncludesFn::new()));
30    register_if_enabled(runtime, "find_index", enabled, Box::new(FindIndexFn::new()));
31    register_if_enabled(runtime, "first", enabled, Box::new(FirstFn::new()));
32    register_if_enabled(runtime, "last", enabled, Box::new(LastFn::new()));
33    register_if_enabled(runtime, "group_by", enabled, Box::new(GroupByFn::new()));
34    register_if_enabled(runtime, "index_by", enabled, Box::new(IndexByFn::new()));
35    register_if_enabled(runtime, "nth", enabled, Box::new(NthFn::new()));
36    register_if_enabled(
37        runtime,
38        "interleave",
39        enabled,
40        Box::new(InterleaveFn::new()),
41    );
42    register_if_enabled(runtime, "rotate", enabled, Box::new(RotateFn::new()));
43    register_if_enabled(runtime, "partition", enabled, Box::new(PartitionFn::new()));
44    register_if_enabled(
45        runtime,
46        "difference",
47        enabled,
48        Box::new(DifferenceFn::new()),
49    );
50    register_if_enabled(
51        runtime,
52        "intersection",
53        enabled,
54        Box::new(IntersectionFn::new()),
55    );
56    register_if_enabled(runtime, "union", enabled, Box::new(UnionFn::new()));
57    register_if_enabled(
58        runtime,
59        "frequencies",
60        enabled,
61        Box::new(FrequenciesFn::new()),
62    );
63    register_if_enabled(runtime, "mode", enabled, Box::new(ModeFn::new()));
64    register_if_enabled(runtime, "cartesian", enabled, Box::new(CartesianFn::new()));
65    register_if_enabled(runtime, "initial", enabled, Box::new(InitialFn::new()));
66    // Alias for initial (Clojure-style name)
67    register_if_enabled(runtime, "butlast", enabled, Box::new(InitialFn::new()));
68    // Clojure-inspired functions
69    register_if_enabled(runtime, "interpose", enabled, Box::new(InterposeFn::new()));
70    register_if_enabled(runtime, "zipmap", enabled, Box::new(ZipmapFn::new()));
71    register_if_enabled(
72        runtime,
73        "partition_by",
74        enabled,
75        Box::new(PartitionByFn::new()),
76    );
77    register_if_enabled(runtime, "dedupe", enabled, Box::new(DedupeFn::new()));
78    register_if_enabled(runtime, "tail", enabled, Box::new(TailFn::new()));
79    register_if_enabled(runtime, "without", enabled, Box::new(WithoutFn::new()));
80    register_if_enabled(runtime, "xor", enabled, Box::new(XorFn::new()));
81    register_if_enabled(runtime, "fill", enabled, Box::new(FillFn::new()));
82    register_if_enabled(runtime, "pull_at", enabled, Box::new(PullAtFn::new()));
83    register_if_enabled(runtime, "window", enabled, Box::new(WindowFn::new()));
84    register_if_enabled(
85        runtime,
86        "combinations",
87        enabled,
88        Box::new(CombinationsFn::new()),
89    );
90    register_if_enabled(runtime, "transpose", enabled, Box::new(TransposeFn::new()));
91    register_if_enabled(runtime, "pairwise", enabled, Box::new(PairwiseFn::new()));
92    // Alias for window (sliding_window is a common name)
93    register_if_enabled(
94        runtime,
95        "sliding_window",
96        enabled,
97        Box::new(WindowFn::new()),
98    );
99    // jq-parity functions
100    register_if_enabled(
101        runtime,
102        "indices_array",
103        enabled,
104        Box::new(IndicesArrayFn::new()),
105    );
106    register_if_enabled(
107        runtime,
108        "inside_array",
109        enabled,
110        Box::new(InsideArrayFn::new()),
111    );
112    register_if_enabled(runtime, "bsearch", enabled, Box::new(BsearchFn::new()));
113    // Clojure-inspired functions (Phase 3)
114    register_if_enabled(
115        runtime,
116        "repeat_array",
117        enabled,
118        Box::new(RepeatArrayFn::new()),
119    );
120    register_if_enabled(runtime, "cycle", enabled, Box::new(CycleFn::new()));
121}
122
123// =============================================================================
124// unique(array) -> array
125// =============================================================================
126
127defn!(UniqueFn, vec![arg!(array)], None);
128
129impl Function for UniqueFn {
130    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
131        self.signature.validate(args, ctx)?;
132
133        let arr = args[0]
134            .as_array()
135            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
136
137        let mut seen = HashSet::new();
138        let mut result = Vec::new();
139
140        for item in arr {
141            let key = serde_json::to_string(item).unwrap_or_default();
142            if seen.insert(key) {
143                result.push(item.clone());
144            }
145        }
146
147        Ok(Value::Array(result))
148    }
149}
150
151// =============================================================================
152// zip(array1, array2) -> array of pairs
153// =============================================================================
154
155defn!(ZipFn, vec![arg!(array), arg!(array)], None);
156
157impl Function for ZipFn {
158    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
159        self.signature.validate(args, ctx)?;
160
161        let arr1 = args[0]
162            .as_array()
163            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
164
165        let arr2 = args[1]
166            .as_array()
167            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
168
169        let result: Vec<Value> = arr1
170            .iter()
171            .zip(arr2.iter())
172            .map(|(a, b)| Value::Array(vec![a.clone(), b.clone()]))
173            .collect();
174
175        Ok(Value::Array(result))
176    }
177}
178
179// =============================================================================
180// chunk(array, size) -> array of arrays
181// =============================================================================
182
183defn!(ChunkFn, vec![arg!(array), arg!(number)], None);
184
185impl Function for ChunkFn {
186    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
187        self.signature.validate(args, ctx)?;
188
189        let arr = args[0]
190            .as_array()
191            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
192
193        let size = args[1]
194            .as_f64()
195            .map(|n| n as usize)
196            .ok_or_else(|| custom_error(ctx, "Expected positive number for size"))?;
197
198        if size == 0 {
199            return Ok(Value::Array(vec![]));
200        }
201
202        let chunks: Vec<Value> = arr
203            .chunks(size)
204            .map(|chunk| Value::Array(chunk.to_vec()))
205            .collect();
206
207        Ok(Value::Array(chunks))
208    }
209}
210
211// =============================================================================
212// take(array, n) -> array (first n elements)
213// =============================================================================
214
215defn!(TakeFn, vec![arg!(array), arg!(number)], None);
216
217impl Function for TakeFn {
218    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
219        self.signature.validate(args, ctx)?;
220
221        let arr = args[0]
222            .as_array()
223            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
224
225        let n = args[1]
226            .as_f64()
227            .map(|n| n as usize)
228            .ok_or_else(|| custom_error(ctx, "Expected positive number"))?;
229
230        let result: Vec<Value> = arr.iter().take(n).cloned().collect();
231
232        Ok(Value::Array(result))
233    }
234}
235
236// =============================================================================
237// drop(array, n) -> array (skip first n elements)
238// =============================================================================
239
240defn!(DropFn, vec![arg!(array), arg!(number)], None);
241
242impl Function for DropFn {
243    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
244        self.signature.validate(args, ctx)?;
245
246        let arr = args[0]
247            .as_array()
248            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
249
250        let n = args[1]
251            .as_f64()
252            .map(|n| n as usize)
253            .ok_or_else(|| custom_error(ctx, "Expected positive number"))?;
254
255        let result: Vec<Value> = arr.iter().skip(n).cloned().collect();
256
257        Ok(Value::Array(result))
258    }
259}
260
261// =============================================================================
262// flatten_deep(array) -> array (recursively flatten)
263// =============================================================================
264
265defn!(FlattenDeepFn, vec![arg!(array)], None);
266
267fn flatten_recursive(arr: &[Value]) -> Vec<Value> {
268    let mut result = Vec::new();
269    for item in arr {
270        if let Some(inner) = item.as_array() {
271            result.extend(flatten_recursive(inner));
272        } else {
273            result.push(item.clone());
274        }
275    }
276    result
277}
278
279impl Function for FlattenDeepFn {
280    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
281        self.signature.validate(args, ctx)?;
282
283        let arr = args[0]
284            .as_array()
285            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
286
287        Ok(Value::Array(flatten_recursive(arr)))
288    }
289}
290
291// =============================================================================
292// flatten(array) -> array (single-level flatten)
293// =============================================================================
294
295defn!(FlattenFn, vec![arg!(array)], None);
296
297impl Function for FlattenFn {
298    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
299        self.signature.validate(args, ctx)?;
300
301        let arr = args[0]
302            .as_array()
303            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
304
305        let mut result = Vec::new();
306        for item in arr {
307            if let Some(inner) = item.as_array() {
308                result.extend(inner.iter().cloned());
309            } else {
310                result.push(item.clone());
311            }
312        }
313
314        Ok(Value::Array(result))
315    }
316}
317
318// =============================================================================
319// compact(array) -> array (remove null/false values)
320// =============================================================================
321
322defn!(CompactFn, vec![arg!(array)], None);
323
324impl Function for CompactFn {
325    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
326        self.signature.validate(args, ctx)?;
327
328        let arr = args[0]
329            .as_array()
330            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
331
332        let result: Vec<Value> = arr
333            .iter()
334            .filter(|v| !v.is_null() && !matches!(v, Value::Bool(false)))
335            .cloned()
336            .collect();
337
338        Ok(Value::Array(result))
339    }
340}
341
342// =============================================================================
343// range(start, end, step?) -> array
344// =============================================================================
345
346defn!(
347    RangeFn,
348    vec![arg!(number), arg!(number)],
349    Some(arg!(number))
350);
351
352impl Function for RangeFn {
353    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
354        self.signature.validate(args, ctx)?;
355
356        let start = args[0]
357            .as_f64()
358            .map(|n| n as i64)
359            .ok_or_else(|| custom_error(ctx, "Expected start number"))?;
360
361        let end = args[1]
362            .as_f64()
363            .map(|n| n as i64)
364            .ok_or_else(|| custom_error(ctx, "Expected end number"))?;
365
366        let step = if args.len() > 2 {
367            args[2]
368                .as_f64()
369                .map(|n| n as i64)
370                .ok_or_else(|| custom_error(ctx, "Expected step number"))?
371        } else {
372            1
373        };
374
375        if step == 0 {
376            return Err(custom_error(ctx, "Step cannot be zero"));
377        }
378
379        let mut result = Vec::new();
380        let mut current = start;
381
382        const MAX_RANGE: usize = 10000;
383
384        if step > 0 {
385            while current < end && result.len() < MAX_RANGE {
386                result.push(Value::Number(Number::from(current)));
387                current += step;
388            }
389        } else {
390            while current > end && result.len() < MAX_RANGE {
391                result.push(Value::Number(Number::from(current)));
392                current += step;
393            }
394        }
395
396        Ok(Value::Array(result))
397    }
398}
399
400// =============================================================================
401// index_at(array, index) -> element (supports negative index)
402// =============================================================================
403
404defn!(IndexAtFn, vec![arg!(array), arg!(number)], None);
405
406impl Function for IndexAtFn {
407    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
408        self.signature.validate(args, ctx)?;
409
410        let arr = args[0]
411            .as_array()
412            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
413
414        let index = args[1]
415            .as_f64()
416            .map(|n| n as i64)
417            .ok_or_else(|| custom_error(ctx, "Expected number for index"))?;
418
419        let len = arr.len() as i64;
420        let actual_index = if index < 0 {
421            (len + index) as usize
422        } else {
423            index as usize
424        };
425
426        if actual_index < arr.len() {
427            Ok(arr[actual_index].clone())
428        } else {
429            Ok(Value::Null)
430        }
431    }
432}
433
434// =============================================================================
435// includes(array, value) -> boolean
436// =============================================================================
437
438defn!(IncludesFn, vec![arg!(array), arg!(any)], None);
439
440impl Function for IncludesFn {
441    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
442        self.signature.validate(args, ctx)?;
443
444        let arr = args[0]
445            .as_array()
446            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
447
448        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
449
450        let found = arr.iter().any(|item| {
451            let item_key = serde_json::to_string(item).unwrap_or_default();
452            item_key == search_key
453        });
454
455        Ok(Value::Bool(found))
456    }
457}
458
459// =============================================================================
460// find_index(array, value) -> number (-1 if not found)
461// =============================================================================
462
463defn!(FindIndexFn, vec![arg!(array), arg!(any)], None);
464
465impl Function for FindIndexFn {
466    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
467        self.signature.validate(args, ctx)?;
468
469        let arr = args[0]
470            .as_array()
471            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
472
473        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
474
475        let index = arr
476            .iter()
477            .position(|item| {
478                let item_key = serde_json::to_string(item).unwrap_or_default();
479                item_key == search_key
480            })
481            .map(|i| i as i64)
482            .unwrap_or(-1);
483
484        Ok(Value::Number(Number::from(index)))
485    }
486}
487
488// =============================================================================
489// first(array) -> any (first element or null)
490// =============================================================================
491
492defn!(FirstFn, vec![arg!(array)], None);
493
494impl Function for FirstFn {
495    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
496        self.signature.validate(args, ctx)?;
497
498        let arr = args[0]
499            .as_array()
500            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
501
502        Ok(arr.first().cloned().unwrap_or(Value::Null))
503    }
504}
505
506// =============================================================================
507// last(array) -> any (last element or null)
508// =============================================================================
509
510defn!(LastFn, vec![arg!(array)], None);
511
512impl Function for LastFn {
513    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
514        self.signature.validate(args, ctx)?;
515
516        let arr = args[0]
517            .as_array()
518            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
519
520        Ok(arr.last().cloned().unwrap_or(Value::Null))
521    }
522}
523
524// =============================================================================
525// group_by(array, field_name) -> object
526// =============================================================================
527
528defn!(GroupByFn, vec![arg!(array), arg!(string)], None);
529
530impl Function for GroupByFn {
531    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
532        self.signature.validate(args, ctx)?;
533
534        let arr = args[0]
535            .as_array()
536            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
537
538        let field_name = args[1]
539            .as_str()
540            .ok_or_else(|| custom_error(ctx, "Expected field name string"))?;
541
542        let mut groups: std::collections::BTreeMap<String, Vec<Value>> =
543            std::collections::BTreeMap::new();
544
545        for item in arr {
546            let key = if let Some(obj) = item.as_object() {
547                if let Some(field_value) = obj.get(field_name) {
548                    match field_value {
549                        Value::String(s) => s.clone(),
550                        Value::Number(n) => n.to_string(),
551                        Value::Bool(b) => b.to_string(),
552                        Value::Null => "null".to_string(),
553                        _ => continue,
554                    }
555                } else {
556                    "null".to_string()
557                }
558            } else {
559                continue;
560            };
561            groups.entry(key).or_default().push(item.clone());
562        }
563
564        let mut result = serde_json::Map::new();
565        for (k, v) in groups {
566            result.insert(k, Value::Array(v));
567        }
568
569        Ok(Value::Object(result))
570    }
571}
572
573// =============================================================================
574// index_by(array, field_name) -> object (last value wins for duplicates)
575// =============================================================================
576
577defn!(IndexByFn, vec![arg!(array), arg!(string)], None);
578
579impl Function for IndexByFn {
580    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
581        self.signature.validate(args, ctx)?;
582
583        let arr = args[0]
584            .as_array()
585            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
586
587        let field_name = args[1]
588            .as_str()
589            .ok_or_else(|| custom_error(ctx, "Expected field name string"))?;
590
591        let mut result = serde_json::Map::new();
592
593        for item in arr {
594            let key = if let Some(obj) = item.as_object() {
595                if let Some(field_value) = obj.get(field_name) {
596                    match field_value {
597                        Value::String(s) => s.clone(),
598                        Value::Number(n) => n.to_string(),
599                        Value::Bool(b) => b.to_string(),
600                        Value::Null => "null".to_string(),
601                        _ => continue,
602                    }
603                } else {
604                    // Skip items without the key field
605                    continue;
606                }
607            } else {
608                continue;
609            };
610            // Last value wins for duplicate keys
611            result.insert(key, item.clone());
612        }
613
614        Ok(Value::Object(result))
615    }
616}
617
618// =============================================================================
619// nth(array, n) -> array (every nth element)
620// =============================================================================
621
622defn!(NthFn, vec![arg!(array), arg!(number)], None);
623
624impl Function for NthFn {
625    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
626        self.signature.validate(args, ctx)?;
627
628        let arr = args[0]
629            .as_array()
630            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
631
632        let n = args[1]
633            .as_f64()
634            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as usize;
635
636        if n == 0 {
637            return Ok(Value::Null);
638        }
639
640        let result: Vec<Value> = arr.iter().step_by(n).cloned().collect();
641        Ok(Value::Array(result))
642    }
643}
644
645// =============================================================================
646// interleave(array1, array2) -> array (alternate elements)
647// =============================================================================
648
649defn!(InterleaveFn, vec![arg!(array), arg!(array)], None);
650
651impl Function for InterleaveFn {
652    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
653        self.signature.validate(args, ctx)?;
654
655        let arr1 = args[0]
656            .as_array()
657            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
658
659        let arr2 = args[1]
660            .as_array()
661            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
662
663        let mut result = Vec::with_capacity(arr1.len() + arr2.len());
664        let mut iter1 = arr1.iter();
665        let mut iter2 = arr2.iter();
666
667        loop {
668            match (iter1.next(), iter2.next()) {
669                (Some(a), Some(b)) => {
670                    result.push(a.clone());
671                    result.push(b.clone());
672                }
673                (Some(a), None) => {
674                    result.push(a.clone());
675                    result.extend(iter1.cloned());
676                    break;
677                }
678                (None, Some(b)) => {
679                    result.push(b.clone());
680                    result.extend(iter2.cloned());
681                    break;
682                }
683                (None, None) => break,
684            }
685        }
686
687        Ok(Value::Array(result))
688    }
689}
690
691// =============================================================================
692// rotate(array, n) -> array (rotate elements by n positions)
693// =============================================================================
694
695defn!(RotateFn, vec![arg!(array), arg!(number)], None);
696
697impl Function for RotateFn {
698    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
699        self.signature.validate(args, ctx)?;
700
701        let arr = args[0]
702            .as_array()
703            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
704
705        if arr.is_empty() {
706            return Ok(Value::Array(vec![]));
707        }
708
709        let n = args[1]
710            .as_f64()
711            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as i64;
712
713        let len = arr.len() as i64;
714        let rotation = ((n % len) + len) % len;
715        let rotation = rotation as usize;
716
717        let mut result = Vec::with_capacity(arr.len());
718        result.extend(arr[rotation..].iter().cloned());
719        result.extend(arr[..rotation].iter().cloned());
720
721        Ok(Value::Array(result))
722    }
723}
724
725// =============================================================================
726// partition(array, n) -> array (split into n equal parts)
727// =============================================================================
728
729defn!(PartitionFn, vec![arg!(array), arg!(number)], None);
730
731impl Function for PartitionFn {
732    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
733        self.signature.validate(args, ctx)?;
734
735        let arr = args[0]
736            .as_array()
737            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
738
739        let n = args[1]
740            .as_f64()
741            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as usize;
742
743        if n == 0 {
744            return Ok(Value::Null);
745        }
746
747        let len = arr.len();
748        let base_size = len / n;
749        let remainder = len % n;
750
751        let mut result = Vec::with_capacity(n);
752        let mut start = 0;
753
754        for i in 0..n {
755            let size = base_size + if i < remainder { 1 } else { 0 };
756            if size > 0 {
757                result.push(Value::Array(arr[start..start + size].to_vec()));
758            } else {
759                result.push(Value::Array(vec![]));
760            }
761            start += size;
762        }
763
764        Ok(Value::Array(result))
765    }
766}
767
768// =============================================================================
769// difference(arr1, arr2) -> array (set difference)
770// =============================================================================
771
772defn!(DifferenceFn, vec![arg!(array), arg!(array)], None);
773
774impl Function for DifferenceFn {
775    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
776        self.signature.validate(args, ctx)?;
777
778        let arr1 = args[0]
779            .as_array()
780            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
781
782        let arr2 = args[1]
783            .as_array()
784            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
785
786        let set2: HashSet<String> = arr2
787            .iter()
788            .map(|v| serde_json::to_string(v).unwrap_or_default())
789            .collect();
790
791        let result: Vec<Value> = arr1
792            .iter()
793            .filter(|v| {
794                let key = serde_json::to_string(*v).unwrap_or_default();
795                !set2.contains(&key)
796            })
797            .cloned()
798            .collect();
799
800        Ok(Value::Array(result))
801    }
802}
803
804// =============================================================================
805// intersection(arr1, arr2) -> array (set intersection)
806// =============================================================================
807
808defn!(IntersectionFn, vec![arg!(array), arg!(array)], None);
809
810impl Function for IntersectionFn {
811    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
812        self.signature.validate(args, ctx)?;
813
814        let arr1 = args[0]
815            .as_array()
816            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
817
818        let arr2 = args[1]
819            .as_array()
820            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
821
822        let set2: HashSet<String> = arr2
823            .iter()
824            .map(|v| serde_json::to_string(v).unwrap_or_default())
825            .collect();
826
827        let mut seen: HashSet<String> = HashSet::new();
828        let result: Vec<Value> = arr1
829            .iter()
830            .filter(|v| {
831                let key = serde_json::to_string(*v).unwrap_or_default();
832                set2.contains(&key) && seen.insert(key)
833            })
834            .cloned()
835            .collect();
836
837        Ok(Value::Array(result))
838    }
839}
840
841// =============================================================================
842// union(arr1, arr2) -> array (set union)
843// =============================================================================
844
845defn!(UnionFn, vec![arg!(array), arg!(array)], None);
846
847impl Function for UnionFn {
848    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
849        self.signature.validate(args, ctx)?;
850
851        let arr1 = args[0]
852            .as_array()
853            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
854
855        let arr2 = args[1]
856            .as_array()
857            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
858
859        let mut seen: HashSet<String> = HashSet::new();
860        let mut result: Vec<Value> = Vec::new();
861
862        for item in arr1.iter().chain(arr2.iter()) {
863            let key = serde_json::to_string(item).unwrap_or_default();
864            if seen.insert(key) {
865                result.push(item.clone());
866            }
867        }
868
869        Ok(Value::Array(result))
870    }
871}
872
873// =============================================================================
874// frequencies(array) -> object (count occurrences)
875// =============================================================================
876
877defn!(FrequenciesFn, vec![arg!(array)], None);
878
879impl Function for FrequenciesFn {
880    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
881        self.signature.validate(args, ctx)?;
882
883        let arr = args[0]
884            .as_array()
885            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
886
887        let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
888
889        for item in arr {
890            let key = match item {
891                Value::String(s) => s.clone(),
892                Value::Number(n) => n.to_string(),
893                Value::Bool(b) => b.to_string(),
894                Value::Null => "null".to_string(),
895                _ => serde_json::to_string(item).unwrap_or_else(|_| "null".to_string()),
896            };
897            *counts.entry(key).or_insert(0) += 1;
898        }
899
900        let mut result = serde_json::Map::new();
901        // Use BTreeMap for sorted output
902        let sorted: std::collections::BTreeMap<String, i64> = counts.into_iter().collect();
903        for (k, v) in sorted {
904            result.insert(k, Value::Number(Number::from(v)));
905        }
906
907        Ok(Value::Object(result))
908    }
909}
910
911// =============================================================================
912// mode(array) -> any (most frequent value)
913// =============================================================================
914
915defn!(ModeFn, vec![arg!(array)], None);
916
917impl Function for ModeFn {
918    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
919        self.signature.validate(args, ctx)?;
920
921        let arr = args[0]
922            .as_array()
923            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
924
925        if arr.is_empty() {
926            return Ok(Value::Null);
927        }
928
929        let mut counts: std::collections::HashMap<String, (i64, Value)> =
930            std::collections::HashMap::new();
931
932        for item in arr {
933            let key = serde_json::to_string(item).unwrap_or_default();
934            counts
935                .entry(key)
936                .and_modify(|(count, _)| *count += 1)
937                .or_insert((1, item.clone()));
938        }
939
940        let (_, (_, mode_value)) = counts
941            .into_iter()
942            .max_by_key(|(_, (count, _))| *count)
943            .unwrap();
944
945        Ok(mode_value)
946    }
947}
948
949// =============================================================================
950// cartesian(arr1, arr2) -> array (cartesian product of 2 arrays)
951// cartesian(array_of_arrays) -> array (cartesian product of N arrays, jq parity)
952// =============================================================================
953
954defn!(CartesianFn, vec![arg!(array)], Some(arg!(array)));
955
956impl Function for CartesianFn {
957    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
958        self.signature.validate(args, ctx)?;
959
960        let first = args[0]
961            .as_array()
962            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
963
964        // Two modes:
965        // 1. cartesian(arr1, arr2) - two separate arrays (original behavior)
966        // 2. cartesian(array_of_arrays) - single array containing arrays (jq-style)
967
968        if args.len() == 2 {
969            // Original two-argument mode
970            let arr2 = args[1]
971                .as_array()
972                .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
973
974            let mut result = Vec::with_capacity(first.len() * arr2.len());
975            for a in first {
976                for b in arr2 {
977                    result.push(Value::Array(vec![a.clone(), b.clone()]));
978                }
979            }
980            Ok(Value::Array(result))
981        } else {
982            // Single argument mode - check if it's an array of arrays
983            // If all elements are arrays, do N-way cartesian product
984            let arrays: Vec<&Vec<Value>> =
985                first.iter().filter_map(|item| item.as_array()).collect();
986
987            if arrays.len() != first.len() || arrays.is_empty() {
988                // Not all elements are arrays, or empty - return empty
989                return Ok(Value::Array(vec![]));
990            }
991
992            // N-way cartesian product
993            let result = cartesian_product_n(&arrays);
994            Ok(Value::Array(result))
995        }
996    }
997}
998
999/// Compute N-way cartesian product of arrays.
1000fn cartesian_product_n(arrays: &[&Vec<Value>]) -> Vec<Value> {
1001    if arrays.is_empty() {
1002        return vec![];
1003    }
1004
1005    if arrays.len() == 1 {
1006        // Single array - each element becomes a 1-element tuple
1007        return arrays[0]
1008            .iter()
1009            .map(|item| Value::Array(vec![item.clone()]))
1010            .collect();
1011    }
1012
1013    // Calculate total size for pre-allocation
1014    let total_size: usize = arrays.iter().map(|a| a.len()).product();
1015    if total_size == 0 {
1016        return vec![];
1017    }
1018
1019    let mut result = Vec::with_capacity(total_size);
1020
1021    // Iterative cartesian product using indices
1022    let mut indices = vec![0usize; arrays.len()];
1023
1024    loop {
1025        // Build current combination
1026        let combo: Vec<Value> = indices
1027            .iter()
1028            .enumerate()
1029            .map(|(arr_idx, &elem_idx)| arrays[arr_idx][elem_idx].clone())
1030            .collect();
1031        result.push(Value::Array(combo));
1032
1033        // Increment indices (like counting in mixed radix)
1034        let mut carry = true;
1035        for i in (0..arrays.len()).rev() {
1036            if carry {
1037                indices[i] += 1;
1038                if indices[i] >= arrays[i].len() {
1039                    indices[i] = 0;
1040                } else {
1041                    carry = false;
1042                }
1043            }
1044        }
1045
1046        // If we carried all the way through, we're done
1047        if carry {
1048            break;
1049        }
1050    }
1051
1052    result
1053}
1054
1055// =============================================================================
1056// initial(array) -> array (all elements except the last)
1057// =============================================================================
1058
1059defn!(InitialFn, vec![arg!(array)], None);
1060
1061impl Function for InitialFn {
1062    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1063        self.signature.validate(args, ctx)?;
1064
1065        let arr = args[0]
1066            .as_array()
1067            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1068
1069        if arr.is_empty() {
1070            return Ok(Value::Array(vec![]));
1071        }
1072
1073        let result: Vec<Value> = arr[..arr.len() - 1].to_vec();
1074        Ok(Value::Array(result))
1075    }
1076}
1077
1078// =============================================================================
1079// interpose(array, separator) -> array (insert separator between elements)
1080// =============================================================================
1081
1082defn!(InterposeFn, vec![arg!(array), arg!(any)], None);
1083
1084impl Function for InterposeFn {
1085    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1086        self.signature.validate(args, ctx)?;
1087
1088        let arr = args[0]
1089            .as_array()
1090            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1091
1092        let separator = args[1].clone();
1093
1094        if arr.is_empty() {
1095            return Ok(Value::Array(vec![]));
1096        }
1097
1098        if arr.len() == 1 {
1099            return Ok(Value::Array(arr.clone()));
1100        }
1101
1102        let mut result = Vec::with_capacity(arr.len() * 2 - 1);
1103        for (i, item) in arr.iter().enumerate() {
1104            if i > 0 {
1105                result.push(separator.clone());
1106            }
1107            result.push(item.clone());
1108        }
1109
1110        Ok(Value::Array(result))
1111    }
1112}
1113
1114// =============================================================================
1115// zipmap(keys, values) -> object (create object from parallel arrays)
1116// =============================================================================
1117
1118defn!(ZipmapFn, vec![arg!(array), arg!(array)], None);
1119
1120impl Function for ZipmapFn {
1121    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1122        self.signature.validate(args, ctx)?;
1123
1124        let keys = args[0]
1125            .as_array()
1126            .ok_or_else(|| custom_error(ctx, "Expected array argument for keys"))?;
1127
1128        let values = args[1]
1129            .as_array()
1130            .ok_or_else(|| custom_error(ctx, "Expected array argument for values"))?;
1131
1132        let len = keys.len().min(values.len());
1133        let mut result = serde_json::Map::new();
1134
1135        for i in 0..len {
1136            let key = keys[i]
1137                .as_str()
1138                .ok_or_else(|| custom_error(ctx, "Keys must be strings"))?;
1139            result.insert(key.to_string(), values[i].clone());
1140        }
1141
1142        Ok(Value::Object(result))
1143    }
1144}
1145
1146// =============================================================================
1147// partition_by(array, field_name) -> array (split when field value changes)
1148// =============================================================================
1149
1150defn!(PartitionByFn, vec![arg!(array), arg!(string)], None);
1151
1152impl Function for PartitionByFn {
1153    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1154        self.signature.validate(args, ctx)?;
1155
1156        let arr = args[0]
1157            .as_array()
1158            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1159
1160        let field_name = args[1]
1161            .as_str()
1162            .ok_or_else(|| custom_error(ctx, "Expected field name string"))?;
1163
1164        if arr.is_empty() {
1165            return Ok(Value::Array(vec![]));
1166        }
1167
1168        let mut result: Vec<Value> = Vec::new();
1169        let mut current_partition: Vec<Value> = Vec::new();
1170        let mut last_key: Option<String> = None;
1171
1172        for item in arr {
1173            // Extract key from object field, or serialize the item itself for primitives
1174            let key = if let Some(obj) = item.as_object() {
1175                if let Some(field_value) = obj.get(field_name) {
1176                    serde_json::to_string(field_value).unwrap_or_default()
1177                } else {
1178                    "null".to_string()
1179                }
1180            } else {
1181                // For non-objects, use the value itself as the key
1182                serde_json::to_string(item).unwrap_or_default()
1183            };
1184
1185            match &last_key {
1186                Some(prev_key) if *prev_key == key => {
1187                    current_partition.push(item.clone());
1188                }
1189                _ => {
1190                    if !current_partition.is_empty() {
1191                        result.push(Value::Array(current_partition));
1192                    }
1193                    current_partition = vec![item.clone()];
1194                    last_key = Some(key);
1195                }
1196            }
1197        }
1198
1199        // Don't forget the last partition
1200        if !current_partition.is_empty() {
1201            result.push(Value::Array(current_partition));
1202        }
1203
1204        Ok(Value::Array(result))
1205    }
1206}
1207
1208// =============================================================================
1209// dedupe(array) -> array (remove consecutive duplicates)
1210// =============================================================================
1211
1212defn!(DedupeFn, vec![arg!(array)], None);
1213
1214impl Function for DedupeFn {
1215    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1216        self.signature.validate(args, ctx)?;
1217
1218        let arr = args[0]
1219            .as_array()
1220            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1221
1222        if arr.is_empty() {
1223            return Ok(Value::Array(vec![]));
1224        }
1225
1226        let mut result: Vec<Value> = Vec::with_capacity(arr.len());
1227        let mut last_key: Option<String> = None;
1228
1229        for item in arr {
1230            let key = serde_json::to_string(item).unwrap_or_default();
1231            match &last_key {
1232                Some(prev_key) if *prev_key == key => {
1233                    // Skip consecutive duplicate
1234                }
1235                _ => {
1236                    result.push(item.clone());
1237                    last_key = Some(key);
1238                }
1239            }
1240        }
1241
1242        Ok(Value::Array(result))
1243    }
1244}
1245
1246// =============================================================================
1247// tail(array) -> array (all elements except the first)
1248// =============================================================================
1249
1250defn!(TailFn, vec![arg!(array)], None);
1251
1252impl Function for TailFn {
1253    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1254        self.signature.validate(args, ctx)?;
1255
1256        let arr = args[0]
1257            .as_array()
1258            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1259
1260        if arr.is_empty() {
1261            return Ok(Value::Array(vec![]));
1262        }
1263
1264        let result: Vec<Value> = arr[1..].to_vec();
1265        Ok(Value::Array(result))
1266    }
1267}
1268
1269// =============================================================================
1270// without(array, values_array) -> array (remove specified values)
1271// =============================================================================
1272
1273defn!(WithoutFn, vec![arg!(array), arg!(array)], None);
1274
1275impl Function for WithoutFn {
1276    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1277        self.signature.validate(args, ctx)?;
1278
1279        let arr = args[0]
1280            .as_array()
1281            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1282
1283        let exclude = args[1]
1284            .as_array()
1285            .ok_or_else(|| custom_error(ctx, "Expected array argument for values to exclude"))?;
1286
1287        // Create a set of serialized values to exclude for efficient lookup
1288        let exclude_set: HashSet<String> = exclude
1289            .iter()
1290            .map(|v| serde_json::to_string(v).unwrap_or_default())
1291            .collect();
1292
1293        let result: Vec<Value> = arr
1294            .iter()
1295            .filter(|item| {
1296                let key = serde_json::to_string(*item).unwrap_or_default();
1297                !exclude_set.contains(&key)
1298            })
1299            .cloned()
1300            .collect();
1301
1302        Ok(Value::Array(result))
1303    }
1304}
1305
1306// =============================================================================
1307// xor(array1, array2) -> array (symmetric difference)
1308// =============================================================================
1309
1310defn!(XorFn, vec![arg!(array), arg!(array)], None);
1311
1312impl Function for XorFn {
1313    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1314        self.signature.validate(args, ctx)?;
1315
1316        let arr1 = args[0]
1317            .as_array()
1318            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1319
1320        let arr2 = args[1]
1321            .as_array()
1322            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1323
1324        // Create sets of serialized values
1325        let set1: HashSet<String> = arr1
1326            .iter()
1327            .map(|v| serde_json::to_string(v).unwrap_or_default())
1328            .collect();
1329
1330        let set2: HashSet<String> = arr2
1331            .iter()
1332            .map(|v| serde_json::to_string(v).unwrap_or_default())
1333            .collect();
1334
1335        let mut result = Vec::new();
1336
1337        // Add elements from arr1 that are not in arr2
1338        for item in arr1 {
1339            let key = serde_json::to_string(item).unwrap_or_default();
1340            if !set2.contains(&key) {
1341                result.push(item.clone());
1342            }
1343        }
1344
1345        // Add elements from arr2 that are not in arr1
1346        for item in arr2 {
1347            let key = serde_json::to_string(item).unwrap_or_default();
1348            if !set1.contains(&key) {
1349                result.push(item.clone());
1350            }
1351        }
1352
1353        Ok(Value::Array(result))
1354    }
1355}
1356
1357// =============================================================================
1358// window(array, size, step?) -> array (sliding window)
1359// =============================================================================
1360
1361defn!(
1362    WindowFn,
1363    vec![arg!(array), arg!(number)],
1364    Some(arg!(number))
1365);
1366
1367impl Function for WindowFn {
1368    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1369        self.signature.validate(args, ctx)?;
1370
1371        let arr = args[0]
1372            .as_array()
1373            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1374
1375        let size = args[1]
1376            .as_f64()
1377            .ok_or_else(|| custom_error(ctx, "Expected number for window size"))?
1378            as usize;
1379
1380        if size == 0 {
1381            return Ok(Value::Array(vec![]));
1382        }
1383
1384        // Default step is 1
1385        let step = if args.len() > 2 {
1386            args[2]
1387                .as_f64()
1388                .ok_or_else(|| custom_error(ctx, "Expected number for step"))? as usize
1389        } else {
1390            1
1391        };
1392
1393        if step == 0 {
1394            return Err(custom_error(ctx, "Step cannot be zero"));
1395        }
1396
1397        let len = arr.len();
1398        if len < size {
1399            return Ok(Value::Array(vec![]));
1400        }
1401
1402        let mut result = Vec::new();
1403        let mut i = 0;
1404
1405        while i + size <= len {
1406            let window: Vec<Value> = arr[i..i + size].to_vec();
1407            result.push(Value::Array(window));
1408            i += step;
1409        }
1410
1411        Ok(Value::Array(result))
1412    }
1413}
1414
1415// =============================================================================
1416// combinations(array, k) -> array (k-combinations of array)
1417// =============================================================================
1418
1419defn!(CombinationsFn, vec![arg!(array), arg!(number)], None);
1420
1421fn generate_combinations(arr: &[Value], k: usize) -> Vec<Vec<Value>> {
1422    if k == 0 {
1423        return vec![vec![]];
1424    }
1425    if arr.len() < k {
1426        return vec![];
1427    }
1428
1429    let mut result = Vec::new();
1430
1431    // Include first element in combination
1432    let first = arr[0].clone();
1433    let rest = &arr[1..];
1434    for mut combo in generate_combinations(rest, k - 1) {
1435        let mut new_combo = vec![first.clone()];
1436        new_combo.append(&mut combo);
1437        result.push(new_combo);
1438    }
1439
1440    // Exclude first element
1441    result.extend(generate_combinations(rest, k));
1442
1443    result
1444}
1445
1446impl Function for CombinationsFn {
1447    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1448        self.signature.validate(args, ctx)?;
1449
1450        let arr = args[0]
1451            .as_array()
1452            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1453
1454        let k = args[1]
1455            .as_f64()
1456            .ok_or_else(|| custom_error(ctx, "Expected number for k"))? as usize;
1457
1458        // Limit to prevent excessive computation
1459        const MAX_COMBINATIONS: usize = 10000;
1460
1461        // Quick check: if C(n, k) would be too large, return error
1462        let n = arr.len();
1463        if n > 20 && k > 3 && k < n - 3 {
1464            return Err(custom_error(ctx, "Combination size too large"));
1465        }
1466
1467        let combinations = generate_combinations(arr, k);
1468
1469        if combinations.len() > MAX_COMBINATIONS {
1470            return Err(custom_error(ctx, "Too many combinations generated"));
1471        }
1472
1473        let result: Vec<Value> = combinations.into_iter().map(Value::Array).collect();
1474
1475        Ok(Value::Array(result))
1476    }
1477}
1478
1479// =============================================================================
1480// fill(array, value, start?, end?) -> array (fill range with value)
1481// =============================================================================
1482
1483defn!(FillFn, vec![arg!(array), arg!(any)], Some(arg!(number)));
1484
1485impl Function for FillFn {
1486    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1487        self.signature.validate(args, ctx)?;
1488
1489        let arr = args[0]
1490            .as_array()
1491            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1492
1493        let fill_value = args[1].clone();
1494
1495        let len = arr.len();
1496        if len == 0 {
1497            return Ok(Value::Array(vec![]));
1498        }
1499
1500        // Default start is 0, default end is array length
1501        let start = if args.len() > 2 {
1502            let s = args[2]
1503                .as_f64()
1504                .ok_or_else(|| custom_error(ctx, "Expected number for start index"))?
1505                as i64;
1506            // Handle negative indices
1507            if s < 0 {
1508                (len as i64 + s).max(0) as usize
1509            } else {
1510                (s as usize).min(len)
1511            }
1512        } else {
1513            0
1514        };
1515
1516        let end = if args.len() > 3 {
1517            let e = args[3]
1518                .as_f64()
1519                .ok_or_else(|| custom_error(ctx, "Expected number for end index"))?
1520                as i64;
1521            // Handle negative indices
1522            if e < 0 {
1523                (len as i64 + e).max(0) as usize
1524            } else {
1525                (e as usize).min(len)
1526            }
1527        } else {
1528            len
1529        };
1530
1531        let mut result: Vec<Value> = arr.clone();
1532
1533        for item in result.iter_mut().take(end.min(len)).skip(start) {
1534            *item = fill_value.clone();
1535        }
1536
1537        Ok(Value::Array(result))
1538    }
1539}
1540
1541// =============================================================================
1542// pull_at(array, indices_array) -> array (get elements at specified indices)
1543// =============================================================================
1544
1545defn!(PullAtFn, vec![arg!(array), arg!(array)], None);
1546
1547impl Function for PullAtFn {
1548    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1549        self.signature.validate(args, ctx)?;
1550
1551        let arr = args[0]
1552            .as_array()
1553            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1554
1555        let indices = args[1]
1556            .as_array()
1557            .ok_or_else(|| custom_error(ctx, "Expected array of indices"))?;
1558
1559        let len = arr.len();
1560        let mut result = Vec::new();
1561
1562        for idx_var in indices {
1563            let idx = idx_var
1564                .as_f64()
1565                .ok_or_else(|| custom_error(ctx, "Expected number in indices array"))?
1566                as i64;
1567
1568            // Handle negative indices
1569            let actual_idx = if idx < 0 {
1570                (len as i64 + idx).max(0) as usize
1571            } else {
1572                idx as usize
1573            };
1574
1575            if actual_idx < len {
1576                result.push(arr[actual_idx].clone());
1577            }
1578        }
1579
1580        Ok(Value::Array(result))
1581    }
1582}
1583
1584// =============================================================================
1585// transpose(array) -> array
1586// =============================================================================
1587
1588defn!(TransposeFn, vec![arg!(array)], None);
1589
1590impl Function for TransposeFn {
1591    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1592        self.signature.validate(args, ctx)?;
1593
1594        let arr = args[0]
1595            .as_array()
1596            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1597
1598        if arr.is_empty() {
1599            return Ok(Value::Array(vec![]));
1600        }
1601
1602        // Get all inner arrays and find the minimum length
1603        let mut inner_arrays: Vec<&Vec<Value>> = Vec::new();
1604        let mut min_len = usize::MAX;
1605
1606        for item in arr {
1607            if let Some(inner) = item.as_array() {
1608                min_len = min_len.min(inner.len());
1609                inner_arrays.push(inner);
1610            } else {
1611                // If any element is not an array, return empty
1612                return Ok(Value::Array(vec![]));
1613            }
1614        }
1615
1616        if inner_arrays.is_empty() || min_len == 0 {
1617            return Ok(Value::Array(vec![]));
1618        }
1619
1620        // Transpose: create new arrays where each contains the i-th element from each inner array
1621        let mut result = Vec::with_capacity(min_len);
1622        for i in 0..min_len {
1623            let mut row = Vec::with_capacity(inner_arrays.len());
1624            for inner in &inner_arrays {
1625                row.push(inner[i].clone());
1626            }
1627            result.push(Value::Array(row));
1628        }
1629
1630        Ok(Value::Array(result))
1631    }
1632}
1633
1634// =============================================================================
1635// pairwise(array) -> array
1636// =============================================================================
1637
1638defn!(PairwiseFn, vec![arg!(array)], None);
1639
1640impl Function for PairwiseFn {
1641    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1642        self.signature.validate(args, ctx)?;
1643
1644        let arr = args[0]
1645            .as_array()
1646            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1647
1648        if arr.len() < 2 {
1649            return Ok(Value::Array(vec![]));
1650        }
1651
1652        let mut result = Vec::with_capacity(arr.len() - 1);
1653        for i in 0..arr.len() - 1 {
1654            let pair = vec![arr[i].clone(), arr[i + 1].clone()];
1655            result.push(Value::Array(pair));
1656        }
1657
1658        Ok(Value::Array(result))
1659    }
1660}
1661
1662// =============================================================================
1663// indices_array(array, value) -> array of indices
1664// =============================================================================
1665
1666defn!(IndicesArrayFn, vec![arg!(array), arg!(any)], None);
1667
1668impl Function for IndicesArrayFn {
1669    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1670        self.signature.validate(args, ctx)?;
1671
1672        let arr = args[0]
1673            .as_array()
1674            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1675
1676        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
1677
1678        let mut indices: Vec<Value> = Vec::new();
1679        for (i, item) in arr.iter().enumerate() {
1680            let item_key = serde_json::to_string(item).unwrap_or_default();
1681            if item_key == search_key {
1682                indices.push(Value::Number(Number::from(i as i64)));
1683            }
1684        }
1685
1686        Ok(Value::Array(indices))
1687    }
1688}
1689
1690// =============================================================================
1691// inside_array(needle, haystack) -> boolean
1692// =============================================================================
1693
1694defn!(InsideArrayFn, vec![arg!(array), arg!(array)], None);
1695
1696impl Function for InsideArrayFn {
1697    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1698        self.signature.validate(args, ctx)?;
1699
1700        let needle = args[0]
1701            .as_array()
1702            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1703
1704        let haystack = args[1]
1705            .as_array()
1706            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1707
1708        // Build a set of serialized haystack values for efficient lookup
1709        let haystack_set: HashSet<String> = haystack
1710            .iter()
1711            .map(|item| serde_json::to_string(item).unwrap_or_default())
1712            .collect();
1713
1714        // Check if all needle elements are in the haystack
1715        let result = needle.iter().all(|item| {
1716            let item_key = serde_json::to_string(item).unwrap_or_default();
1717            haystack_set.contains(&item_key)
1718        });
1719
1720        Ok(Value::Bool(result))
1721    }
1722}
1723
1724// =============================================================================
1725// bsearch(sorted_array, value) -> number
1726// =============================================================================
1727
1728defn!(BsearchFn, vec![arg!(array), arg!(any)], None);
1729
1730impl Function for BsearchFn {
1731    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1732        self.signature.validate(args, ctx)?;
1733
1734        let arr = args[0]
1735            .as_array()
1736            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1737
1738        let target = &args[1];
1739
1740        if arr.is_empty() {
1741            return Ok(Value::Number(Number::from(-1)));
1742        }
1743
1744        // Helper to compare two Value values
1745        // Returns Ordering based on type-aware comparison
1746        fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1747            match (a, b) {
1748                // Numbers: compare numerically
1749                (Value::Number(n1), Value::Number(n2)) => {
1750                    let f1 = n1.as_f64().unwrap_or(0.0);
1751                    let f2 = n2.as_f64().unwrap_or(0.0);
1752                    f1.partial_cmp(&f2).unwrap_or(std::cmp::Ordering::Equal)
1753                }
1754                // Strings: compare lexicographically
1755                (Value::String(s1), Value::String(s2)) => s1.cmp(s2),
1756                // Booleans: false < true
1757                (Value::Bool(b1), Value::Bool(b2)) => b1.cmp(b2),
1758                // Different types or complex types: fall back to JSON string comparison
1759                _ => {
1760                    let s1 = serde_json::to_string(a).unwrap_or_default();
1761                    let s2 = serde_json::to_string(b).unwrap_or_default();
1762                    s1.cmp(&s2)
1763                }
1764            }
1765        }
1766
1767        let mut left = 0i64;
1768        let mut right = arr.len() as i64 - 1;
1769
1770        while left <= right {
1771            let mid = left + (right - left) / 2;
1772
1773            match compare_values(&arr[mid as usize], target) {
1774                std::cmp::Ordering::Equal => {
1775                    return Ok(Value::Number(Number::from(mid)));
1776                }
1777                std::cmp::Ordering::Less => {
1778                    left = mid + 1;
1779                }
1780                std::cmp::Ordering::Greater => {
1781                    right = mid - 1;
1782                }
1783            }
1784        }
1785
1786        // Not found - return -(insertion_point) - 1
1787        // At this point, left is the insertion point
1788        let result = -(left) - 1;
1789        Ok(Value::Number(Number::from(result)))
1790    }
1791}
1792
1793// =============================================================================
1794// repeat_array(value, n) -> array (create array with value repeated n times)
1795// =============================================================================
1796
1797defn!(RepeatArrayFn, vec![arg!(any), arg!(number)], None);
1798
1799impl Function for RepeatArrayFn {
1800    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1801        self.signature.validate(args, ctx)?;
1802
1803        let value = &args[0];
1804        let n = args[1]
1805            .as_f64()
1806            .ok_or_else(|| custom_error(ctx, "Expected number for count argument"))?
1807            as i64;
1808
1809        if n < 0 {
1810            return Err(custom_error(ctx, "Count must be non-negative"));
1811        }
1812
1813        let result: Vec<Value> = (0..n).map(|_| value.clone()).collect();
1814        Ok(Value::Array(result))
1815    }
1816}
1817
1818// =============================================================================
1819// cycle(array, n) -> array (cycle through array n times)
1820// =============================================================================
1821
1822defn!(CycleFn, vec![arg!(array), arg!(number)], None);
1823
1824impl Function for CycleFn {
1825    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1826        self.signature.validate(args, ctx)?;
1827
1828        let arr = args[0]
1829            .as_array()
1830            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1831
1832        let n = args[1]
1833            .as_f64()
1834            .ok_or_else(|| custom_error(ctx, "Expected number for count argument"))?
1835            as i64;
1836
1837        if n < 0 {
1838            return Err(custom_error(ctx, "Count must be non-negative"));
1839        }
1840
1841        if arr.is_empty() || n == 0 {
1842            return Ok(Value::Array(vec![]));
1843        }
1844
1845        let mut result: Vec<Value> = Vec::with_capacity(arr.len() * n as usize);
1846        for _ in 0..n {
1847            result.extend(arr.iter().cloned());
1848        }
1849        Ok(Value::Array(result))
1850    }
1851}
1852
1853#[cfg(test)]
1854mod tests {
1855    use crate::Runtime;
1856    use serde_json::json;
1857
1858    fn setup_runtime() -> Runtime {
1859        Runtime::builder()
1860            .with_standard()
1861            .with_all_extensions()
1862            .build()
1863    }
1864
1865    #[test]
1866    fn test_unique() {
1867        let runtime = setup_runtime();
1868        let expr = runtime.compile("unique(@)").unwrap();
1869        let data = json!([1, 2, 1]);
1870        let result = expr.search(&data).unwrap();
1871        let arr = result.as_array().unwrap();
1872        assert_eq!(arr.len(), 2);
1873    }
1874
1875    #[test]
1876    fn test_first() {
1877        let runtime = setup_runtime();
1878        let expr = runtime.compile("first(@)").unwrap();
1879        let data = json!([1, 2]);
1880        let result = expr.search(&data).unwrap();
1881        assert_eq!(result.as_f64().unwrap() as i64, 1);
1882    }
1883
1884    #[test]
1885    fn test_last() {
1886        let runtime = setup_runtime();
1887        let expr = runtime.compile("last(@)").unwrap();
1888        let data = json!([1, 2]);
1889        let result = expr.search(&data).unwrap();
1890        assert_eq!(result.as_f64().unwrap() as i64, 2);
1891    }
1892
1893    #[test]
1894    fn test_range() {
1895        let runtime = setup_runtime();
1896        let expr = runtime.compile("range(`0`, `5`)").unwrap();
1897        let data = json!(null);
1898        let result = expr.search(&data).unwrap();
1899        let arr = result.as_array().unwrap();
1900        assert_eq!(arr.len(), 5);
1901    }
1902
1903    #[test]
1904    fn test_butlast() {
1905        // butlast is an alias for initial
1906        let runtime = setup_runtime();
1907        let expr = runtime.compile("butlast(@)").unwrap();
1908        let data = json!([1, 2, 3]);
1909        let result = expr.search(&data).unwrap();
1910        let arr = result.as_array().unwrap();
1911        assert_eq!(arr.len(), 2);
1912        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1913        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
1914    }
1915
1916    #[test]
1917    fn test_interpose() {
1918        let runtime = setup_runtime();
1919        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1920        let data = json!([1, 2, 3]);
1921        let result = expr.search(&data).unwrap();
1922        let arr = result.as_array().unwrap();
1923        assert_eq!(arr.len(), 5);
1924        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1925        assert_eq!(arr[1].as_f64().unwrap() as i64, 0);
1926        assert_eq!(arr[2].as_f64().unwrap() as i64, 2);
1927        assert_eq!(arr[3].as_f64().unwrap() as i64, 0);
1928        assert_eq!(arr[4].as_f64().unwrap() as i64, 3);
1929    }
1930
1931    #[test]
1932    fn test_interpose_empty() {
1933        let runtime = setup_runtime();
1934        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1935        let data = json!([]);
1936        let result = expr.search(&data).unwrap();
1937        let arr = result.as_array().unwrap();
1938        assert_eq!(arr.len(), 0);
1939    }
1940
1941    #[test]
1942    fn test_interpose_single() {
1943        let runtime = setup_runtime();
1944        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1945        let data = json!([1]);
1946        let result = expr.search(&data).unwrap();
1947        let arr = result.as_array().unwrap();
1948        assert_eq!(arr.len(), 1);
1949        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1950    }
1951
1952    #[test]
1953    fn test_interpose_string_separator() {
1954        let runtime = setup_runtime();
1955        let expr = runtime.compile("interpose(@, `\"-\"`)").unwrap();
1956        let data = json!(["a", "b", "c"]);
1957        let result = expr.search(&data).unwrap();
1958        let arr = result.as_array().unwrap();
1959        assert_eq!(arr.len(), 5);
1960        assert_eq!(arr[0].as_str().unwrap(), "a");
1961        assert_eq!(arr[1].as_str().unwrap(), "-");
1962        assert_eq!(arr[2].as_str().unwrap(), "b");
1963        assert_eq!(arr[3].as_str().unwrap(), "-");
1964        assert_eq!(arr[4].as_str().unwrap(), "c");
1965    }
1966
1967    #[test]
1968    fn test_zipmap() {
1969        let runtime = setup_runtime();
1970        let expr = runtime
1971            .compile("zipmap(`[\"a\", \"b\", \"c\"]`, `[1, 2, 3]`)")
1972            .unwrap();
1973        let data = json!(null);
1974        let result = expr.search(&data).unwrap();
1975        let obj = result.as_object().unwrap();
1976        assert_eq!(obj.len(), 3);
1977        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 1);
1978        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
1979        assert_eq!(obj.get("c").unwrap().as_f64().unwrap() as i64, 3);
1980    }
1981
1982    #[test]
1983    fn test_zipmap_unequal_lengths() {
1984        let runtime = setup_runtime();
1985        // Keys shorter than values
1986        let expr = runtime
1987            .compile("zipmap(`[\"x\", \"y\"]`, `[10, 20, 30]`)")
1988            .unwrap();
1989        let data = json!(null);
1990        let result = expr.search(&data).unwrap();
1991        let obj = result.as_object().unwrap();
1992        assert_eq!(obj.len(), 2);
1993        assert_eq!(obj.get("x").unwrap().as_f64().unwrap() as i64, 10);
1994        assert_eq!(obj.get("y").unwrap().as_f64().unwrap() as i64, 20);
1995    }
1996
1997    #[test]
1998    fn test_zipmap_empty() {
1999        let runtime = setup_runtime();
2000        let expr = runtime.compile("zipmap(`[]`, `[]`)").unwrap();
2001        let data = json!(null);
2002        let result = expr.search(&data).unwrap();
2003        let obj = result.as_object().unwrap();
2004        assert_eq!(obj.len(), 0);
2005    }
2006
2007    #[test]
2008    fn test_partition_by() {
2009        let runtime = setup_runtime();
2010        // Test with objects - partition by "type" field
2011        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2012        let data: serde_json::Value = serde_json::from_str(
2013            r#"[{"type": "a", "v": 1}, {"type": "a", "v": 2}, {"type": "b", "v": 3}, {"type": "a", "v": 4}]"#,
2014        )
2015        .unwrap();
2016        let result = expr.search(&data).unwrap();
2017        let arr = result.as_array().unwrap();
2018        assert_eq!(arr.len(), 3);
2019
2020        let partition1 = arr[0].as_array().unwrap();
2021        assert_eq!(partition1.len(), 2); // Two "a" items
2022
2023        let partition2 = arr[1].as_array().unwrap();
2024        assert_eq!(partition2.len(), 1); // One "b" item
2025
2026        let partition3 = arr[2].as_array().unwrap();
2027        assert_eq!(partition3.len(), 1); // One more "a" item
2028    }
2029
2030    #[test]
2031    fn test_partition_by_primitives() {
2032        let runtime = setup_runtime();
2033        // For primitives, use any field name - it will use the value itself
2034        let expr = runtime.compile(r#"partition_by(@, `"_"`)"#).unwrap();
2035        let data = json!([1, 1, 2, 2, 1, 1]);
2036        let result = expr.search(&data).unwrap();
2037        let arr = result.as_array().unwrap();
2038        assert_eq!(arr.len(), 3);
2039
2040        let partition1 = arr[0].as_array().unwrap();
2041        assert_eq!(partition1.len(), 2);
2042        assert_eq!(partition1[0].as_f64().unwrap() as i64, 1);
2043
2044        let partition2 = arr[1].as_array().unwrap();
2045        assert_eq!(partition2.len(), 2);
2046        assert_eq!(partition2[0].as_f64().unwrap() as i64, 2);
2047
2048        let partition3 = arr[2].as_array().unwrap();
2049        assert_eq!(partition3.len(), 2);
2050        assert_eq!(partition3[0].as_f64().unwrap() as i64, 1);
2051    }
2052
2053    #[test]
2054    fn test_partition_by_empty() {
2055        let runtime = setup_runtime();
2056        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2057        let data = json!([]);
2058        let result = expr.search(&data).unwrap();
2059        let arr = result.as_array().unwrap();
2060        assert_eq!(arr.len(), 0);
2061    }
2062
2063    #[test]
2064    fn test_partition_by_single() {
2065        let runtime = setup_runtime();
2066        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2067        let data: serde_json::Value = serde_json::from_str(r#"[{"type": "a"}]"#).unwrap();
2068        let result = expr.search(&data).unwrap();
2069        let arr = result.as_array().unwrap();
2070        assert_eq!(arr.len(), 1);
2071        let partition1 = arr[0].as_array().unwrap();
2072        assert_eq!(partition1.len(), 1);
2073    }
2074
2075    #[test]
2076    fn test_dedupe() {
2077        let runtime = setup_runtime();
2078        let expr = runtime.compile("dedupe(@)").unwrap();
2079        let data = json!([1, 1, 2, 2, 1, 1]);
2080        let result = expr.search(&data).unwrap();
2081        let arr = result.as_array().unwrap();
2082        assert_eq!(arr.len(), 3);
2083        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2084        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
2085        assert_eq!(arr[2].as_f64().unwrap() as i64, 1);
2086    }
2087
2088    #[test]
2089    fn test_dedupe_empty() {
2090        let runtime = setup_runtime();
2091        let expr = runtime.compile("dedupe(@)").unwrap();
2092        let data = json!([]);
2093        let result = expr.search(&data).unwrap();
2094        let arr = result.as_array().unwrap();
2095        assert_eq!(arr.len(), 0);
2096    }
2097
2098    #[test]
2099    fn test_dedupe_no_consecutive() {
2100        let runtime = setup_runtime();
2101        let expr = runtime.compile("dedupe(@)").unwrap();
2102        let data = json!([1, 2, 3]);
2103        let result = expr.search(&data).unwrap();
2104        let arr = result.as_array().unwrap();
2105        assert_eq!(arr.len(), 3);
2106    }
2107
2108    #[test]
2109    fn test_dedupe_strings() {
2110        let runtime = setup_runtime();
2111        let expr = runtime.compile("dedupe(@)").unwrap();
2112        let data = json!(["a", "a", "b", "a"]);
2113        let result = expr.search(&data).unwrap();
2114        let arr = result.as_array().unwrap();
2115        assert_eq!(arr.len(), 3);
2116        assert_eq!(arr[0].as_str().unwrap(), "a");
2117        assert_eq!(arr[1].as_str().unwrap(), "b");
2118        assert_eq!(arr[2].as_str().unwrap(), "a");
2119    }
2120
2121    #[test]
2122    fn test_dedupe_objects() {
2123        let runtime = setup_runtime();
2124        let expr = runtime.compile("dedupe(@)").unwrap();
2125        let data: serde_json::Value =
2126            serde_json::from_str(r#"[{"x": 1}, {"x": 1}, {"x": 2}, {"x": 1}]"#).unwrap();
2127        let result = expr.search(&data).unwrap();
2128        let arr = result.as_array().unwrap();
2129        assert_eq!(arr.len(), 3);
2130    }
2131
2132    #[test]
2133    fn test_dedupe_all_same() {
2134        let runtime = setup_runtime();
2135        let expr = runtime.compile("dedupe(@)").unwrap();
2136        let data = json!([1, 1, 1]);
2137        let result = expr.search(&data).unwrap();
2138        let arr = result.as_array().unwrap();
2139        assert_eq!(arr.len(), 1);
2140        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2141    }
2142
2143    #[test]
2144    fn test_zipmap_duplicate_keys() {
2145        // Later key should overwrite earlier one
2146        let runtime = setup_runtime();
2147        let expr = runtime
2148            .compile("zipmap(`[\"a\", \"b\", \"a\"]`, `[1, 2, 3]`)")
2149            .unwrap();
2150        let data = json!(null);
2151        let result = expr.search(&data).unwrap();
2152        let obj = result.as_object().unwrap();
2153        assert_eq!(obj.len(), 2);
2154        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 3); // Last value wins
2155        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
2156    }
2157
2158    #[test]
2159    fn test_zipmap_values_longer() {
2160        let runtime = setup_runtime();
2161        let expr = runtime.compile("zipmap(`[\"a\"]`, `[1, 2, 3]`)").unwrap();
2162        let data = json!(null);
2163        let result = expr.search(&data).unwrap();
2164        let obj = result.as_object().unwrap();
2165        assert_eq!(obj.len(), 1);
2166        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 1);
2167    }
2168
2169    #[test]
2170    fn test_partition_by_missing_field() {
2171        let runtime = setup_runtime();
2172        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2173        // Some objects have type, some don't
2174        let data: serde_json::Value =
2175            serde_json::from_str(r#"[{"type": "a"}, {"name": "no-type"}, {"type": "a"}]"#).unwrap();
2176        let result = expr.search(&data).unwrap();
2177        let arr = result.as_array().unwrap();
2178        // Should partition into 3: [type:a], [no type -> null], [type:a]
2179        assert_eq!(arr.len(), 3);
2180    }
2181
2182    #[test]
2183    fn test_partition_by_all_same() {
2184        let runtime = setup_runtime();
2185        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2186        let data: serde_json::Value =
2187            serde_json::from_str(r#"[{"type": "a"}, {"type": "a"}, {"type": "a"}]"#).unwrap();
2188        let result = expr.search(&data).unwrap();
2189        let arr = result.as_array().unwrap();
2190        assert_eq!(arr.len(), 1);
2191        let partition = arr[0].as_array().unwrap();
2192        assert_eq!(partition.len(), 3);
2193    }
2194
2195    #[test]
2196    fn test_interpose_null_separator() {
2197        let runtime = setup_runtime();
2198        let expr = runtime.compile("interpose(@, `null`)").unwrap();
2199        let data = json!([1, 2]);
2200        let result = expr.search(&data).unwrap();
2201        let arr = result.as_array().unwrap();
2202        assert_eq!(arr.len(), 3);
2203        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2204        assert!(arr[1].is_null());
2205        assert_eq!(arr[2].as_f64().unwrap() as i64, 2);
2206    }
2207
2208    #[test]
2209    fn test_interpose_object_separator() {
2210        let runtime = setup_runtime();
2211        let expr = runtime.compile(r#"interpose(@, `{"sep": true}`)"#).unwrap();
2212        let data = json!([1, 2]);
2213        let result = expr.search(&data).unwrap();
2214        let arr = result.as_array().unwrap();
2215        assert_eq!(arr.len(), 3);
2216        assert!(arr[1].as_object().is_some());
2217    }
2218
2219    // =========================================================================
2220    // zip tests
2221    // =========================================================================
2222
2223    #[test]
2224    fn test_zip_basic() {
2225        let runtime = setup_runtime();
2226        let data: serde_json::Value =
2227            serde_json::from_str(r#"{"a": [1, 2, 3], "b": ["x", "y", "z"]}"#).unwrap();
2228        let expr = runtime.compile("zip(a, b)").unwrap();
2229        let result = expr.search(&data).unwrap();
2230        let arr = result.as_array().unwrap();
2231        assert_eq!(arr.len(), 3);
2232        assert_eq!(arr[0].as_array().unwrap()[0].as_f64().unwrap() as i64, 1);
2233        assert_eq!(arr[0].as_array().unwrap()[1].as_str().unwrap(), "x");
2234    }
2235
2236    #[test]
2237    fn test_zip_unequal_lengths() {
2238        let runtime = setup_runtime();
2239        let data: serde_json::Value =
2240            serde_json::from_str(r#"{"a": [1, 2], "b": ["x", "y", "z"]}"#).unwrap();
2241        let expr = runtime.compile("zip(a, b)").unwrap();
2242        let result = expr.search(&data).unwrap();
2243        let arr = result.as_array().unwrap();
2244        // Stops at shorter array
2245        assert_eq!(arr.len(), 2);
2246    }
2247
2248    #[test]
2249    fn test_zip_empty_array() {
2250        let runtime = setup_runtime();
2251        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2, 3]}"#).unwrap();
2252        let expr = runtime.compile("zip(a, b)").unwrap();
2253        let result = expr.search(&data).unwrap();
2254        let arr = result.as_array().unwrap();
2255        assert_eq!(arr.len(), 0);
2256    }
2257
2258    #[test]
2259    fn test_zip_with_objects() {
2260        let runtime = setup_runtime();
2261        let data: serde_json::Value =
2262            serde_json::from_str(r#"{"names": ["Alice", "Bob"], "scores": [95, 87]}"#).unwrap();
2263        let expr = runtime.compile("zip(names, scores)").unwrap();
2264        let result = expr.search(&data).unwrap();
2265        let arr = result.as_array().unwrap();
2266        assert_eq!(arr.len(), 2);
2267        assert_eq!(arr[0].as_array().unwrap()[0].as_str().unwrap(), "Alice");
2268        assert_eq!(arr[0].as_array().unwrap()[1].as_f64().unwrap() as i64, 95);
2269    }
2270
2271    // =========================================================================
2272    // chunk tests
2273    // =========================================================================
2274
2275    #[test]
2276    fn test_chunk_basic() {
2277        let runtime = setup_runtime();
2278        let data = json!([1, 2, 3, 4, 5]);
2279        let expr = runtime.compile("chunk(@, `2`)").unwrap();
2280        let result = expr.search(&data).unwrap();
2281        let arr = result.as_array().unwrap();
2282        assert_eq!(arr.len(), 3); // [1,2], [3,4], [5]
2283        assert_eq!(arr[0].as_array().unwrap().len(), 2);
2284        assert_eq!(arr[2].as_array().unwrap().len(), 1);
2285    }
2286
2287    #[test]
2288    fn test_chunk_exact_fit() {
2289        let runtime = setup_runtime();
2290        let data = json!([1, 2, 3, 4, 5, 6]);
2291        let expr = runtime.compile("chunk(@, `3`)").unwrap();
2292        let result = expr.search(&data).unwrap();
2293        let arr = result.as_array().unwrap();
2294        assert_eq!(arr.len(), 2);
2295        assert_eq!(arr[0].as_array().unwrap().len(), 3);
2296        assert_eq!(arr[1].as_array().unwrap().len(), 3);
2297    }
2298
2299    #[test]
2300    fn test_chunk_size_larger_than_array() {
2301        let runtime = setup_runtime();
2302        let data = json!([1, 2, 3]);
2303        let expr = runtime.compile("chunk(@, `10`)").unwrap();
2304        let result = expr.search(&data).unwrap();
2305        let arr = result.as_array().unwrap();
2306        assert_eq!(arr.len(), 1);
2307        assert_eq!(arr[0].as_array().unwrap().len(), 3);
2308    }
2309
2310    #[test]
2311    fn test_chunk_size_one() {
2312        let runtime = setup_runtime();
2313        let data = json!([1, 2, 3]);
2314        let expr = runtime.compile("chunk(@, `1`)").unwrap();
2315        let result = expr.search(&data).unwrap();
2316        let arr = result.as_array().unwrap();
2317        assert_eq!(arr.len(), 3);
2318    }
2319
2320    #[test]
2321    fn test_chunk_and_process_pipeline() {
2322        let runtime = setup_runtime();
2323        let data = json!([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2324        let expr = runtime.compile("chunk(@, `3`)").unwrap();
2325        let result = expr.search(&data).unwrap();
2326        let arr = result.as_array().unwrap();
2327        // [1,2,3], [4,5,6], [7,8,9], [10]
2328        assert_eq!(arr.len(), 4);
2329    }
2330
2331    // =========================================================================
2332    // take tests
2333    // =========================================================================
2334
2335    #[test]
2336    fn test_take_basic() {
2337        let runtime = setup_runtime();
2338        let data = json!([1, 2, 3, 4, 5]);
2339        let expr = runtime.compile("take(@, `3`)").unwrap();
2340        let result = expr.search(&data).unwrap();
2341        let arr = result.as_array().unwrap();
2342        assert_eq!(arr.len(), 3);
2343        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2344        assert_eq!(arr[2].as_f64().unwrap() as i64, 3);
2345    }
2346
2347    #[test]
2348    fn test_take_more_than_length() {
2349        let runtime = setup_runtime();
2350        let data = json!([1, 2]);
2351        let expr = runtime.compile("take(@, `10`)").unwrap();
2352        let result = expr.search(&data).unwrap();
2353        let arr = result.as_array().unwrap();
2354        assert_eq!(arr.len(), 2);
2355    }
2356
2357    #[test]
2358    fn test_take_zero() {
2359        let runtime = setup_runtime();
2360        let data = json!([1, 2, 3]);
2361        let expr = runtime.compile("take(@, `0`)").unwrap();
2362        let result = expr.search(&data).unwrap();
2363        let arr = result.as_array().unwrap();
2364        assert_eq!(arr.len(), 0);
2365    }
2366
2367    // =========================================================================
2368    // drop tests
2369    // =========================================================================
2370
2371    #[test]
2372    fn test_drop_basic() {
2373        let runtime = setup_runtime();
2374        let data = json!([1, 2, 3, 4, 5]);
2375        let expr = runtime.compile("drop(@, `2`)").unwrap();
2376        let result = expr.search(&data).unwrap();
2377        let arr = result.as_array().unwrap();
2378        assert_eq!(arr.len(), 3);
2379        assert_eq!(arr[0].as_f64().unwrap() as i64, 3);
2380    }
2381
2382    #[test]
2383    fn test_drop_more_than_length() {
2384        let runtime = setup_runtime();
2385        let data = json!([1, 2]);
2386        let expr = runtime.compile("drop(@, `10`)").unwrap();
2387        let result = expr.search(&data).unwrap();
2388        let arr = result.as_array().unwrap();
2389        assert_eq!(arr.len(), 0);
2390    }
2391
2392    #[test]
2393    fn test_drop_zero() {
2394        let runtime = setup_runtime();
2395        let data = json!([1, 2, 3]);
2396        let expr = runtime.compile("drop(@, `0`)").unwrap();
2397        let result = expr.search(&data).unwrap();
2398        let arr = result.as_array().unwrap();
2399        assert_eq!(arr.len(), 3);
2400    }
2401
2402    // =========================================================================
2403    // flatten_deep tests
2404    // =========================================================================
2405
2406    #[test]
2407    fn test_flatten_deep_basic() {
2408        let runtime = setup_runtime();
2409        let data = json!([[1, 2], [3, 4]]);
2410        let expr = runtime.compile("flatten_deep(@)").unwrap();
2411        let result = expr.search(&data).unwrap();
2412        let arr = result.as_array().unwrap();
2413        assert_eq!(arr.len(), 4);
2414    }
2415
2416    #[test]
2417    fn test_flatten_deep_nested() {
2418        let runtime = setup_runtime();
2419        let data = json!([1, [2, [3, [4, [5]]]]]);
2420        let expr = runtime.compile("flatten_deep(@)").unwrap();
2421        let result = expr.search(&data).unwrap();
2422        let arr = result.as_array().unwrap();
2423        assert_eq!(arr.len(), 5);
2424        assert_eq!(arr[4].as_f64().unwrap() as i64, 5);
2425    }
2426
2427    #[test]
2428    fn test_flatten_deep_already_flat() {
2429        let runtime = setup_runtime();
2430        let data = json!([1, 2, 3]);
2431        let expr = runtime.compile("flatten_deep(@)").unwrap();
2432        let result = expr.search(&data).unwrap();
2433        let arr = result.as_array().unwrap();
2434        assert_eq!(arr.len(), 3);
2435    }
2436
2437    #[test]
2438    fn test_flatten_deep_mixed() {
2439        let runtime = setup_runtime();
2440        let data = json!([1, [2, 3], [[4]], [[[5, 6]]]]);
2441        let expr = runtime.compile("flatten_deep(@)").unwrap();
2442        let result = expr.search(&data).unwrap();
2443        let arr = result.as_array().unwrap();
2444        assert_eq!(arr.len(), 6);
2445    }
2446
2447    // =========================================================================
2448    // compact tests
2449    // =========================================================================
2450
2451    #[test]
2452    fn test_compact_basic() {
2453        let runtime = setup_runtime();
2454        let data = json!([1, null, 2, false, 3]);
2455        let expr = runtime.compile("compact(@)").unwrap();
2456        let result = expr.search(&data).unwrap();
2457        let arr = result.as_array().unwrap();
2458        assert_eq!(arr.len(), 3);
2459    }
2460
2461    #[test]
2462    fn test_compact_keeps_zero_and_empty_string() {
2463        let runtime = setup_runtime();
2464        let data = json!([0, "", null, true]);
2465        let expr = runtime.compile("compact(@)").unwrap();
2466        let result = expr.search(&data).unwrap();
2467        let arr = result.as_array().unwrap();
2468        assert_eq!(arr.len(), 3); // 0, "", true
2469    }
2470
2471    #[test]
2472    fn test_compact_all_falsy() {
2473        let runtime = setup_runtime();
2474        let data = json!([null, false, null]);
2475        let expr = runtime.compile("compact(@)").unwrap();
2476        let result = expr.search(&data).unwrap();
2477        let arr = result.as_array().unwrap();
2478        assert_eq!(arr.len(), 0);
2479    }
2480
2481    // =========================================================================
2482    // index_at tests
2483    // =========================================================================
2484
2485    #[test]
2486    fn test_index_at_positive() {
2487        let runtime = setup_runtime();
2488        let data = json!(["a", "b", "c", "d"]);
2489        let expr = runtime.compile("index_at(@, `2`)").unwrap();
2490        let result = expr.search(&data).unwrap();
2491        assert_eq!(result.as_str().unwrap(), "c");
2492    }
2493
2494    #[test]
2495    fn test_index_at_negative() {
2496        let runtime = setup_runtime();
2497        let data = json!(["a", "b", "c", "d"]);
2498        let expr = runtime.compile("index_at(@, `-1`)").unwrap();
2499        let result = expr.search(&data).unwrap();
2500        assert_eq!(result.as_str().unwrap(), "d");
2501    }
2502
2503    #[test]
2504    fn test_index_at_negative_second() {
2505        let runtime = setup_runtime();
2506        let data = json!(["a", "b", "c", "d"]);
2507        let expr = runtime.compile("index_at(@, `-2`)").unwrap();
2508        let result = expr.search(&data).unwrap();
2509        assert_eq!(result.as_str().unwrap(), "c");
2510    }
2511
2512    #[test]
2513    fn test_index_at_out_of_bounds() {
2514        let runtime = setup_runtime();
2515        let data = json!(["a", "b", "c"]);
2516        let expr = runtime.compile("index_at(@, `10`)").unwrap();
2517        let result = expr.search(&data).unwrap();
2518        assert!(result.is_null());
2519    }
2520
2521    // =========================================================================
2522    // includes tests
2523    // =========================================================================
2524
2525    #[test]
2526    fn test_includes_number() {
2527        let runtime = setup_runtime();
2528        let data = json!([1, 2, 3, 4, 5]);
2529        let expr = runtime.compile("includes(@, `3`)").unwrap();
2530        let result = expr.search(&data).unwrap();
2531        assert!(result.as_bool().unwrap());
2532    }
2533
2534    #[test]
2535    fn test_includes_not_found() {
2536        let runtime = setup_runtime();
2537        let data = json!([1, 2, 3]);
2538        let expr = runtime.compile("includes(@, `10`)").unwrap();
2539        let result = expr.search(&data).unwrap();
2540        assert!(!result.as_bool().unwrap());
2541    }
2542
2543    #[test]
2544    fn test_includes_string() {
2545        let runtime = setup_runtime();
2546        let data = json!(["apple", "banana", "cherry"]);
2547        let expr = runtime.compile(r#"includes(@, `"banana"`)"#).unwrap();
2548        let result = expr.search(&data).unwrap();
2549        assert!(result.as_bool().unwrap());
2550    }
2551
2552    #[test]
2553    fn test_includes_object() {
2554        let runtime = setup_runtime();
2555        let data = json!([{"a": 1}, {"b": 2}]);
2556        let expr = runtime.compile(r#"includes(@, `{"a": 1}`)"#).unwrap();
2557        let result = expr.search(&data).unwrap();
2558        assert!(result.as_bool().unwrap());
2559    }
2560
2561    // =========================================================================
2562    // find_index tests
2563    // =========================================================================
2564
2565    #[test]
2566    fn test_find_index_found() {
2567        let runtime = setup_runtime();
2568        let data = json!(["a", "b", "c", "d"]);
2569        let expr = runtime.compile(r#"find_index(@, `"c"`)"#).unwrap();
2570        let result = expr.search(&data).unwrap();
2571        assert_eq!(result.as_f64().unwrap() as i64, 2);
2572    }
2573
2574    #[test]
2575    fn test_find_index_not_found() {
2576        let runtime = setup_runtime();
2577        let data = json!(["a", "b", "c"]);
2578        let expr = runtime.compile(r#"find_index(@, `"z"`)"#).unwrap();
2579        let result = expr.search(&data).unwrap();
2580        assert_eq!(result.as_f64().unwrap() as i64, -1);
2581    }
2582
2583    // =========================================================================
2584    // group_by tests
2585    // =========================================================================
2586
2587    #[test]
2588    fn test_group_by_basic() {
2589        let runtime = setup_runtime();
2590        let data: serde_json::Value = serde_json::from_str(
2591            r#"[{"type": "a", "v": 1}, {"type": "b", "v": 2}, {"type": "a", "v": 3}]"#,
2592        )
2593        .unwrap();
2594        let expr = runtime.compile(r#"group_by(@, `"type"`)"#).unwrap();
2595        let result = expr.search(&data).unwrap();
2596        let obj = result.as_object().unwrap();
2597        assert_eq!(obj.get("a").unwrap().as_array().unwrap().len(), 2);
2598        assert_eq!(obj.get("b").unwrap().as_array().unwrap().len(), 1);
2599    }
2600
2601    // =========================================================================
2602    // index_by tests
2603    // =========================================================================
2604
2605    #[test]
2606    fn test_index_by_basic() {
2607        let runtime = setup_runtime();
2608        let data: serde_json::Value =
2609            serde_json::from_str(r#"[{"id": 1, "name": "alice"}, {"id": 2, "name": "bob"}]"#)
2610                .unwrap();
2611        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2612        let result = expr.search(&data).unwrap();
2613        let obj = result.as_object().unwrap();
2614        assert_eq!(obj.len(), 2);
2615        // Keys are string versions of the id
2616        let alice = obj.get("1").unwrap().as_object().unwrap();
2617        assert_eq!(alice.get("name").unwrap().as_str().unwrap(), "alice");
2618        let bob = obj.get("2").unwrap().as_object().unwrap();
2619        assert_eq!(bob.get("name").unwrap().as_str().unwrap(), "bob");
2620    }
2621
2622    #[test]
2623    fn test_index_by_string_key() {
2624        let runtime = setup_runtime();
2625        let data: serde_json::Value = serde_json::from_str(
2626            r#"[{"code": "US", "name": "United States"}, {"code": "UK", "name": "United Kingdom"}]"#,
2627        )
2628        .unwrap();
2629        let expr = runtime.compile(r#"index_by(@, `"code"`)"#).unwrap();
2630        let result = expr.search(&data).unwrap();
2631        let obj = result.as_object().unwrap();
2632        assert_eq!(obj.len(), 2);
2633        let us = obj.get("US").unwrap().as_object().unwrap();
2634        assert_eq!(us.get("name").unwrap().as_str().unwrap(), "United States");
2635    }
2636
2637    #[test]
2638    fn test_index_by_duplicate_keys() {
2639        // Last value wins for duplicate keys
2640        let runtime = setup_runtime();
2641        let data: serde_json::Value = serde_json::from_str(
2642            r#"[{"type": "a", "v": 1}, {"type": "a", "v": 2}, {"type": "a", "v": 3}]"#,
2643        )
2644        .unwrap();
2645        let expr = runtime.compile(r#"index_by(@, `"type"`)"#).unwrap();
2646        let result = expr.search(&data).unwrap();
2647        let obj = result.as_object().unwrap();
2648        assert_eq!(obj.len(), 1);
2649        // Last value (v=3) wins
2650        let a = obj.get("a").unwrap().as_object().unwrap();
2651        assert_eq!(a.get("v").unwrap().as_f64().unwrap() as i64, 3);
2652    }
2653
2654    #[test]
2655    fn test_index_by_missing_key() {
2656        // Items without the key field are skipped
2657        let runtime = setup_runtime();
2658        let data: serde_json::Value = serde_json::from_str(
2659            r#"[{"id": 1, "name": "alice"}, {"name": "bob"}, {"id": 3, "name": "charlie"}]"#,
2660        )
2661        .unwrap();
2662        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2663        let result = expr.search(&data).unwrap();
2664        let obj = result.as_object().unwrap();
2665        assert_eq!(obj.len(), 2);
2666        assert!(obj.contains_key("1"));
2667        assert!(obj.contains_key("3"));
2668        assert!(!obj.contains_key("2")); // bob was skipped
2669    }
2670
2671    #[test]
2672    fn test_index_by_empty_array() {
2673        let runtime = setup_runtime();
2674        let data = json!([]);
2675        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2676        let result = expr.search(&data).unwrap();
2677        let obj = result.as_object().unwrap();
2678        assert!(obj.is_empty());
2679    }
2680
2681    // =========================================================================
2682    // set operations tests
2683    // =========================================================================
2684
2685    #[test]
2686    fn test_difference() {
2687        let runtime = setup_runtime();
2688        let data: serde_json::Value =
2689            serde_json::from_str(r#"{"a": [1, 2, 3, 4], "b": [2, 4]}"#).unwrap();
2690        let expr = runtime.compile("difference(a, b)").unwrap();
2691        let result = expr.search(&data).unwrap();
2692        let arr = result.as_array().unwrap();
2693        assert_eq!(arr.len(), 2); // 1, 3
2694    }
2695
2696    #[test]
2697    fn test_intersection() {
2698        let runtime = setup_runtime();
2699        let data: serde_json::Value =
2700            serde_json::from_str(r#"{"a": [1, 2, 3], "b": [2, 3, 4]}"#).unwrap();
2701        let expr = runtime.compile("intersection(a, b)").unwrap();
2702        let result = expr.search(&data).unwrap();
2703        let arr = result.as_array().unwrap();
2704        assert_eq!(arr.len(), 2); // 2, 3
2705    }
2706
2707    #[test]
2708    fn test_union() {
2709        let runtime = setup_runtime();
2710        let data: serde_json::Value =
2711            serde_json::from_str(r#"{"a": [1, 2], "b": [2, 3]}"#).unwrap();
2712        let expr = runtime.compile("union(a, b)").unwrap();
2713        let result = expr.search(&data).unwrap();
2714        let arr = result.as_array().unwrap();
2715        assert_eq!(arr.len(), 3); // 1, 2, 3
2716    }
2717
2718    // =========================================================================
2719    // frequencies tests
2720    // =========================================================================
2721
2722    #[test]
2723    fn test_frequencies_basic() {
2724        let runtime = setup_runtime();
2725        let data = json!(["a", "b", "a", "c", "a", "b"]);
2726        let expr = runtime.compile("frequencies(@)").unwrap();
2727        let result = expr.search(&data).unwrap();
2728        let obj = result.as_object().unwrap();
2729        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 3);
2730        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
2731        assert_eq!(obj.get("c").unwrap().as_f64().unwrap() as i64, 1);
2732    }
2733
2734    #[test]
2735    fn test_frequencies_numbers() {
2736        let runtime = setup_runtime();
2737        let data = json!([1, 2, 1, 1, 2, 3]);
2738        let expr = runtime.compile("frequencies(@)").unwrap();
2739        let result = expr.search(&data).unwrap();
2740        let obj = result.as_object().unwrap();
2741        assert_eq!(obj.get("1").unwrap().as_f64().unwrap() as i64, 3);
2742        assert_eq!(obj.get("2").unwrap().as_f64().unwrap() as i64, 2);
2743    }
2744
2745    // =========================================================================
2746    // mode tests
2747    // =========================================================================
2748
2749    #[test]
2750    fn test_mode_basic() {
2751        let runtime = setup_runtime();
2752        let data = json!([1, 2, 2, 3, 2, 4]);
2753        let expr = runtime.compile("mode(@)").unwrap();
2754        let result = expr.search(&data).unwrap();
2755        assert_eq!(result.as_f64().unwrap() as i64, 2);
2756    }
2757
2758    #[test]
2759    fn test_mode_empty() {
2760        let runtime = setup_runtime();
2761        let data = json!([]);
2762        let expr = runtime.compile("mode(@)").unwrap();
2763        let result = expr.search(&data).unwrap();
2764        assert!(result.is_null());
2765    }
2766
2767    // =========================================================================
2768    // cartesian tests
2769    // =========================================================================
2770
2771    #[test]
2772    fn test_cartesian_basic() {
2773        let runtime = setup_runtime();
2774        let data: serde_json::Value =
2775            serde_json::from_str(r#"{"a": [1, 2], "b": ["x", "y"]}"#).unwrap();
2776        let expr = runtime.compile("cartesian(a, b)").unwrap();
2777        let result = expr.search(&data).unwrap();
2778        let arr = result.as_array().unwrap();
2779        assert_eq!(arr.len(), 4); // [1,x], [1,y], [2,x], [2,y]
2780    }
2781
2782    #[test]
2783    fn test_cartesian_empty() {
2784        let runtime = setup_runtime();
2785        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2]}"#).unwrap();
2786        let expr = runtime.compile("cartesian(a, b)").unwrap();
2787        let result = expr.search(&data).unwrap();
2788        let arr = result.as_array().unwrap();
2789        assert_eq!(arr.len(), 0);
2790    }
2791
2792    #[test]
2793    fn test_cartesian_n_way_two_arrays() {
2794        let runtime = setup_runtime();
2795        let data = json!([[1, 2], ["a", "b"]]);
2796        let expr = runtime.compile("cartesian(@)").unwrap();
2797        let result = expr.search(&data).unwrap();
2798        let arr = result.as_array().unwrap();
2799        assert_eq!(arr.len(), 4); // [1,a], [1,b], [2,a], [2,b]
2800    }
2801
2802    #[test]
2803    fn test_cartesian_n_way_three_arrays() {
2804        let runtime = setup_runtime();
2805        let data = json!([[1, 2], ["a", "b"], [true, false]]);
2806        let expr = runtime.compile("cartesian(@)").unwrap();
2807        let result = expr.search(&data).unwrap();
2808        let arr = result.as_array().unwrap();
2809        assert_eq!(arr.len(), 8); // 2 * 2 * 2 = 8 combinations
2810    }
2811
2812    #[test]
2813    fn test_cartesian_n_way_single_array() {
2814        let runtime = setup_runtime();
2815        let data = json!([[1, 2, 3]]);
2816        let expr = runtime.compile("cartesian(@)").unwrap();
2817        let result = expr.search(&data).unwrap();
2818        let arr = result.as_array().unwrap();
2819        assert_eq!(arr.len(), 3); // [1], [2], [3]
2820    }
2821
2822    #[test]
2823    fn test_cartesian_n_way_empty() {
2824        let runtime = setup_runtime();
2825        let data = json!([]);
2826        let expr = runtime.compile("cartesian(@)").unwrap();
2827        let result = expr.search(&data).unwrap();
2828        let arr = result.as_array().unwrap();
2829        assert_eq!(arr.len(), 0);
2830    }
2831
2832    // =========================================================================
2833    // Edge cases
2834    // =========================================================================
2835
2836    #[test]
2837    fn test_first_empty_array() {
2838        let runtime = setup_runtime();
2839        let data = json!([]);
2840        let expr = runtime.compile("first(@)").unwrap();
2841        let result = expr.search(&data).unwrap();
2842        assert!(result.is_null());
2843    }
2844
2845    #[test]
2846    fn test_last_empty_array() {
2847        let runtime = setup_runtime();
2848        let data = json!([]);
2849        let expr = runtime.compile("last(@)").unwrap();
2850        let result = expr.search(&data).unwrap();
2851        assert!(result.is_null());
2852    }
2853
2854    #[test]
2855    fn test_unique_preserves_order() {
2856        let runtime = setup_runtime();
2857        let data = json!(["c", "a", "b", "a", "c"]);
2858        let expr = runtime.compile("unique(@)").unwrap();
2859        let result = expr.search(&data).unwrap();
2860        let arr = result.as_array().unwrap();
2861        assert_eq!(arr.len(), 3);
2862        assert_eq!(arr[0].as_str().unwrap(), "c");
2863        assert_eq!(arr[1].as_str().unwrap(), "a");
2864        assert_eq!(arr[2].as_str().unwrap(), "b");
2865    }
2866
2867    #[test]
2868    fn test_unique_different_types() {
2869        let runtime = setup_runtime();
2870        let data = json!([1, "1", 1, "1"]);
2871        let expr = runtime.compile("unique(@)").unwrap();
2872        let result = expr.search(&data).unwrap();
2873        let arr = result.as_array().unwrap();
2874        assert_eq!(arr.len(), 2); // 1 and "1" are different
2875    }
2876
2877    #[test]
2878    fn test_range_with_step() {
2879        let runtime = setup_runtime();
2880        let data = json!(null);
2881        let expr = runtime.compile("range(`1`, `10`, `2`)").unwrap();
2882        let result = expr.search(&data).unwrap();
2883        let arr = result.as_array().unwrap();
2884        assert_eq!(arr.len(), 5); // 1, 3, 5, 7, 9
2885        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2886        assert_eq!(arr[4].as_f64().unwrap() as i64, 9);
2887    }
2888
2889    #[test]
2890    fn test_range_descending() {
2891        let runtime = setup_runtime();
2892        let data = json!(null);
2893        let expr = runtime.compile("range(`5`, `0`, `-1`)").unwrap();
2894        let result = expr.search(&data).unwrap();
2895        let arr = result.as_array().unwrap();
2896        assert_eq!(arr.len(), 5); // 5, 4, 3, 2, 1
2897        assert_eq!(arr[0].as_f64().unwrap() as i64, 5);
2898        assert_eq!(arr[4].as_f64().unwrap() as i64, 1);
2899    }
2900
2901    // =========================================================================
2902    // Pipeline patterns with arrays
2903    // =========================================================================
2904
2905    #[test]
2906    fn test_pipeline_unique_sort() {
2907        let runtime = setup_runtime();
2908        let data = json!(["redis", "database", "redis", "nosql", "database"]);
2909        let expr = runtime.compile("unique(@) | sort(@)").unwrap();
2910        let result = expr.search(&data).unwrap();
2911        let arr = result.as_array().unwrap();
2912        assert_eq!(arr.len(), 3);
2913        assert_eq!(arr[0].as_str().unwrap(), "database");
2914        assert_eq!(arr[1].as_str().unwrap(), "nosql");
2915        assert_eq!(arr[2].as_str().unwrap(), "redis");
2916    }
2917
2918    #[test]
2919    fn test_pipeline_filter_take() {
2920        let runtime = setup_runtime();
2921        let data = json!([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2922        let expr = runtime.compile("[?@ > `3`] | take(@, `3`)").unwrap();
2923        let result = expr.search(&data).unwrap();
2924        let arr = result.as_array().unwrap();
2925        assert_eq!(arr.len(), 3);
2926        assert_eq!(arr[0].as_f64().unwrap() as i64, 4);
2927        assert_eq!(arr[1].as_f64().unwrap() as i64, 5);
2928        assert_eq!(arr[2].as_f64().unwrap() as i64, 6);
2929    }
2930
2931    #[test]
2932    fn test_pipeline_flatten_unique() {
2933        let runtime = setup_runtime();
2934        let data = json!([[1, 2], [2, 3], [3, 4]]);
2935        let expr = runtime.compile("flatten_deep(@) | unique(@)").unwrap();
2936        let result = expr.search(&data).unwrap();
2937        let arr = result.as_array().unwrap();
2938        assert_eq!(arr.len(), 4); // 1, 2, 3, 4
2939    }
2940
2941    #[test]
2942    fn test_large_array_processing() {
2943        let runtime = setup_runtime();
2944        // Create array with 1000 elements
2945        let items: Vec<i32> = (1..=1000).collect();
2946        let json_str = serde_json::to_string(&items).unwrap();
2947        let data: serde_json::Value = serde_json::from_str(&json_str).unwrap();
2948
2949        let expr = runtime.compile("length(@)").unwrap();
2950        let result = expr.search(&data).unwrap();
2951        assert_eq!(result.as_f64().unwrap() as i64, 1000);
2952    }
2953
2954    #[test]
2955    fn test_transpose_basic() {
2956        let runtime = setup_runtime();
2957        let data = json!([[1, 2, 3], [4, 5, 6]]);
2958        let expr = runtime.compile("transpose(@)").unwrap();
2959        let result = expr.search(&data).unwrap();
2960        let arr = result.as_array().unwrap();
2961        assert_eq!(arr.len(), 3);
2962        // First column: [1, 4]
2963        let col0 = arr[0].as_array().unwrap();
2964        assert_eq!(col0[0].as_f64().unwrap() as i64, 1);
2965        assert_eq!(col0[1].as_f64().unwrap() as i64, 4);
2966        // Second column: [2, 5]
2967        let col1 = arr[1].as_array().unwrap();
2968        assert_eq!(col1[0].as_f64().unwrap() as i64, 2);
2969        assert_eq!(col1[1].as_f64().unwrap() as i64, 5);
2970    }
2971
2972    #[test]
2973    fn test_transpose_empty() {
2974        let runtime = setup_runtime();
2975        let data = json!([]);
2976        let expr = runtime.compile("transpose(@)").unwrap();
2977        let result = expr.search(&data).unwrap();
2978        let arr = result.as_array().unwrap();
2979        assert_eq!(arr.len(), 0);
2980    }
2981
2982    #[test]
2983    fn test_transpose_unequal_rows() {
2984        let runtime = setup_runtime();
2985        let data = json!([[1, 2], [3, 4, 5], [6, 7]]);
2986        let expr = runtime.compile("transpose(@)").unwrap();
2987        let result = expr.search(&data).unwrap();
2988        let arr = result.as_array().unwrap();
2989        // Should use minimum length (2)
2990        assert_eq!(arr.len(), 2);
2991    }
2992
2993    #[test]
2994    fn test_pairwise_basic() {
2995        let runtime = setup_runtime();
2996        let data = json!([1, 2, 3, 4]);
2997        let expr = runtime.compile("pairwise(@)").unwrap();
2998        let result = expr.search(&data).unwrap();
2999        let arr = result.as_array().unwrap();
3000        assert_eq!(arr.len(), 3);
3001        // First pair: [1, 2]
3002        let pair0 = arr[0].as_array().unwrap();
3003        assert_eq!(pair0[0].as_f64().unwrap() as i64, 1);
3004        assert_eq!(pair0[1].as_f64().unwrap() as i64, 2);
3005        // Second pair: [2, 3]
3006        let pair1 = arr[1].as_array().unwrap();
3007        assert_eq!(pair1[0].as_f64().unwrap() as i64, 2);
3008        assert_eq!(pair1[1].as_f64().unwrap() as i64, 3);
3009    }
3010
3011    #[test]
3012    fn test_pairwise_short_array() {
3013        let runtime = setup_runtime();
3014        let data = json!([1]);
3015        let expr = runtime.compile("pairwise(@)").unwrap();
3016        let result = expr.search(&data).unwrap();
3017        let arr = result.as_array().unwrap();
3018        assert_eq!(arr.len(), 0);
3019    }
3020
3021    #[test]
3022    fn test_sliding_window_alias() {
3023        let runtime = setup_runtime();
3024        let data = json!([1, 2, 3, 4, 5]);
3025        let expr = runtime.compile("sliding_window(@, `3`)").unwrap();
3026        let result = expr.search(&data).unwrap();
3027        let arr = result.as_array().unwrap();
3028        assert_eq!(arr.len(), 3);
3029        // First window: [1, 2, 3]
3030        let win0 = arr[0].as_array().unwrap();
3031        assert_eq!(win0.len(), 3);
3032        assert_eq!(win0[0].as_f64().unwrap() as i64, 1);
3033    }
3034
3035    // indices_array tests
3036    #[test]
3037    fn test_indices_array_found() {
3038        let runtime = setup_runtime();
3039        let data = json!([1, 2, 3, 2, 4, 2]);
3040        let expr = runtime.compile("indices_array(@, `2`)").unwrap();
3041        let result = expr.search(&data).unwrap();
3042        let arr = result.as_array().unwrap();
3043        assert_eq!(arr.len(), 3);
3044        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3045        assert_eq!(arr[1].as_f64().unwrap() as i64, 3);
3046        assert_eq!(arr[2].as_f64().unwrap() as i64, 5);
3047    }
3048
3049    #[test]
3050    fn test_indices_array_not_found() {
3051        let runtime = setup_runtime();
3052        let data = json!([1, 2, 3]);
3053        let expr = runtime.compile("indices_array(@, `5`)").unwrap();
3054        let result = expr.search(&data).unwrap();
3055        let arr = result.as_array().unwrap();
3056        assert_eq!(arr.len(), 0);
3057    }
3058
3059    #[test]
3060    fn test_indices_array_strings() {
3061        let runtime = setup_runtime();
3062        let data = json!(["a", "b", "a", "c", "a"]);
3063        let expr = runtime.compile(r#"indices_array(@, `"a"`)"#).unwrap();
3064        let result = expr.search(&data).unwrap();
3065        let arr = result.as_array().unwrap();
3066        assert_eq!(arr.len(), 3);
3067        assert_eq!(arr[0].as_f64().unwrap() as i64, 0);
3068        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3069        assert_eq!(arr[2].as_f64().unwrap() as i64, 4);
3070    }
3071
3072    // inside_array tests
3073    #[test]
3074    fn test_inside_array_true() {
3075        let runtime = setup_runtime();
3076        let data: serde_json::Value =
3077            serde_json::from_str(r#"{"a": [1, 2], "b": [1, 2, 3, 4]}"#).unwrap();
3078        let expr = runtime.compile("inside_array(a, b)").unwrap();
3079        let result = expr.search(&data).unwrap();
3080        assert!(result.as_bool().unwrap());
3081    }
3082
3083    #[test]
3084    fn test_inside_array_false() {
3085        let runtime = setup_runtime();
3086        let data: serde_json::Value =
3087            serde_json::from_str(r#"{"a": [1, 5], "b": [1, 2, 3, 4]}"#).unwrap();
3088        let expr = runtime.compile("inside_array(a, b)").unwrap();
3089        let result = expr.search(&data).unwrap();
3090        assert!(!result.as_bool().unwrap());
3091    }
3092
3093    #[test]
3094    fn test_inside_array_empty() {
3095        let runtime = setup_runtime();
3096        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2, 3]}"#).unwrap();
3097        let expr = runtime.compile("inside_array(a, b)").unwrap();
3098        let result = expr.search(&data).unwrap();
3099        assert!(result.as_bool().unwrap());
3100    }
3101
3102    // bsearch tests
3103    #[test]
3104    fn test_bsearch_found() {
3105        let runtime = setup_runtime();
3106        let data = json!([1, 3, 5, 7, 9]);
3107        let expr = runtime.compile("bsearch(@, `5`)").unwrap();
3108        let result = expr.search(&data).unwrap();
3109        assert_eq!(result.as_f64().unwrap() as i64, 2);
3110    }
3111
3112    #[test]
3113    fn test_bsearch_not_found_middle() {
3114        let runtime = setup_runtime();
3115        let data = json!([1, 3, 5, 7, 9]);
3116        let expr = runtime.compile("bsearch(@, `4`)").unwrap();
3117        let result = expr.search(&data).unwrap();
3118        assert_eq!(result.as_f64().unwrap() as i64, -3);
3119    }
3120
3121    #[test]
3122    fn test_bsearch_not_found_start() {
3123        let runtime = setup_runtime();
3124        let data = json!([1, 3, 5, 7, 9]);
3125        let expr = runtime.compile("bsearch(@, `0`)").unwrap();
3126        let result = expr.search(&data).unwrap();
3127        assert_eq!(result.as_f64().unwrap() as i64, -1);
3128    }
3129
3130    #[test]
3131    fn test_bsearch_not_found_end() {
3132        let runtime = setup_runtime();
3133        let data = json!([1, 3, 5, 7, 9]);
3134        let expr = runtime.compile("bsearch(@, `10`)").unwrap();
3135        let result = expr.search(&data).unwrap();
3136        assert_eq!(result.as_f64().unwrap() as i64, -6);
3137    }
3138
3139    #[test]
3140    fn test_bsearch_empty_array() {
3141        let runtime = setup_runtime();
3142        let data = json!([]);
3143        let expr = runtime.compile("bsearch(@, `5`)").unwrap();
3144        let result = expr.search(&data).unwrap();
3145        assert_eq!(result.as_f64().unwrap() as i64, -1);
3146    }
3147
3148    // repeat_array tests
3149    #[test]
3150    fn test_repeat_array_basic() {
3151        let runtime = setup_runtime();
3152        let data = json!(null);
3153        let expr = runtime.compile("repeat_array(`1`, `3`)").unwrap();
3154        let result = expr.search(&data).unwrap();
3155        let arr = result.as_array().unwrap();
3156        assert_eq!(arr.len(), 3);
3157        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3158        assert_eq!(arr[1].as_f64().unwrap() as i64, 1);
3159        assert_eq!(arr[2].as_f64().unwrap() as i64, 1);
3160    }
3161
3162    #[test]
3163    fn test_repeat_array_string() {
3164        let runtime = setup_runtime();
3165        let data = json!(null);
3166        let expr = runtime.compile(r#"repeat_array(`"x"`, `4`)"#).unwrap();
3167        let result = expr.search(&data).unwrap();
3168        let arr = result.as_array().unwrap();
3169        assert_eq!(arr.len(), 4);
3170        assert_eq!(arr[0].as_str().unwrap(), "x");
3171        assert_eq!(arr[3].as_str().unwrap(), "x");
3172    }
3173
3174    #[test]
3175    fn test_repeat_array_zero() {
3176        let runtime = setup_runtime();
3177        let data = json!(null);
3178        let expr = runtime.compile("repeat_array(`1`, `0`)").unwrap();
3179        let result = expr.search(&data).unwrap();
3180        let arr = result.as_array().unwrap();
3181        assert_eq!(arr.len(), 0);
3182    }
3183
3184    #[test]
3185    fn test_repeat_array_object() {
3186        let runtime = setup_runtime();
3187        let data = json!(null);
3188        let expr = runtime.compile(r#"repeat_array(`{"a": 1}`, `2`)"#).unwrap();
3189        let result = expr.search(&data).unwrap();
3190        let arr = result.as_array().unwrap();
3191        assert_eq!(arr.len(), 2);
3192        assert_eq!(
3193            arr[0]
3194                .as_object()
3195                .unwrap()
3196                .get("a")
3197                .unwrap()
3198                .as_f64()
3199                .unwrap() as i64,
3200            1
3201        );
3202    }
3203
3204    // cycle tests
3205    #[test]
3206    fn test_cycle_basic() {
3207        let runtime = setup_runtime();
3208        let data = json!([1, 2, 3]);
3209        let expr = runtime.compile("cycle(@, `2`)").unwrap();
3210        let result = expr.search(&data).unwrap();
3211        let arr = result.as_array().unwrap();
3212        assert_eq!(arr.len(), 6);
3213        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3214        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3215        assert_eq!(arr[2].as_f64().unwrap() as i64, 3);
3216        assert_eq!(arr[3].as_f64().unwrap() as i64, 1);
3217        assert_eq!(arr[4].as_f64().unwrap() as i64, 2);
3218        assert_eq!(arr[5].as_f64().unwrap() as i64, 3);
3219    }
3220
3221    #[test]
3222    fn test_cycle_strings() {
3223        let runtime = setup_runtime();
3224        let data = json!(["a", "b"]);
3225        let expr = runtime.compile("cycle(@, `3`)").unwrap();
3226        let result = expr.search(&data).unwrap();
3227        let arr = result.as_array().unwrap();
3228        assert_eq!(arr.len(), 6);
3229        assert_eq!(arr[0].as_str().unwrap(), "a");
3230        assert_eq!(arr[1].as_str().unwrap(), "b");
3231        assert_eq!(arr[2].as_str().unwrap(), "a");
3232    }
3233
3234    #[test]
3235    fn test_cycle_zero() {
3236        let runtime = setup_runtime();
3237        let data = json!([1, 2, 3]);
3238        let expr = runtime.compile("cycle(@, `0`)").unwrap();
3239        let result = expr.search(&data).unwrap();
3240        let arr = result.as_array().unwrap();
3241        assert_eq!(arr.len(), 0);
3242    }
3243
3244    #[test]
3245    fn test_cycle_empty_array() {
3246        let runtime = setup_runtime();
3247        let data = json!([]);
3248        let expr = runtime.compile("cycle(@, `5`)").unwrap();
3249        let result = expr.search(&data).unwrap();
3250        let arr = result.as_array().unwrap();
3251        assert_eq!(arr.len(), 0);
3252    }
3253
3254    #[test]
3255    fn test_cycle_once() {
3256        let runtime = setup_runtime();
3257        let data = json!([1, 2]);
3258        let expr = runtime.compile("cycle(@, `1`)").unwrap();
3259        let result = expr.search(&data).unwrap();
3260        let arr = result.as_array().unwrap();
3261        assert_eq!(arr.len(), 2);
3262        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3263        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3264    }
3265}