1use std::collections::HashSet;
4
5use serde_json::{Number, Value};
6
7use crate::functions::{Function, number_value};
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
14 register_if_enabled(runtime, "round", enabled, Box::new(RoundFn::new()));
15 register_if_enabled(runtime, "floor_fn", enabled, Box::new(FloorFnExt::new()));
16 register_if_enabled(runtime, "ceil_fn", enabled, Box::new(CeilFnExt::new()));
17 register_if_enabled(runtime, "abs_fn", enabled, Box::new(AbsFnExt::new()));
18 register_if_enabled(runtime, "mod_fn", enabled, Box::new(ModFn::new()));
19 register_if_enabled(runtime, "pow", enabled, Box::new(PowFn::new()));
20 register_if_enabled(runtime, "sqrt", enabled, Box::new(SqrtFn::new()));
21 register_if_enabled(runtime, "log", enabled, Box::new(LogFn::new()));
22 register_if_enabled(runtime, "clamp", enabled, Box::new(ClampFn::new()));
23 register_if_enabled(runtime, "median", enabled, Box::new(MedianFn::new()));
24 register_if_enabled(
25 runtime,
26 "percentile",
27 enabled,
28 Box::new(PercentileFn::new()),
29 );
30 register_if_enabled(runtime, "variance", enabled, Box::new(VarianceFn::new()));
31 register_if_enabled(runtime, "stddev", enabled, Box::new(StddevFn::new()));
32 register_if_enabled(runtime, "sin", enabled, Box::new(SinFn::new()));
33 register_if_enabled(runtime, "cos", enabled, Box::new(CosFn::new()));
34 register_if_enabled(runtime, "tan", enabled, Box::new(TanFn::new()));
35 register_if_enabled(runtime, "asin", enabled, Box::new(AsinFn::new()));
36 register_if_enabled(runtime, "acos", enabled, Box::new(AcosFn::new()));
37 register_if_enabled(runtime, "atan", enabled, Box::new(AtanFn::new()));
38 register_if_enabled(runtime, "atan2", enabled, Box::new(Atan2Fn::new()));
39 register_if_enabled(runtime, "deg_to_rad", enabled, Box::new(DegToRadFn::new()));
40 register_if_enabled(runtime, "rad_to_deg", enabled, Box::new(RadToDegFn::new()));
41 register_if_enabled(runtime, "sign", enabled, Box::new(SignFn::new()));
42 register_if_enabled(runtime, "add", enabled, Box::new(AddFn::new()));
43 register_if_enabled(runtime, "subtract", enabled, Box::new(SubtractFn::new()));
44 register_if_enabled(runtime, "multiply", enabled, Box::new(MultiplyFn::new()));
45 register_if_enabled(runtime, "divide", enabled, Box::new(DivideFn::new()));
46 register_if_enabled(runtime, "mode", enabled, Box::new(ModeFn::new()));
47 register_if_enabled(runtime, "to_fixed", enabled, Box::new(ToFixedFn::new()));
48 register_if_enabled(
49 runtime,
50 "format_number",
51 enabled,
52 Box::new(FormatNumberFn::new()),
53 );
54 register_if_enabled(runtime, "histogram", enabled, Box::new(HistogramFn::new()));
55 register_if_enabled(runtime, "normalize", enabled, Box::new(NormalizeFn::new()));
56 register_if_enabled(runtime, "z_score", enabled, Box::new(ZScoreFn::new()));
57 register_if_enabled(
58 runtime,
59 "correlation",
60 enabled,
61 Box::new(CorrelationFn::new()),
62 );
63 register_if_enabled(runtime, "quantile", enabled, Box::new(QuantileFn::new()));
64 register_if_enabled(runtime, "moving_avg", enabled, Box::new(MovingAvgFn::new()));
65 register_if_enabled(runtime, "ewma", enabled, Box::new(EwmaFn::new()));
66 register_if_enabled(
67 runtime,
68 "covariance",
69 enabled,
70 Box::new(CovarianceFn::new()),
71 );
72 register_if_enabled(
73 runtime,
74 "standardize",
75 enabled,
76 Box::new(StandardizeFn::new()),
77 );
78 register_if_enabled(runtime, "quartiles", enabled, Box::new(QuartilesFn::new()));
79 register_if_enabled(
80 runtime,
81 "outliers_iqr",
82 enabled,
83 Box::new(OutliersIqrFn::new()),
84 );
85 register_if_enabled(
86 runtime,
87 "outliers_zscore",
88 enabled,
89 Box::new(OutliersZscoreFn::new()),
90 );
91 register_if_enabled(runtime, "skew", enabled, Box::new(SkewFn::new()));
92 register_if_enabled(runtime, "kurtosis", enabled, Box::new(KurtosisFn::new()));
93 register_if_enabled(runtime, "mad", enabled, Box::new(MadFn::new()));
94 register_if_enabled(runtime, "trend", enabled, Box::new(TrendFn::new()));
96 register_if_enabled(
97 runtime,
98 "trend_slope",
99 enabled,
100 Box::new(TrendSlopeFn::new()),
101 );
102 register_if_enabled(
103 runtime,
104 "rate_of_change",
105 enabled,
106 Box::new(RateOfChangeFn::new()),
107 );
108 register_if_enabled(
109 runtime,
110 "cumulative_sum",
111 enabled,
112 Box::new(CumulativeSumFn::new()),
113 );
114}
115
116defn!(RoundFn, vec![arg!(number)], Some(arg!(number)));
121
122impl Function for RoundFn {
123 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
124 self.signature.validate(args, ctx)?;
125
126 let n = args[0].as_f64().unwrap();
127
128 let precision = if args.len() > 1 {
129 args[1].as_f64().map(|p| p as i32).unwrap_or(0)
130 } else {
131 0
132 };
133
134 let result = if precision == 0 {
135 n.round()
136 } else {
137 let multiplier = 10_f64.powi(precision);
138 (n * multiplier).round() / multiplier
139 };
140
141 Ok(number_value(result))
142 }
143}
144
145defn!(FloorFnExt, vec![arg!(number)], None);
150
151impl Function for FloorFnExt {
152 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
153 self.signature.validate(args, ctx)?;
154 let n = args[0].as_f64().unwrap();
155 Ok(Value::Number(Number::from(n.floor() as i64)))
156 }
157}
158
159defn!(CeilFnExt, vec![arg!(number)], None);
164
165impl Function for CeilFnExt {
166 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
167 self.signature.validate(args, ctx)?;
168 let n = args[0].as_f64().unwrap();
169 Ok(Value::Number(Number::from(n.ceil() as i64)))
170 }
171}
172
173defn!(AbsFnExt, vec![arg!(number)], None);
178
179impl Function for AbsFnExt {
180 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
181 self.signature.validate(args, ctx)?;
182 let n = args[0].as_f64().unwrap();
183 Ok(number_value(n.abs()))
184 }
185}
186
187defn!(ModFn, vec![arg!(number), arg!(number)], None);
192
193impl Function for ModFn {
194 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
195 self.signature.validate(args, ctx)?;
196
197 let n = args[0].as_f64().unwrap();
198 let divisor = args[1].as_f64().unwrap();
199
200 if divisor == 0.0 {
201 return Err(crate::functions::custom_error(ctx, "Division by zero"));
202 }
203
204 Ok(number_value(n % divisor))
205 }
206}
207
208defn!(PowFn, vec![arg!(number), arg!(number)], None);
213
214impl Function for PowFn {
215 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
216 self.signature.validate(args, ctx)?;
217 let base = args[0].as_f64().unwrap();
218 let exp = args[1].as_f64().unwrap();
219 Ok(number_value(base.powf(exp)))
220 }
221}
222
223defn!(SqrtFn, vec![arg!(number)], None);
228
229impl Function for SqrtFn {
230 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
231 self.signature.validate(args, ctx)?;
232 let n = args[0].as_f64().unwrap();
233
234 if n < 0.0 {
235 return Err(crate::functions::custom_error(
236 ctx,
237 "Cannot take square root of negative number",
238 ));
239 }
240
241 Ok(number_value(n.sqrt()))
242 }
243}
244
245defn!(LogFn, vec![arg!(number)], Some(arg!(number)));
250
251impl Function for LogFn {
252 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
253 self.signature.validate(args, ctx)?;
254
255 let n = args[0].as_f64().unwrap();
256
257 if n <= 0.0 {
258 return Err(crate::functions::custom_error(
259 ctx,
260 "Logarithm requires positive number",
261 ));
262 }
263
264 let result = if args.len() > 1 {
265 let base = args[1].as_f64().unwrap();
266 n.log(base)
267 } else {
268 n.ln()
269 };
270
271 Ok(number_value(result))
272 }
273}
274
275defn!(
280 ClampFn,
281 vec![arg!(number), arg!(number), arg!(number)],
282 None
283);
284
285impl Function for ClampFn {
286 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
287 self.signature.validate(args, ctx)?;
288
289 let n = args[0].as_f64().unwrap();
290 let min = args[1].as_f64().unwrap();
291 let max = args[2].as_f64().unwrap();
292
293 let result = n.max(min).min(max);
294
295 Ok(number_value(result))
296 }
297}
298
299defn!(MedianFn, vec![arg!(array)], None);
304
305impl Function for MedianFn {
306 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
307 self.signature.validate(args, ctx)?;
308
309 let arr = args[0].as_array().unwrap();
310
311 let mut numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
312
313 if numbers.is_empty() {
314 return Ok(Value::Null);
315 }
316
317 numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
318
319 let len = numbers.len();
320 let median = if len.is_multiple_of(2) {
321 (numbers[len / 2 - 1] + numbers[len / 2]) / 2.0
322 } else {
323 numbers[len / 2]
324 };
325
326 Ok(number_value(median))
327 }
328}
329
330defn!(PercentileFn, vec![arg!(array), arg!(number)], None);
335
336impl Function for PercentileFn {
337 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
338 self.signature.validate(args, ctx)?;
339
340 let arr = args[0].as_array().unwrap();
341 let p = args[1].as_f64().unwrap();
342
343 if !(0.0..=100.0).contains(&p) {
344 return Err(crate::functions::custom_error(
345 ctx,
346 "Percentile must be between 0 and 100",
347 ));
348 }
349
350 let mut numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
351
352 if numbers.is_empty() {
353 return Ok(Value::Null);
354 }
355
356 numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
357
358 let len = numbers.len();
359 if len == 1 {
360 return Ok(number_value(numbers[0]));
361 }
362
363 let rank = (p / 100.0) * (len - 1) as f64;
364 let lower_idx = rank.floor() as usize;
365 let upper_idx = rank.ceil() as usize;
366 let fraction = rank - lower_idx as f64;
367
368 let result = if lower_idx == upper_idx {
369 numbers[lower_idx]
370 } else {
371 numbers[lower_idx] * (1.0 - fraction) + numbers[upper_idx] * fraction
372 };
373
374 Ok(number_value(result))
375 }
376}
377
378defn!(VarianceFn, vec![arg!(array)], None);
383
384impl Function for VarianceFn {
385 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
386 self.signature.validate(args, ctx)?;
387
388 let arr = args[0].as_array().unwrap();
389
390 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
391
392 if numbers.is_empty() {
393 return Ok(Value::Null);
394 }
395
396 let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
397 let variance =
398 numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / numbers.len() as f64;
399
400 Ok(number_value(variance))
401 }
402}
403
404defn!(StddevFn, vec![arg!(array)], None);
409
410impl Function for StddevFn {
411 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
412 self.signature.validate(args, ctx)?;
413
414 let arr = args[0].as_array().unwrap();
415
416 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
417
418 if numbers.is_empty() {
419 return Ok(Value::Null);
420 }
421
422 let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
423 let variance =
424 numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / numbers.len() as f64;
425 let stddev = variance.sqrt();
426
427 Ok(number_value(stddev))
428 }
429}
430
431defn!(SinFn, vec![arg!(number)], None);
436
437impl Function for SinFn {
438 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
439 self.signature.validate(args, ctx)?;
440 let n = args[0].as_f64().unwrap();
441 Ok(number_value(n.sin()))
442 }
443}
444
445defn!(CosFn, vec![arg!(number)], None);
446
447impl Function for CosFn {
448 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
449 self.signature.validate(args, ctx)?;
450 let n = args[0].as_f64().unwrap();
451 Ok(number_value(n.cos()))
452 }
453}
454
455defn!(TanFn, vec![arg!(number)], None);
456
457impl Function for TanFn {
458 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
459 self.signature.validate(args, ctx)?;
460 let n = args[0].as_f64().unwrap();
461 Ok(number_value(n.tan()))
462 }
463}
464
465defn!(AsinFn, vec![arg!(number)], None);
466
467impl Function for AsinFn {
468 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
469 self.signature.validate(args, ctx)?;
470 let n = args[0].as_f64().unwrap();
471 let result = n.asin();
472 if result.is_nan() {
474 Ok(Value::Null)
475 } else {
476 Ok(number_value(result))
477 }
478 }
479}
480
481defn!(AcosFn, vec![arg!(number)], None);
482
483impl Function for AcosFn {
484 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
485 self.signature.validate(args, ctx)?;
486 let n = args[0].as_f64().unwrap();
487 let result = n.acos();
488 if result.is_nan() {
490 Ok(Value::Null)
491 } else {
492 Ok(number_value(result))
493 }
494 }
495}
496
497defn!(AtanFn, vec![arg!(number)], None);
498
499impl Function for AtanFn {
500 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
501 self.signature.validate(args, ctx)?;
502 let n = args[0].as_f64().unwrap();
503 Ok(number_value(n.atan()))
504 }
505}
506
507defn!(Atan2Fn, vec![arg!(number), arg!(number)], None);
508
509impl Function for Atan2Fn {
510 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
511 self.signature.validate(args, ctx)?;
512 let y = args[0].as_f64().unwrap();
513 let x = args[1].as_f64().unwrap();
514 Ok(number_value(y.atan2(x)))
515 }
516}
517
518defn!(DegToRadFn, vec![arg!(number)], None);
519
520impl Function for DegToRadFn {
521 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
522 self.signature.validate(args, ctx)?;
523 let n = args[0].as_f64().unwrap();
524 Ok(number_value(n.to_radians()))
525 }
526}
527
528defn!(RadToDegFn, vec![arg!(number)], None);
529
530impl Function for RadToDegFn {
531 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
532 self.signature.validate(args, ctx)?;
533 let n = args[0].as_f64().unwrap();
534 Ok(number_value(n.to_degrees()))
535 }
536}
537
538defn!(SignFn, vec![arg!(number)], None);
539
540impl Function for SignFn {
541 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
542 self.signature.validate(args, ctx)?;
543 let n = args[0].as_f64().unwrap();
544 let sign = if n > 0.0 {
545 1
546 } else if n < 0.0 {
547 -1
548 } else {
549 0
550 };
551 Ok(Value::Number(Number::from(sign)))
552 }
553}
554
555defn!(AddFn, vec![arg!(number), arg!(number)], None);
560
561impl Function for AddFn {
562 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
563 self.signature.validate(args, ctx)?;
564 let a = args[0].as_f64().unwrap();
565 let b = args[1].as_f64().unwrap();
566 Ok(number_value(a + b))
567 }
568}
569
570defn!(SubtractFn, vec![arg!(number), arg!(number)], None);
575
576impl Function for SubtractFn {
577 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
578 self.signature.validate(args, ctx)?;
579 let a = args[0].as_f64().unwrap();
580 let b = args[1].as_f64().unwrap();
581 Ok(number_value(a - b))
582 }
583}
584
585defn!(MultiplyFn, vec![arg!(number), arg!(number)], None);
590
591impl Function for MultiplyFn {
592 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
593 self.signature.validate(args, ctx)?;
594 let a = args[0].as_f64().unwrap();
595 let b = args[1].as_f64().unwrap();
596 Ok(number_value(a * b))
597 }
598}
599
600defn!(DivideFn, vec![arg!(number), arg!(number)], None);
605
606impl Function for DivideFn {
607 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
608 self.signature.validate(args, ctx)?;
609 let a = args[0].as_f64().unwrap();
610 let b = args[1].as_f64().unwrap();
611 if b == 0.0 {
612 return Err(crate::functions::custom_error(ctx, "Division by zero"));
613 }
614 Ok(number_value(a / b))
615 }
616}
617
618defn!(ModeFn, vec![arg!(array)], None);
623
624impl Function for ModeFn {
625 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
626 self.signature.validate(args, ctx)?;
627
628 let arr = args[0].as_array().unwrap();
629
630 if arr.is_empty() {
631 return Ok(Value::Null);
632 }
633
634 let mut counts: std::collections::HashMap<String, (usize, Value)> =
636 std::collections::HashMap::new();
637
638 for item in arr.iter() {
639 let key = serde_json::to_string(item).unwrap_or_default();
640 counts
641 .entry(key)
642 .and_modify(|(count, _)| *count += 1)
643 .or_insert((1, item.clone()));
644 }
645
646 let (_, (_, mode_value)) = counts
648 .into_iter()
649 .max_by_key(|(_, (count, _))| *count)
650 .unwrap();
651
652 Ok(mode_value)
653 }
654}
655
656defn!(ToFixedFn, vec![arg!(number), arg!(number)], None);
661
662impl Function for ToFixedFn {
663 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
664 self.signature.validate(args, ctx)?;
665
666 let num = args[0].as_f64().unwrap();
667 let precision = args[1].as_f64().unwrap() as usize;
668
669 let result = format!("{:.prec$}", num, prec = precision);
670 Ok(Value::String(result))
671 }
672}
673
674defn!(FormatNumberFn, vec![arg!(number)], Some(arg!(any)));
679
680impl Function for FormatNumberFn {
681 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
682 self.signature.validate(args, ctx)?;
683
684 let num = args[0].as_f64().unwrap();
685
686 let precision = args
687 .get(1)
688 .and_then(|v| v.as_f64())
689 .map(|n| n as usize)
690 .unwrap_or(0);
691
692 let suffix = args.get(2).and_then(|v| v.as_str()).map(|s| s.to_string());
693
694 let (scaled_num, auto_suffix) = if let Some(ref s) = suffix {
696 match s.as_str() {
697 "k" | "K" => (num / 1_000.0, "k"),
698 "M" => (num / 1_000_000.0, "M"),
699 "B" => (num / 1_000_000_000.0, "B"),
700 "T" => (num / 1_000_000_000_000.0, "T"),
701 "auto" => {
702 let abs_num = num.abs();
703 if abs_num >= 1_000_000_000_000.0 {
704 (num / 1_000_000_000_000.0, "T")
705 } else if abs_num >= 1_000_000_000.0 {
706 (num / 1_000_000_000.0, "B")
707 } else if abs_num >= 1_000_000.0 {
708 (num / 1_000_000.0, "M")
709 } else if abs_num >= 1_000.0 {
710 (num / 1_000.0, "k")
711 } else {
712 (num, "")
713 }
714 }
715 _ => (num, s.as_str()),
716 }
717 } else {
718 (num, "")
719 };
720
721 let formatted = format!("{:.prec$}", scaled_num, prec = precision);
723
724 let result = if suffix.is_none() || suffix.as_deref() == Some("") {
726 add_thousand_separators(&formatted)
727 } else {
728 format!("{}{}", formatted, auto_suffix)
729 };
730
731 Ok(Value::String(result))
732 }
733}
734
735fn add_thousand_separators(s: &str) -> String {
737 let parts: Vec<&str> = s.split('.').collect();
738 let int_part = parts[0];
739 let dec_part = parts.get(1);
740
741 let (sign, digits) = if let Some(stripped) = int_part.strip_prefix('-') {
743 ("-", stripped)
744 } else {
745 ("", int_part)
746 };
747
748 let digit_chars: Vec<char> = digits.chars().collect();
750 let len = digit_chars.len();
751 let with_commas: String = digit_chars
752 .iter()
753 .enumerate()
754 .map(|(i, c)| {
755 let pos_from_right = len - 1 - i;
756 if pos_from_right > 0 && pos_from_right.is_multiple_of(3) {
757 format!("{},", c)
758 } else {
759 c.to_string()
760 }
761 })
762 .collect();
763
764 match dec_part {
765 Some(dec) => format!("{}{}.{}", sign, with_commas, dec),
766 None => format!("{}{}", sign, with_commas),
767 }
768}
769
770defn!(HistogramFn, vec![arg!(array), arg!(number)], None);
775
776impl Function for HistogramFn {
777 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
778 self.signature.validate(args, ctx)?;
779
780 let arr = args[0].as_array().unwrap();
781
782 let num_bins = args[1].as_f64().unwrap() as usize;
783
784 if num_bins == 0 {
785 return Err(crate::functions::custom_error(
786 ctx,
787 "Number of bins must be greater than 0",
788 ));
789 }
790
791 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
793
794 if values.is_empty() {
795 return Ok(Value::Array(vec![]));
796 }
797
798 let min_val = values.iter().cloned().fold(f64::INFINITY, f64::min);
799 let max_val = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
800
801 let bin_width = if (max_val - min_val).abs() < f64::EPSILON {
803 1.0
804 } else {
805 (max_val - min_val) / num_bins as f64
806 };
807
808 let mut bins: Vec<(f64, f64, usize)> = (0..num_bins)
810 .map(|i| {
811 let bin_min = min_val + (i as f64 * bin_width);
812 let bin_max = if i == num_bins - 1 {
813 max_val
814 } else {
815 min_val + ((i + 1) as f64 * bin_width)
816 };
817 (bin_min, bin_max, 0)
818 })
819 .collect();
820
821 for val in &values {
823 let bin_idx = if (max_val - min_val).abs() < f64::EPSILON {
824 0
825 } else {
826 let idx = ((val - min_val) / bin_width) as usize;
827 idx.min(num_bins - 1)
828 };
829 bins[bin_idx].2 += 1;
830 }
831
832 let result: Vec<Value> = bins
834 .into_iter()
835 .map(|(bin_min, bin_max, count)| {
836 let mut map = serde_json::Map::new();
837 map.insert("min".to_string(), number_value(bin_min));
838 map.insert("max".to_string(), number_value(bin_max));
839 map.insert("count".to_string(), Value::Number(Number::from(count)));
840 Value::Object(map)
841 })
842 .collect();
843
844 Ok(Value::Array(result))
845 }
846}
847
848defn!(NormalizeFn, vec![arg!(array)], None);
853
854impl Function for NormalizeFn {
855 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
856 self.signature.validate(args, ctx)?;
857
858 let arr = args[0].as_array().unwrap();
859
860 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
861
862 if values.is_empty() {
863 return Ok(Value::Array(vec![]));
864 }
865
866 let min_val = values.iter().cloned().fold(f64::INFINITY, f64::min);
867 let max_val = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
868 let range = max_val - min_val;
869
870 let result: Vec<Value> = values
871 .iter()
872 .map(|v| {
873 let normalized = if range.abs() < f64::EPSILON {
874 0.0
875 } else {
876 (v - min_val) / range
877 };
878 number_value(normalized)
879 })
880 .collect();
881
882 Ok(Value::Array(result))
883 }
884}
885
886defn!(ZScoreFn, vec![arg!(array)], None);
891
892impl Function for ZScoreFn {
893 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
894 self.signature.validate(args, ctx)?;
895
896 let arr = args[0].as_array().unwrap();
897
898 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
899
900 if values.is_empty() {
901 return Ok(Value::Array(vec![]));
902 }
903
904 let n = values.len() as f64;
905 let mean = values.iter().sum::<f64>() / n;
906 let variance = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / n;
907 let stddev = variance.sqrt();
908
909 let result: Vec<Value> = values
910 .iter()
911 .map(|v| {
912 let z = if stddev.abs() < f64::EPSILON {
913 0.0
914 } else {
915 (v - mean) / stddev
916 };
917 number_value(z)
918 })
919 .collect();
920
921 Ok(Value::Array(result))
922 }
923}
924
925defn!(CorrelationFn, vec![arg!(array), arg!(array)], None);
930
931impl Function for CorrelationFn {
932 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
933 self.signature.validate(args, ctx)?;
934
935 let arr1 = args[0].as_array().unwrap();
936 let arr2 = args[1].as_array().unwrap();
937
938 let values1: Vec<f64> = arr1.iter().filter_map(|v| v.as_f64()).collect();
939 let values2: Vec<f64> = arr2.iter().filter_map(|v| v.as_f64()).collect();
940
941 if values1.is_empty() || values2.is_empty() {
942 return Ok(Value::Null);
943 }
944
945 let n = values1.len().min(values2.len());
947 if n == 0 {
948 return Ok(Value::Null);
949 }
950
951 let values1 = &values1[..n];
952 let values2 = &values2[..n];
953
954 let mean1 = values1.iter().sum::<f64>() / n as f64;
955 let mean2 = values2.iter().sum::<f64>() / n as f64;
956
957 let mut cov = 0.0;
958 let mut var1 = 0.0;
959 let mut var2 = 0.0;
960
961 for i in 0..n {
962 let d1 = values1[i] - mean1;
963 let d2 = values2[i] - mean2;
964 cov += d1 * d2;
965 var1 += d1 * d1;
966 var2 += d2 * d2;
967 }
968
969 let denom = (var1 * var2).sqrt();
970 let correlation = if denom.abs() < f64::EPSILON {
971 0.0
972 } else {
973 cov / denom
974 };
975
976 Ok(number_value(correlation))
977 }
978}
979
980defn!(QuantileFn, vec![arg!(array), arg!(number)], None);
985
986impl Function for QuantileFn {
987 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
988 self.signature.validate(args, ctx)?;
989
990 let arr = args[0].as_array().unwrap();
991 let q = args[1].as_f64().unwrap();
992
993 if !(0.0..=1.0).contains(&q) {
994 return Ok(Value::Null);
995 }
996
997 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
998
999 if values.is_empty() {
1000 return Ok(Value::Null);
1001 }
1002
1003 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1004
1005 let n = values.len();
1006 let pos = q * (n - 1) as f64;
1007 let lower = pos.floor() as usize;
1008 let upper = pos.ceil() as usize;
1009 let frac = pos - lower as f64;
1010
1011 let result = if lower == upper {
1012 values[lower]
1013 } else {
1014 values[lower] * (1.0 - frac) + values[upper] * frac
1015 };
1016
1017 Ok(number_value(result))
1018 }
1019}
1020
1021defn!(MovingAvgFn, vec![arg!(array), arg!(number)], None);
1026
1027impl Function for MovingAvgFn {
1028 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1029 self.signature.validate(args, ctx)?;
1030
1031 let arr = args[0].as_array().unwrap();
1032
1033 let window = args[1].as_f64().unwrap() as usize;
1034
1035 if window == 0 {
1036 return Ok(Value::Null);
1037 }
1038
1039 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1040
1041 if values.is_empty() || window > values.len() {
1042 return Ok(Value::Array(vec![]));
1043 }
1044
1045 let mut result: Vec<Value> = Vec::new();
1046
1047 for i in 0..values.len() {
1048 if i + 1 < window {
1049 result.push(Value::Null);
1050 } else {
1051 let start = i + 1 - window;
1052 let sum: f64 = values[start..=i].iter().sum();
1053 let avg = sum / window as f64;
1054 result.push(number_value(avg));
1055 }
1056 }
1057
1058 Ok(Value::Array(result))
1059 }
1060}
1061
1062defn!(EwmaFn, vec![arg!(array), arg!(number)], None);
1067
1068impl Function for EwmaFn {
1069 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1070 self.signature.validate(args, ctx)?;
1071
1072 let arr = args[0].as_array().unwrap();
1073 let alpha = args[1].as_f64().unwrap();
1074
1075 if !(0.0..=1.0).contains(&alpha) {
1076 return Ok(Value::Null);
1077 }
1078
1079 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1080
1081 if values.is_empty() {
1082 return Ok(Value::Array(vec![]));
1083 }
1084
1085 let mut result: Vec<Value> = Vec::new();
1086 let mut ewma = values[0];
1087
1088 for value in &values {
1089 ewma = alpha * value + (1.0 - alpha) * ewma;
1090 result.push(number_value(ewma));
1091 }
1092
1093 Ok(Value::Array(result))
1094 }
1095}
1096
1097defn!(CovarianceFn, vec![arg!(array), arg!(array)], None);
1102
1103impl Function for CovarianceFn {
1104 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1105 self.signature.validate(args, ctx)?;
1106
1107 let arr1 = args[0].as_array().unwrap();
1108 let arr2 = args[1].as_array().unwrap();
1109
1110 let values1: Vec<f64> = arr1.iter().filter_map(|v| v.as_f64()).collect();
1111 let values2: Vec<f64> = arr2.iter().filter_map(|v| v.as_f64()).collect();
1112
1113 if values1.is_empty() || values1.len() != values2.len() {
1114 return Ok(Value::Null);
1115 }
1116
1117 let n = values1.len() as f64;
1118 let mean1: f64 = values1.iter().sum::<f64>() / n;
1119 let mean2: f64 = values2.iter().sum::<f64>() / n;
1120
1121 let cov: f64 = values1
1122 .iter()
1123 .zip(values2.iter())
1124 .map(|(x, y)| (x - mean1) * (y - mean2))
1125 .sum::<f64>()
1126 / n;
1127
1128 Ok(number_value(cov))
1129 }
1130}
1131
1132defn!(StandardizeFn, vec![arg!(array)], None);
1137
1138impl Function for StandardizeFn {
1139 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1140 self.signature.validate(args, ctx)?;
1141
1142 let arr = args[0].as_array().unwrap();
1143
1144 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1145
1146 if values.is_empty() {
1147 return Ok(Value::Array(vec![]));
1148 }
1149
1150 let n = values.len() as f64;
1151 let mean: f64 = values.iter().sum::<f64>() / n;
1152 let variance: f64 = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n;
1153 let std_dev = variance.sqrt();
1154
1155 let result: Vec<Value> = values
1156 .iter()
1157 .map(|x| {
1158 let standardized = if std_dev.abs() < f64::EPSILON {
1159 0.0
1160 } else {
1161 (x - mean) / std_dev
1162 };
1163 number_value(standardized)
1164 })
1165 .collect();
1166
1167 Ok(Value::Array(result))
1168 }
1169}
1170
1171defn!(QuartilesFn, vec![arg!(array)], None);
1176
1177impl Function for QuartilesFn {
1178 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1179 self.signature.validate(args, ctx)?;
1180
1181 let arr = args[0].as_array().unwrap();
1182
1183 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1184
1185 if values.is_empty() {
1186 return Ok(Value::Null);
1187 }
1188
1189 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1190
1191 let n = values.len();
1192 let min = values[0];
1193 let max = values[n - 1];
1194
1195 let q1 = percentile_value(&values, 25.0);
1196 let q2 = percentile_value(&values, 50.0);
1197 let q3 = percentile_value(&values, 75.0);
1198 let iqr = q3 - q1;
1199
1200 let mut result = serde_json::Map::new();
1201 result.insert("min".to_string(), number_value(min));
1202 result.insert("q1".to_string(), number_value(q1));
1203 result.insert("q2".to_string(), number_value(q2));
1204 result.insert("q3".to_string(), number_value(q3));
1205 result.insert("max".to_string(), number_value(max));
1206 result.insert("iqr".to_string(), number_value(iqr));
1207
1208 Ok(Value::Object(result))
1209 }
1210}
1211
1212fn percentile_value(sorted_values: &[f64], p: f64) -> f64 {
1213 let n = sorted_values.len();
1214 if n == 1 {
1215 return sorted_values[0];
1216 }
1217
1218 let k = (p / 100.0) * (n - 1) as f64;
1219 let f = k.floor() as usize;
1220 let c = k.ceil() as usize;
1221
1222 if f == c {
1223 sorted_values[f]
1224 } else {
1225 let d = k - f as f64;
1226 sorted_values[f] * (1.0 - d) + sorted_values[c] * d
1227 }
1228}
1229
1230defn!(OutliersIqrFn, vec![arg!(array)], Some(arg!(number)));
1235
1236impl Function for OutliersIqrFn {
1237 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1238 self.signature.validate(args, ctx)?;
1239
1240 let arr = args[0].as_array().unwrap();
1241
1242 let multiplier = if args.len() > 1 {
1243 args[1].as_f64().unwrap_or(1.5)
1244 } else {
1245 1.5
1246 };
1247
1248 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1249
1250 if values.is_empty() {
1251 return Ok(Value::Array(vec![]));
1252 }
1253
1254 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1255
1256 let q1 = percentile_value(&values, 25.0);
1257 let q3 = percentile_value(&values, 75.0);
1258 let iqr = q3 - q1;
1259
1260 let lower_bound = q1 - multiplier * iqr;
1261 let upper_bound = q3 + multiplier * iqr;
1262
1263 let outliers: Vec<Value> = arr
1264 .iter()
1265 .filter(|v| {
1266 if let Some(n) = v.as_f64() {
1267 n < lower_bound || n > upper_bound
1268 } else {
1269 false
1270 }
1271 })
1272 .cloned()
1273 .collect();
1274
1275 Ok(Value::Array(outliers))
1276 }
1277}
1278
1279defn!(OutliersZscoreFn, vec![arg!(array)], Some(arg!(number)));
1284
1285impl Function for OutliersZscoreFn {
1286 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1287 self.signature.validate(args, ctx)?;
1288
1289 let arr = args[0].as_array().unwrap();
1290
1291 let threshold = if args.len() > 1 {
1292 args[1].as_f64().unwrap_or(2.0)
1293 } else {
1294 2.0
1295 };
1296
1297 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1298
1299 if values.is_empty() {
1300 return Ok(Value::Array(vec![]));
1301 }
1302
1303 let n = values.len() as f64;
1304 let mean: f64 = values.iter().sum::<f64>() / n;
1305 let variance: f64 = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / n;
1306 let stddev = variance.sqrt();
1307
1308 if stddev.abs() < f64::EPSILON {
1309 return Ok(Value::Array(vec![]));
1310 }
1311
1312 let outliers: Vec<Value> = arr
1313 .iter()
1314 .filter(|v| {
1315 if let Some(n) = v.as_f64() {
1316 let z = (n - mean) / stddev;
1317 z.abs() > threshold
1318 } else {
1319 false
1320 }
1321 })
1322 .cloned()
1323 .collect();
1324
1325 Ok(Value::Array(outliers))
1326 }
1327}
1328
1329defn!(TrendFn, vec![arg!(array)], None);
1334
1335impl Function for TrendFn {
1336 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1337 self.signature.validate(args, ctx)?;
1338
1339 let arr = args[0].as_array().unwrap();
1340
1341 if arr.len() < 2 {
1342 return Ok(Value::String("stable".to_string()));
1343 }
1344
1345 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1346
1347 if values.len() < 2 {
1348 return Ok(Value::String("stable".to_string()));
1349 }
1350
1351 let n = values.len() as f64;
1353 let sum_x: f64 = (0..values.len()).map(|i| i as f64).sum();
1354 let sum_y: f64 = values.iter().sum();
1355 let sum_xy: f64 = values.iter().enumerate().map(|(i, y)| i as f64 * y).sum();
1356 let sum_x2: f64 = (0..values.len()).map(|i| (i as f64).powi(2)).sum();
1357
1358 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x.powi(2));
1359
1360 let mean: f64 = sum_y / n;
1362 let threshold = mean.abs() * 0.01;
1363
1364 let trend = if slope > threshold {
1365 "increasing"
1366 } else if slope < -threshold {
1367 "decreasing"
1368 } else {
1369 "stable"
1370 };
1371
1372 Ok(Value::String(trend.to_string()))
1373 }
1374}
1375
1376defn!(TrendSlopeFn, vec![arg!(array)], None);
1381
1382impl Function for TrendSlopeFn {
1383 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1384 self.signature.validate(args, ctx)?;
1385
1386 let arr = args[0].as_array().unwrap();
1387
1388 if arr.len() < 2 {
1389 return Ok(number_value(0.0));
1390 }
1391
1392 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1393
1394 if values.len() < 2 {
1395 return Ok(number_value(0.0));
1396 }
1397
1398 let n = values.len() as f64;
1400 let sum_x: f64 = (0..values.len()).map(|i| i as f64).sum();
1401 let sum_y: f64 = values.iter().sum();
1402 let sum_xy: f64 = values.iter().enumerate().map(|(i, y)| i as f64 * y).sum();
1403 let sum_x2: f64 = (0..values.len()).map(|i| (i as f64).powi(2)).sum();
1404
1405 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x.powi(2));
1406
1407 Ok(number_value(slope))
1408 }
1409}
1410
1411defn!(RateOfChangeFn, vec![arg!(array)], None);
1416
1417impl Function for RateOfChangeFn {
1418 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1419 self.signature.validate(args, ctx)?;
1420
1421 let arr = args[0].as_array().unwrap();
1422
1423 if arr.len() < 2 {
1424 return Ok(Value::Array(vec![]));
1425 }
1426
1427 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1428
1429 if values.len() < 2 {
1430 return Ok(Value::Array(vec![]));
1431 }
1432
1433 let changes: Vec<Value> = values
1434 .windows(2)
1435 .map(|w| {
1436 let prev = w[0];
1437 let curr = w[1];
1438 let pct_change = if prev != 0.0 {
1439 ((curr - prev) / prev) * 100.0
1440 } else {
1441 0.0
1442 };
1443 number_value(pct_change)
1444 })
1445 .collect();
1446
1447 Ok(Value::Array(changes))
1448 }
1449}
1450
1451defn!(CumulativeSumFn, vec![arg!(array)], None);
1456
1457impl Function for CumulativeSumFn {
1458 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1459 self.signature.validate(args, ctx)?;
1460
1461 let arr = args[0].as_array().unwrap();
1462
1463 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1464
1465 let mut running_sum = 0.0;
1466 let cumsum: Vec<Value> = values
1467 .iter()
1468 .map(|v| {
1469 running_sum += v;
1470 number_value(running_sum)
1471 })
1472 .collect();
1473
1474 Ok(Value::Array(cumsum))
1475 }
1476}
1477
1478defn!(SkewFn, vec![arg!(array)], None);
1483
1484impl Function for SkewFn {
1485 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1486 self.signature.validate(args, ctx)?;
1487 let arr = args[0].as_array().unwrap();
1488 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1489 let n = numbers.len();
1490 if n < 3 {
1491 return Ok(Value::Null);
1492 }
1493 let mean = numbers.iter().sum::<f64>() / n as f64;
1494 let m2: f64 = numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
1495 let m3: f64 = numbers.iter().map(|x| (x - mean).powi(3)).sum::<f64>() / n as f64;
1496 if m2 == 0.0 {
1497 return Ok(number_value(0.0));
1498 }
1499 let skewness = m3 / m2.powf(1.5);
1500 Ok(number_value(skewness))
1501 }
1502}
1503
1504defn!(KurtosisFn, vec![arg!(array)], None);
1509
1510impl Function for KurtosisFn {
1511 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1512 self.signature.validate(args, ctx)?;
1513 let arr = args[0].as_array().unwrap();
1514 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1515 let n = numbers.len();
1516 if n < 4 {
1517 return Ok(Value::Null);
1518 }
1519 let mean = numbers.iter().sum::<f64>() / n as f64;
1520 let m2: f64 = numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
1521 let m4: f64 = numbers.iter().map(|x| (x - mean).powi(4)).sum::<f64>() / n as f64;
1522 if m2 == 0.0 {
1523 return Ok(number_value(0.0));
1524 }
1525 let kurt = (m4 / (m2 * m2)) - 3.0;
1526 Ok(number_value(kurt))
1527 }
1528}
1529
1530defn!(MadFn, vec![arg!(array)], None);
1535
1536impl Function for MadFn {
1537 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1538 self.signature.validate(args, ctx)?;
1539 let arr = args[0].as_array().unwrap();
1540 let mut numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
1541 if numbers.is_empty() {
1542 return Ok(Value::Null);
1543 }
1544 numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1545 let len = numbers.len();
1546 let median = if len.is_multiple_of(2) {
1547 (numbers[len / 2 - 1] + numbers[len / 2]) / 2.0
1548 } else {
1549 numbers[len / 2]
1550 };
1551 let mut abs_devs: Vec<f64> = numbers.iter().map(|x| (x - median).abs()).collect();
1552 abs_devs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1553 let mad = if len.is_multiple_of(2) {
1554 (abs_devs[len / 2 - 1] + abs_devs[len / 2]) / 2.0
1555 } else {
1556 abs_devs[len / 2]
1557 };
1558 Ok(number_value(mad))
1559 }
1560}
1561
1562#[cfg(test)]
1563mod tests {
1564 use crate::Runtime;
1565 use serde_json::json;
1566
1567 fn setup_runtime() -> Runtime {
1568 Runtime::builder()
1569 .with_standard()
1570 .with_all_extensions()
1571 .build()
1572 }
1573
1574 #[test]
1575 #[allow(clippy::approx_constant)]
1576 fn test_round() {
1577 let runtime = setup_runtime();
1578 let expr = runtime.compile("round(`3.14159`, `2`)").unwrap();
1579 let result = expr.search(&json!(null)).unwrap();
1580 assert!((result.as_f64().unwrap() - 3.14_f64).abs() < 0.001);
1581 }
1582
1583 #[test]
1584 fn test_sqrt() {
1585 let runtime = setup_runtime();
1586 let expr = runtime.compile("sqrt(`16`)").unwrap();
1587 let result = expr.search(&json!(null)).unwrap();
1588 assert_eq!(result.as_f64().unwrap() as i64, 4);
1589 }
1590
1591 #[test]
1592 fn test_clamp() {
1593 let runtime = setup_runtime();
1594 let expr = runtime.compile("clamp(`5`, `0`, `3`)").unwrap();
1595 let result = expr.search(&json!(null)).unwrap();
1596 assert_eq!(result.as_f64().unwrap() as i64, 3);
1597 }
1598
1599 #[test]
1600 fn test_add() {
1601 let runtime = setup_runtime();
1602 let expr = runtime.compile("add(`1`, `2`)").unwrap();
1603 let result = expr.search(&json!(null)).unwrap();
1604 assert_eq!(result.as_f64().unwrap() as i64, 3);
1605 }
1606
1607 #[test]
1608 fn test_subtract() {
1609 let runtime = setup_runtime();
1610 let expr = runtime.compile("subtract(`10`, `3`)").unwrap();
1611 let result = expr.search(&json!(null)).unwrap();
1612 assert_eq!(result.as_f64().unwrap() as i64, 7);
1613 }
1614
1615 #[test]
1616 fn test_multiply() {
1617 let runtime = setup_runtime();
1618 let expr = runtime.compile("multiply(`4`, `5`)").unwrap();
1619 let result = expr.search(&json!(null)).unwrap();
1620 assert_eq!(result.as_f64().unwrap() as i64, 20);
1621 }
1622
1623 #[test]
1624 fn test_divide() {
1625 let runtime = setup_runtime();
1626 let expr = runtime.compile("divide(`10`, `4`)").unwrap();
1627 let result = expr.search(&json!(null)).unwrap();
1628 assert_eq!(result.as_f64().unwrap(), 2.5);
1629 }
1630
1631 #[test]
1632 fn test_mode_numbers() {
1633 let runtime = setup_runtime();
1634 let expr = runtime.compile("mode(`[1, 2, 2, 3]`)").unwrap();
1635 let result = expr.search(&json!(null)).unwrap();
1636 assert_eq!(result.as_f64().unwrap() as i64, 2);
1637 }
1638
1639 #[test]
1640 fn test_mode_strings() {
1641 let runtime = setup_runtime();
1642 let expr = runtime
1643 .compile("mode(`[\"a\", \"b\", \"a\", \"c\"]`)")
1644 .unwrap();
1645 let result = expr.search(&json!(null)).unwrap();
1646 assert_eq!(result.as_str().unwrap(), "a");
1647 }
1648
1649 #[test]
1650 fn test_mode_empty() {
1651 let runtime = setup_runtime();
1652 let expr = runtime.compile("mode(`[]`)").unwrap();
1653 let result = expr.search(&json!(null)).unwrap();
1654 assert!(result.is_null());
1655 }
1656
1657 #[test]
1658 fn test_to_fixed() {
1659 let runtime = setup_runtime();
1660 let expr = runtime.compile("to_fixed(`3.14159`, `2`)").unwrap();
1661 let result = expr.search(&json!(null)).unwrap();
1662 assert_eq!(result.as_str().unwrap(), "3.14");
1663 }
1664
1665 #[test]
1666 fn test_to_fixed_padding() {
1667 let runtime = setup_runtime();
1668 let expr = runtime.compile("to_fixed(`3.1`, `3`)").unwrap();
1669 let result = expr.search(&json!(null)).unwrap();
1670 assert_eq!(result.as_str().unwrap(), "3.100");
1671 }
1672
1673 #[test]
1674 fn test_format_number_with_separators() {
1675 let runtime = setup_runtime();
1676 let expr = runtime.compile("format_number(`1234567.89`, `2`)").unwrap();
1677 let result = expr.search(&json!(null)).unwrap();
1678 assert_eq!(result.as_str().unwrap(), "1,234,567.89");
1679 }
1680
1681 #[test]
1682 fn test_format_number_with_k_suffix() {
1683 let runtime = setup_runtime();
1684 let expr = runtime.compile("format_number(`1500`, `1`, 'k')").unwrap();
1685 let result = expr.search(&json!(null)).unwrap();
1686 assert_eq!(result.as_str().unwrap(), "1.5k");
1687 }
1688
1689 #[test]
1690 fn test_format_number_with_m_suffix() {
1691 let runtime = setup_runtime();
1692 let expr = runtime
1693 .compile("format_number(`1500000`, `1`, 'M')")
1694 .unwrap();
1695 let result = expr.search(&json!(null)).unwrap();
1696 assert_eq!(result.as_str().unwrap(), "1.5M");
1697 }
1698
1699 #[test]
1700 fn test_format_number_auto_suffix() {
1701 let runtime = setup_runtime();
1702 let expr = runtime
1703 .compile("format_number(`1500000000`, `2`, 'auto')")
1704 .unwrap();
1705 let result = expr.search(&json!(null)).unwrap();
1706 assert_eq!(result.as_str().unwrap(), "1.50B");
1707 }
1708
1709 #[test]
1710 fn test_histogram() {
1711 let runtime = setup_runtime();
1712 let expr = runtime.compile("histogram(@, `3`)").unwrap();
1713 let data = json!([1, 2, 3, 4, 5, 6, 7, 8, 9]);
1714 let result = expr.search(&data).unwrap();
1715 let arr = result.as_array().unwrap();
1716 assert_eq!(arr.len(), 3);
1717 for bin in arr {
1719 let obj = bin.as_object().unwrap();
1720 assert!(obj.contains_key("min"));
1721 assert!(obj.contains_key("max"));
1722 assert!(obj.contains_key("count"));
1723 }
1724 }
1725
1726 #[test]
1727 fn test_normalize() {
1728 let runtime = setup_runtime();
1729 let expr = runtime.compile("normalize(@)").unwrap();
1730 let data = json!([0, 50, 100]);
1731 let result = expr.search(&data).unwrap();
1732 let arr = result.as_array().unwrap();
1733 assert_eq!(arr.len(), 3);
1734 assert!((arr[0].as_f64().unwrap() - 0.0).abs() < 0.001);
1735 assert!((arr[1].as_f64().unwrap() - 0.5).abs() < 0.001);
1736 assert!((arr[2].as_f64().unwrap() - 1.0).abs() < 0.001);
1737 }
1738
1739 #[test]
1740 fn test_z_score() {
1741 let runtime = setup_runtime();
1742 let expr = runtime.compile("z_score(@)").unwrap();
1743 let data = json!([1, 2, 3, 4, 5]);
1744 let result = expr.search(&data).unwrap();
1745 let arr = result.as_array().unwrap();
1746 assert_eq!(arr.len(), 5);
1747 assert!((arr[2].as_f64().unwrap() - 0.0).abs() < 0.001);
1749 }
1750
1751 #[test]
1752 fn test_correlation_positive() {
1753 let runtime = setup_runtime();
1754 let expr = runtime
1755 .compile("correlation(`[1, 2, 3]`, `[1, 2, 3]`)")
1756 .unwrap();
1757 let result = expr.search(&json!(null)).unwrap();
1758 assert!((result.as_f64().unwrap() - 1.0).abs() < 0.001);
1759 }
1760
1761 #[test]
1762 fn test_correlation_negative() {
1763 let runtime = setup_runtime();
1764 let expr = runtime
1765 .compile("correlation(`[1, 2, 3]`, `[3, 2, 1]`)")
1766 .unwrap();
1767 let result = expr.search(&json!(null)).unwrap();
1768 assert!((result.as_f64().unwrap() - (-1.0)).abs() < 0.001);
1769 }
1770
1771 #[test]
1772 fn test_quantile_median() {
1773 let runtime = setup_runtime();
1774 let expr = runtime
1775 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.5`)")
1776 .unwrap();
1777 let result = expr.search(&json!(null)).unwrap();
1778 assert_eq!(result.as_f64().unwrap(), 3.0);
1779 }
1780
1781 #[test]
1782 fn test_quantile_quartiles() {
1783 let runtime = setup_runtime();
1784 let expr = runtime
1786 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.25`)")
1787 .unwrap();
1788 let result = expr.search(&json!(null)).unwrap();
1789 assert_eq!(result.as_f64().unwrap(), 2.0);
1790
1791 let expr = runtime
1793 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.75`)")
1794 .unwrap();
1795 let result = expr.search(&json!(null)).unwrap();
1796 assert_eq!(result.as_f64().unwrap(), 4.0);
1797 }
1798
1799 #[test]
1800 fn test_moving_avg() {
1801 let runtime = setup_runtime();
1802 let expr = runtime
1803 .compile("moving_avg(`[1, 2, 3, 4, 5, 6]`, `3`)")
1804 .unwrap();
1805 let result = expr.search(&json!(null)).unwrap();
1806 let arr = result.as_array().unwrap();
1807 assert_eq!(arr.len(), 6);
1808 assert!(arr[0].is_null());
1809 assert!(arr[1].is_null());
1810 assert_eq!(arr[2].as_f64().unwrap(), 2.0); assert_eq!(arr[3].as_f64().unwrap(), 3.0); assert_eq!(arr[4].as_f64().unwrap(), 4.0); assert_eq!(arr[5].as_f64().unwrap(), 5.0); }
1815
1816 #[test]
1817 fn test_ewma() {
1818 let runtime = setup_runtime();
1819 let expr = runtime.compile("ewma(`[1, 2, 3, 4, 5]`, `0.5`)").unwrap();
1820 let result = expr.search(&json!(null)).unwrap();
1821 let arr = result.as_array().unwrap();
1822 assert_eq!(arr.len(), 5);
1823 assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1825 assert_eq!(arr[1].as_f64().unwrap(), 1.5); assert_eq!(arr[2].as_f64().unwrap(), 2.25); }
1829
1830 #[test]
1831 fn test_covariance() {
1832 let runtime = setup_runtime();
1833 let expr = runtime
1834 .compile("covariance(`[1, 2, 3]`, `[1, 2, 3]`)")
1835 .unwrap();
1836 let result = expr.search(&json!(null)).unwrap();
1837 assert!((result.as_f64().unwrap() - 0.666666).abs() < 0.01);
1839 }
1840
1841 #[test]
1842 fn test_covariance_negative() {
1843 let runtime = setup_runtime();
1844 let expr = runtime
1845 .compile("covariance(`[1, 2, 3]`, `[3, 2, 1]`)")
1846 .unwrap();
1847 let result = expr.search(&json!(null)).unwrap();
1848 assert!((result.as_f64().unwrap() - (-0.666666)).abs() < 0.01);
1849 }
1850
1851 #[test]
1852 fn test_standardize() {
1853 let runtime = setup_runtime();
1854 let expr = runtime
1855 .compile("standardize(`[10, 20, 30, 40, 50]`)")
1856 .unwrap();
1857 let result = expr.search(&json!(null)).unwrap();
1858 let arr = result.as_array().unwrap();
1859 assert_eq!(arr.len(), 5);
1860 assert!((arr[0].as_f64().unwrap() - (-1.414)).abs() < 0.01);
1863 assert!(arr[2].as_f64().unwrap().abs() < 0.001);
1865 assert!((arr[4].as_f64().unwrap() - 1.414).abs() < 0.01);
1867 }
1868
1869 #[test]
1870 fn test_trend_increasing() {
1871 let runtime = setup_runtime();
1872 let expr = runtime.compile("trend(`[1, 2, 3, 5, 8]`)").unwrap();
1873 let result = expr.search(&json!(null)).unwrap();
1874 assert_eq!(result.as_str().unwrap(), "increasing");
1875 }
1876
1877 #[test]
1878 fn test_trend_decreasing() {
1879 let runtime = setup_runtime();
1880 let expr = runtime.compile("trend(`[10, 9, 8, 7, 6]`)").unwrap();
1881 let result = expr.search(&json!(null)).unwrap();
1882 assert_eq!(result.as_str().unwrap(), "decreasing");
1883 }
1884
1885 #[test]
1886 fn test_trend_stable() {
1887 let runtime = setup_runtime();
1888 let expr = runtime.compile("trend(`[5, 5, 5, 5, 5]`)").unwrap();
1889 let result = expr.search(&json!(null)).unwrap();
1890 assert_eq!(result.as_str().unwrap(), "stable");
1891 }
1892
1893 #[test]
1894 fn test_trend_slope() {
1895 let runtime = setup_runtime();
1896 let expr = runtime.compile("trend_slope(`[0, 1, 2, 3, 4]`)").unwrap();
1897 let result = expr.search(&json!(null)).unwrap();
1898 assert!((result.as_f64().unwrap() - 1.0).abs() < 0.001);
1900 }
1901
1902 #[test]
1903 fn test_rate_of_change() {
1904 let runtime = setup_runtime();
1905 let expr = runtime
1906 .compile("rate_of_change(`[100, 110, 105]`)")
1907 .unwrap();
1908 let result = expr.search(&json!(null)).unwrap();
1909 let arr = result.as_array().unwrap();
1910 assert_eq!(arr.len(), 2);
1911 assert!((arr[0].as_f64().unwrap() - 10.0).abs() < 0.01);
1913 assert!((arr[1].as_f64().unwrap() - (-4.545)).abs() < 0.01);
1915 }
1916
1917 #[test]
1918 fn test_cumulative_sum() {
1919 let runtime = setup_runtime();
1920 let expr = runtime.compile("cumulative_sum(`[1, 2, 3, 4]`)").unwrap();
1921 let result = expr.search(&json!(null)).unwrap();
1922 let arr = result.as_array().unwrap();
1923 assert_eq!(arr.len(), 4);
1924 assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1925 assert_eq!(arr[1].as_f64().unwrap(), 3.0);
1926 assert_eq!(arr[2].as_f64().unwrap(), 6.0);
1927 assert_eq!(arr[3].as_f64().unwrap(), 10.0);
1928 }
1929
1930 #[test]
1931 fn test_skew_symmetric() {
1932 let runtime = setup_runtime();
1933 let expr = runtime.compile("skew(`[1, 2, 3, 4, 5]`)").unwrap();
1934 let result = expr.search(&json!(null)).unwrap();
1935 assert!((result.as_f64().unwrap() - 0.0).abs() < 0.001);
1936 }
1937
1938 #[test]
1939 fn test_skew_right() {
1940 let runtime = setup_runtime();
1941 let expr = runtime.compile("skew(`[1, 1, 1, 10]`)").unwrap();
1942 let result = expr.search(&json!(null)).unwrap();
1943 assert!(result.as_f64().unwrap() > 0.0);
1945 }
1946
1947 #[test]
1948 fn test_skew_too_few() {
1949 let runtime = setup_runtime();
1950 let expr = runtime.compile("skew(`[1, 2]`)").unwrap();
1951 let result = expr.search(&json!(null)).unwrap();
1952 assert!(result.is_null());
1953 }
1954
1955 #[test]
1956 fn test_skew_constant() {
1957 let runtime = setup_runtime();
1958 let expr = runtime.compile("skew(`[5, 5, 5, 5]`)").unwrap();
1959 let result = expr.search(&json!(null)).unwrap();
1960 assert_eq!(result.as_f64().unwrap(), 0.0);
1961 }
1962
1963 #[test]
1964 fn test_kurtosis_uniform_like() {
1965 let runtime = setup_runtime();
1966 let expr = runtime.compile("kurtosis(`[1, 2, 3, 4, 5]`)").unwrap();
1967 let result = expr.search(&json!(null)).unwrap();
1968 assert!(result.as_f64().unwrap() < 0.0);
1970 }
1971
1972 #[test]
1973 fn test_kurtosis_too_few() {
1974 let runtime = setup_runtime();
1975 let expr = runtime.compile("kurtosis(`[1, 2, 3]`)").unwrap();
1976 let result = expr.search(&json!(null)).unwrap();
1977 assert!(result.is_null());
1978 }
1979
1980 #[test]
1981 fn test_kurtosis_constant() {
1982 let runtime = setup_runtime();
1983 let expr = runtime.compile("kurtosis(`[5, 5, 5, 5]`)").unwrap();
1984 let result = expr.search(&json!(null)).unwrap();
1985 assert_eq!(result.as_f64().unwrap(), 0.0);
1986 }
1987
1988 #[test]
1989 fn test_mad_simple() {
1990 let runtime = setup_runtime();
1991 let expr = runtime.compile("mad(`[1, 2, 3, 4, 5]`)").unwrap();
1992 let result = expr.search(&json!(null)).unwrap();
1993 assert_eq!(result.as_f64().unwrap(), 1.0);
1995 }
1996
1997 #[test]
1998 fn test_mad_empty() {
1999 let runtime = setup_runtime();
2000 let expr = runtime.compile("mad(`[]`)").unwrap();
2001 let result = expr.search(&json!(null)).unwrap();
2002 assert!(result.is_null());
2003 }
2004
2005 #[test]
2006 fn test_mad_single() {
2007 let runtime = setup_runtime();
2008 let expr = runtime.compile("mad(`[42]`)").unwrap();
2009 let result = expr.search(&json!(null)).unwrap();
2010 assert_eq!(result.as_f64().unwrap(), 0.0);
2012 }
2013
2014 #[test]
2015 fn test_mad_even_length() {
2016 let runtime = setup_runtime();
2017 let expr = runtime.compile("mad(`[1, 2, 3, 4]`)").unwrap();
2018 let result = expr.search(&json!(null)).unwrap();
2019 assert_eq!(result.as_f64().unwrap(), 1.0);
2021 }
2022}