1use std::collections::HashSet;
20use std::rc::Rc;
21
22use crate::common::{
23 ArgumentType, Context, ErrorReason, Function, JmespathError, Rcvar, Runtime, Variable,
24};
25use crate::define_function;
26use crate::register_if_enabled;
27
28pub fn register(runtime: &mut Runtime) {
30 runtime.register_function("round", Box::new(RoundFn::new()));
31 runtime.register_function("floor_fn", Box::new(FloorFn::new()));
32 runtime.register_function("ceil_fn", Box::new(CeilFn::new()));
33 runtime.register_function("abs_fn", Box::new(AbsFn::new()));
34 runtime.register_function("mod_fn", Box::new(ModFn::new()));
35 runtime.register_function("pow", Box::new(PowFn::new()));
36 runtime.register_function("sqrt", Box::new(SqrtFn::new()));
37 runtime.register_function("log", Box::new(LogFn::new()));
38 runtime.register_function("clamp", Box::new(ClampFn::new()));
39 runtime.register_function("median", Box::new(MedianFn::new()));
40 runtime.register_function("percentile", Box::new(PercentileFn::new()));
41 runtime.register_function("variance", Box::new(VarianceFn::new()));
42 runtime.register_function("stddev", Box::new(StddevFn::new()));
43 runtime.register_function("sin", Box::new(SinFn::new()));
44 runtime.register_function("cos", Box::new(CosFn::new()));
45 runtime.register_function("tan", Box::new(TanFn::new()));
46 runtime.register_function("asin", Box::new(AsinFn::new()));
47 runtime.register_function("acos", Box::new(AcosFn::new()));
48 runtime.register_function("atan", Box::new(AtanFn::new()));
49 runtime.register_function("atan2", Box::new(Atan2Fn::new()));
50 runtime.register_function("deg_to_rad", Box::new(DegToRadFn::new()));
51 runtime.register_function("rad_to_deg", Box::new(RadToDegFn::new()));
52 runtime.register_function("sign", Box::new(SignFn::new()));
53 runtime.register_function("add", Box::new(AddFn::new()));
54 runtime.register_function("subtract", Box::new(SubtractFn::new()));
55 runtime.register_function("multiply", Box::new(MultiplyFn::new()));
56 runtime.register_function("divide", Box::new(DivideFn::new()));
57 runtime.register_function("mode", Box::new(ModeFn::new()));
58 runtime.register_function("to_fixed", Box::new(ToFixedFn::new()));
59 runtime.register_function("format_number", Box::new(FormatNumberFn::new()));
60 runtime.register_function("histogram", Box::new(HistogramFn::new()));
61 runtime.register_function("normalize", Box::new(NormalizeFn::new()));
62 runtime.register_function("z_score", Box::new(ZScoreFn::new()));
63 runtime.register_function("correlation", Box::new(CorrelationFn::new()));
64 runtime.register_function("quantile", Box::new(QuantileFn::new()));
65 runtime.register_function("moving_avg", Box::new(MovingAvgFn::new()));
66 runtime.register_function("ewma", Box::new(EwmaFn::new()));
67 runtime.register_function("covariance", Box::new(CovarianceFn::new()));
68 runtime.register_function("standardize", Box::new(StandardizeFn::new()));
69 runtime.register_function("quartiles", Box::new(QuartilesFn::new()));
70 runtime.register_function("outliers_iqr", Box::new(OutliersIqrFn::new()));
71 runtime.register_function("outliers_zscore", Box::new(OutliersZscoreFn::new()));
72 runtime.register_function("trend", Box::new(TrendFn::new()));
74 runtime.register_function("trend_slope", Box::new(TrendSlopeFn::new()));
75 runtime.register_function("rate_of_change", Box::new(RateOfChangeFn::new()));
76 runtime.register_function("cumulative_sum", Box::new(CumulativeSumFn::new()));
77}
78
79pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
81 register_if_enabled!(runtime, enabled, "round", Box::new(RoundFn::new()));
82 register_if_enabled!(runtime, enabled, "floor_fn", Box::new(FloorFn::new()));
83 register_if_enabled!(runtime, enabled, "ceil_fn", Box::new(CeilFn::new()));
84 register_if_enabled!(runtime, enabled, "abs_fn", Box::new(AbsFn::new()));
85 register_if_enabled!(runtime, enabled, "mod_fn", Box::new(ModFn::new()));
86 register_if_enabled!(runtime, enabled, "pow", Box::new(PowFn::new()));
87 register_if_enabled!(runtime, enabled, "sqrt", Box::new(SqrtFn::new()));
88 register_if_enabled!(runtime, enabled, "log", Box::new(LogFn::new()));
89 register_if_enabled!(runtime, enabled, "clamp", Box::new(ClampFn::new()));
90 register_if_enabled!(runtime, enabled, "median", Box::new(MedianFn::new()));
91 register_if_enabled!(
92 runtime,
93 enabled,
94 "percentile",
95 Box::new(PercentileFn::new())
96 );
97 register_if_enabled!(runtime, enabled, "variance", Box::new(VarianceFn::new()));
98 register_if_enabled!(runtime, enabled, "stddev", Box::new(StddevFn::new()));
99 register_if_enabled!(runtime, enabled, "sin", Box::new(SinFn::new()));
100 register_if_enabled!(runtime, enabled, "cos", Box::new(CosFn::new()));
101 register_if_enabled!(runtime, enabled, "tan", Box::new(TanFn::new()));
102 register_if_enabled!(runtime, enabled, "asin", Box::new(AsinFn::new()));
103 register_if_enabled!(runtime, enabled, "acos", Box::new(AcosFn::new()));
104 register_if_enabled!(runtime, enabled, "atan", Box::new(AtanFn::new()));
105 register_if_enabled!(runtime, enabled, "atan2", Box::new(Atan2Fn::new()));
106 register_if_enabled!(runtime, enabled, "deg_to_rad", Box::new(DegToRadFn::new()));
107 register_if_enabled!(runtime, enabled, "rad_to_deg", Box::new(RadToDegFn::new()));
108 register_if_enabled!(runtime, enabled, "sign", Box::new(SignFn::new()));
109 register_if_enabled!(runtime, enabled, "add", Box::new(AddFn::new()));
110 register_if_enabled!(runtime, enabled, "subtract", Box::new(SubtractFn::new()));
111 register_if_enabled!(runtime, enabled, "multiply", Box::new(MultiplyFn::new()));
112 register_if_enabled!(runtime, enabled, "divide", Box::new(DivideFn::new()));
113 register_if_enabled!(runtime, enabled, "mode", Box::new(ModeFn::new()));
114 register_if_enabled!(runtime, enabled, "to_fixed", Box::new(ToFixedFn::new()));
115 register_if_enabled!(
116 runtime,
117 enabled,
118 "format_number",
119 Box::new(FormatNumberFn::new())
120 );
121 register_if_enabled!(runtime, enabled, "histogram", Box::new(HistogramFn::new()));
122 register_if_enabled!(runtime, enabled, "normalize", Box::new(NormalizeFn::new()));
123 register_if_enabled!(runtime, enabled, "z_score", Box::new(ZScoreFn::new()));
124 register_if_enabled!(
125 runtime,
126 enabled,
127 "correlation",
128 Box::new(CorrelationFn::new())
129 );
130 register_if_enabled!(runtime, enabled, "quantile", Box::new(QuantileFn::new()));
131 register_if_enabled!(runtime, enabled, "moving_avg", Box::new(MovingAvgFn::new()));
132 register_if_enabled!(runtime, enabled, "ewma", Box::new(EwmaFn::new()));
133 register_if_enabled!(
134 runtime,
135 enabled,
136 "covariance",
137 Box::new(CovarianceFn::new())
138 );
139 register_if_enabled!(
140 runtime,
141 enabled,
142 "standardize",
143 Box::new(StandardizeFn::new())
144 );
145 register_if_enabled!(runtime, enabled, "quartiles", Box::new(QuartilesFn::new()));
146 register_if_enabled!(
147 runtime,
148 enabled,
149 "outliers_iqr",
150 Box::new(OutliersIqrFn::new())
151 );
152 register_if_enabled!(
153 runtime,
154 enabled,
155 "outliers_zscore",
156 Box::new(OutliersZscoreFn::new())
157 );
158 register_if_enabled!(runtime, enabled, "trend", Box::new(TrendFn::new()));
160 register_if_enabled!(
161 runtime,
162 enabled,
163 "trend_slope",
164 Box::new(TrendSlopeFn::new())
165 );
166 register_if_enabled!(
167 runtime,
168 enabled,
169 "rate_of_change",
170 Box::new(RateOfChangeFn::new())
171 );
172 register_if_enabled!(
173 runtime,
174 enabled,
175 "cumulative_sum",
176 Box::new(CumulativeSumFn::new())
177 );
178}
179
180define_function!(
185 RoundFn,
186 vec![ArgumentType::Number],
187 Some(ArgumentType::Number)
188);
189
190impl Function for RoundFn {
191 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
192 self.signature.validate(args, ctx)?;
193
194 let n = args[0].as_number().ok_or_else(|| {
195 JmespathError::new(
196 ctx.expression,
197 0,
198 ErrorReason::Parse("Expected number argument".to_owned()),
199 )
200 })?;
201
202 let precision = if args.len() > 1 {
203 args[1].as_number().map(|p| p as i32).unwrap_or(0)
204 } else {
205 0
206 };
207
208 let result = if precision == 0 {
209 n.round()
210 } else {
211 let multiplier = 10_f64.powi(precision);
212 (n * multiplier).round() / multiplier
213 };
214
215 Ok(Rc::new(Variable::Number(
216 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
217 )))
218 }
219}
220
221define_function!(FloorFn, vec![ArgumentType::Number], None);
226
227impl Function for FloorFn {
228 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
229 self.signature.validate(args, ctx)?;
230
231 let n = args[0].as_number().ok_or_else(|| {
232 JmespathError::new(
233 ctx.expression,
234 0,
235 ErrorReason::Parse("Expected number argument".to_owned()),
236 )
237 })?;
238
239 Ok(Rc::new(Variable::Number(serde_json::Number::from(
240 n.floor() as i64,
241 ))))
242 }
243}
244
245define_function!(CeilFn, vec![ArgumentType::Number], None);
250
251impl Function for CeilFn {
252 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
253 self.signature.validate(args, ctx)?;
254
255 let n = args[0].as_number().ok_or_else(|| {
256 JmespathError::new(
257 ctx.expression,
258 0,
259 ErrorReason::Parse("Expected number argument".to_owned()),
260 )
261 })?;
262
263 Ok(Rc::new(Variable::Number(serde_json::Number::from(
264 n.ceil() as i64,
265 ))))
266 }
267}
268
269define_function!(AbsFn, vec![ArgumentType::Number], None);
274
275impl Function for AbsFn {
276 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
277 self.signature.validate(args, ctx)?;
278
279 let n = args[0].as_number().ok_or_else(|| {
280 JmespathError::new(
281 ctx.expression,
282 0,
283 ErrorReason::Parse("Expected number argument".to_owned()),
284 )
285 })?;
286
287 Ok(Rc::new(Variable::Number(
288 serde_json::Number::from_f64(n.abs()).unwrap_or_else(|| serde_json::Number::from(0)),
289 )))
290 }
291}
292
293define_function!(
298 ModFn,
299 vec![ArgumentType::Number, ArgumentType::Number],
300 None
301);
302
303impl Function for ModFn {
304 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
305 self.signature.validate(args, ctx)?;
306
307 let n = args[0].as_number().ok_or_else(|| {
308 JmespathError::new(
309 ctx.expression,
310 0,
311 ErrorReason::Parse("Expected number argument".to_owned()),
312 )
313 })?;
314
315 let divisor = args[1].as_number().ok_or_else(|| {
316 JmespathError::new(
317 ctx.expression,
318 0,
319 ErrorReason::Parse("Expected divisor argument".to_owned()),
320 )
321 })?;
322
323 if divisor == 0.0 {
324 return Err(JmespathError::new(
325 ctx.expression,
326 0,
327 ErrorReason::Parse("Division by zero".to_owned()),
328 ));
329 }
330
331 Ok(Rc::new(Variable::Number(
332 serde_json::Number::from_f64(n % divisor)
333 .unwrap_or_else(|| serde_json::Number::from(0)),
334 )))
335 }
336}
337
338define_function!(
343 PowFn,
344 vec![ArgumentType::Number, ArgumentType::Number],
345 None
346);
347
348impl Function for PowFn {
349 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
350 self.signature.validate(args, ctx)?;
351
352 let base = args[0].as_number().ok_or_else(|| {
353 JmespathError::new(
354 ctx.expression,
355 0,
356 ErrorReason::Parse("Expected base number".to_owned()),
357 )
358 })?;
359
360 let exp = args[1].as_number().ok_or_else(|| {
361 JmespathError::new(
362 ctx.expression,
363 0,
364 ErrorReason::Parse("Expected exponent number".to_owned()),
365 )
366 })?;
367
368 Ok(Rc::new(Variable::Number(
369 serde_json::Number::from_f64(base.powf(exp))
370 .unwrap_or_else(|| serde_json::Number::from(0)),
371 )))
372 }
373}
374
375define_function!(SqrtFn, vec![ArgumentType::Number], None);
380
381impl Function for SqrtFn {
382 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
383 self.signature.validate(args, ctx)?;
384
385 let n = args[0].as_number().ok_or_else(|| {
386 JmespathError::new(
387 ctx.expression,
388 0,
389 ErrorReason::Parse("Expected number argument".to_owned()),
390 )
391 })?;
392
393 if n < 0.0 {
394 return Err(JmespathError::new(
395 ctx.expression,
396 0,
397 ErrorReason::Parse("Cannot take square root of negative number".to_owned()),
398 ));
399 }
400
401 Ok(Rc::new(Variable::Number(
402 serde_json::Number::from_f64(n.sqrt()).unwrap_or_else(|| serde_json::Number::from(0)),
403 )))
404 }
405}
406
407define_function!(
412 LogFn,
413 vec![ArgumentType::Number],
414 Some(ArgumentType::Number)
415);
416
417impl Function for LogFn {
418 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
419 self.signature.validate(args, ctx)?;
420
421 let n = args[0].as_number().ok_or_else(|| {
422 JmespathError::new(
423 ctx.expression,
424 0,
425 ErrorReason::Parse("Expected number argument".to_owned()),
426 )
427 })?;
428
429 if n <= 0.0 {
430 return Err(JmespathError::new(
431 ctx.expression,
432 0,
433 ErrorReason::Parse("Logarithm requires positive number".to_owned()),
434 ));
435 }
436
437 let result = if args.len() > 1 {
438 let base = args[1].as_number().ok_or_else(|| {
439 JmespathError::new(
440 ctx.expression,
441 0,
442 ErrorReason::Parse("Expected base number".to_owned()),
443 )
444 })?;
445 n.log(base)
446 } else {
447 n.ln()
448 };
449
450 Ok(Rc::new(Variable::Number(
451 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
452 )))
453 }
454}
455
456define_function!(
461 ClampFn,
462 vec![
463 ArgumentType::Number,
464 ArgumentType::Number,
465 ArgumentType::Number
466 ],
467 None
468);
469
470impl Function for ClampFn {
471 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
472 self.signature.validate(args, ctx)?;
473
474 let n = args[0].as_number().ok_or_else(|| {
475 JmespathError::new(
476 ctx.expression,
477 0,
478 ErrorReason::Parse("Expected number argument".to_owned()),
479 )
480 })?;
481
482 let min = args[1].as_number().ok_or_else(|| {
483 JmespathError::new(
484 ctx.expression,
485 0,
486 ErrorReason::Parse("Expected min number".to_owned()),
487 )
488 })?;
489
490 let max = args[2].as_number().ok_or_else(|| {
491 JmespathError::new(
492 ctx.expression,
493 0,
494 ErrorReason::Parse("Expected max number".to_owned()),
495 )
496 })?;
497
498 let result = n.max(min).min(max);
499
500 Ok(Rc::new(Variable::Number(
501 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
502 )))
503 }
504}
505
506define_function!(MedianFn, vec![ArgumentType::Array], None);
511
512impl Function for MedianFn {
513 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
514 self.signature.validate(args, ctx)?;
515
516 let arr = args[0].as_array().ok_or_else(|| {
517 JmespathError::new(
518 ctx.expression,
519 0,
520 ErrorReason::Parse("Expected array argument".to_owned()),
521 )
522 })?;
523
524 let mut numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
525
526 if numbers.is_empty() {
527 return Ok(Rc::new(Variable::Null));
528 }
529
530 numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
531
532 let len = numbers.len();
533 let median = if len % 2 == 0 {
534 (numbers[len / 2 - 1] + numbers[len / 2]) / 2.0
535 } else {
536 numbers[len / 2]
537 };
538
539 Ok(Rc::new(Variable::Number(
540 serde_json::Number::from_f64(median).unwrap_or_else(|| serde_json::Number::from(0)),
541 )))
542 }
543}
544
545define_function!(
550 PercentileFn,
551 vec![ArgumentType::Array, ArgumentType::Number],
552 None
553);
554
555impl Function for PercentileFn {
556 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
557 self.signature.validate(args, ctx)?;
558
559 let arr = args[0].as_array().ok_or_else(|| {
560 JmespathError::new(
561 ctx.expression,
562 0,
563 ErrorReason::Parse("Expected array argument".to_owned()),
564 )
565 })?;
566
567 let p = args[1].as_number().ok_or_else(|| {
568 JmespathError::new(
569 ctx.expression,
570 0,
571 ErrorReason::Parse("Expected percentile value".to_owned()),
572 )
573 })?;
574
575 if !(0.0..=100.0).contains(&p) {
576 return Err(JmespathError::new(
577 ctx.expression,
578 0,
579 ErrorReason::Parse("Percentile must be between 0 and 100".to_owned()),
580 ));
581 }
582
583 let mut numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
584
585 if numbers.is_empty() {
586 return Ok(Rc::new(Variable::Null));
587 }
588
589 numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
590
591 let len = numbers.len();
592 if len == 1 {
593 return Ok(Rc::new(Variable::Number(
594 serde_json::Number::from_f64(numbers[0])
595 .unwrap_or_else(|| serde_json::Number::from(0)),
596 )));
597 }
598
599 let rank = (p / 100.0) * (len - 1) as f64;
600 let lower_idx = rank.floor() as usize;
601 let upper_idx = rank.ceil() as usize;
602 let fraction = rank - lower_idx as f64;
603
604 let result = if lower_idx == upper_idx {
605 numbers[lower_idx]
606 } else {
607 numbers[lower_idx] * (1.0 - fraction) + numbers[upper_idx] * fraction
608 };
609
610 Ok(Rc::new(Variable::Number(
611 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
612 )))
613 }
614}
615
616define_function!(VarianceFn, vec![ArgumentType::Array], None);
621
622impl Function for VarianceFn {
623 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
624 self.signature.validate(args, ctx)?;
625
626 let arr = args[0].as_array().ok_or_else(|| {
627 JmespathError::new(
628 ctx.expression,
629 0,
630 ErrorReason::Parse("Expected array argument".to_owned()),
631 )
632 })?;
633
634 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
635
636 if numbers.is_empty() {
637 return Ok(Rc::new(Variable::Null));
638 }
639
640 let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
641 let variance =
642 numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / numbers.len() as f64;
643
644 Ok(Rc::new(Variable::Number(
645 serde_json::Number::from_f64(variance).unwrap_or_else(|| serde_json::Number::from(0)),
646 )))
647 }
648}
649
650define_function!(StddevFn, vec![ArgumentType::Array], None);
655
656impl Function for StddevFn {
657 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
658 self.signature.validate(args, ctx)?;
659
660 let arr = args[0].as_array().ok_or_else(|| {
661 JmespathError::new(
662 ctx.expression,
663 0,
664 ErrorReason::Parse("Expected array argument".to_owned()),
665 )
666 })?;
667
668 let numbers: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
669
670 if numbers.is_empty() {
671 return Ok(Rc::new(Variable::Null));
672 }
673
674 let mean = numbers.iter().sum::<f64>() / numbers.len() as f64;
675 let variance =
676 numbers.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / numbers.len() as f64;
677 let stddev = variance.sqrt();
678
679 Ok(Rc::new(Variable::Number(
680 serde_json::Number::from_f64(stddev).unwrap_or_else(|| serde_json::Number::from(0)),
681 )))
682 }
683}
684
685define_function!(SinFn, vec![ArgumentType::Number], None);
690
691impl Function for SinFn {
692 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
693 self.signature.validate(args, ctx)?;
694 let n = args[0].as_number().ok_or_else(|| {
695 JmespathError::new(
696 ctx.expression,
697 0,
698 ErrorReason::Parse("Expected number".to_owned()),
699 )
700 })?;
701 Ok(Rc::new(Variable::Number(
702 serde_json::Number::from_f64(n.sin()).unwrap_or_else(|| serde_json::Number::from(0)),
703 )))
704 }
705}
706
707define_function!(CosFn, vec![ArgumentType::Number], None);
708
709impl Function for CosFn {
710 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
711 self.signature.validate(args, ctx)?;
712 let n = args[0].as_number().ok_or_else(|| {
713 JmespathError::new(
714 ctx.expression,
715 0,
716 ErrorReason::Parse("Expected number".to_owned()),
717 )
718 })?;
719 Ok(Rc::new(Variable::Number(
720 serde_json::Number::from_f64(n.cos()).unwrap_or_else(|| serde_json::Number::from(0)),
721 )))
722 }
723}
724
725define_function!(TanFn, vec![ArgumentType::Number], None);
726
727impl Function for TanFn {
728 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
729 self.signature.validate(args, ctx)?;
730 let n = args[0].as_number().ok_or_else(|| {
731 JmespathError::new(
732 ctx.expression,
733 0,
734 ErrorReason::Parse("Expected number".to_owned()),
735 )
736 })?;
737 Ok(Rc::new(Variable::Number(
738 serde_json::Number::from_f64(n.tan()).unwrap_or_else(|| serde_json::Number::from(0)),
739 )))
740 }
741}
742
743define_function!(AsinFn, vec![ArgumentType::Number], None);
744
745impl Function for AsinFn {
746 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
747 self.signature.validate(args, ctx)?;
748 let n = args[0].as_number().ok_or_else(|| {
749 JmespathError::new(
750 ctx.expression,
751 0,
752 ErrorReason::Parse("Expected number".to_owned()),
753 )
754 })?;
755 let result = n.asin();
756 if result.is_nan() {
758 Ok(Rc::new(Variable::Null))
759 } else {
760 Ok(Rc::new(Variable::Number(
761 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
762 )))
763 }
764 }
765}
766
767define_function!(AcosFn, vec![ArgumentType::Number], None);
768
769impl Function for AcosFn {
770 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
771 self.signature.validate(args, ctx)?;
772 let n = args[0].as_number().ok_or_else(|| {
773 JmespathError::new(
774 ctx.expression,
775 0,
776 ErrorReason::Parse("Expected number".to_owned()),
777 )
778 })?;
779 let result = n.acos();
780 if result.is_nan() {
782 Ok(Rc::new(Variable::Null))
783 } else {
784 Ok(Rc::new(Variable::Number(
785 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
786 )))
787 }
788 }
789}
790
791define_function!(AtanFn, vec![ArgumentType::Number], None);
792
793impl Function for AtanFn {
794 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
795 self.signature.validate(args, ctx)?;
796 let n = args[0].as_number().ok_or_else(|| {
797 JmespathError::new(
798 ctx.expression,
799 0,
800 ErrorReason::Parse("Expected number".to_owned()),
801 )
802 })?;
803 Ok(Rc::new(Variable::Number(
804 serde_json::Number::from_f64(n.atan()).unwrap_or_else(|| serde_json::Number::from(0)),
805 )))
806 }
807}
808
809define_function!(
810 Atan2Fn,
811 vec![ArgumentType::Number, ArgumentType::Number],
812 None
813);
814
815impl Function for Atan2Fn {
816 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
817 self.signature.validate(args, ctx)?;
818 let y = args[0].as_number().ok_or_else(|| {
819 JmespathError::new(
820 ctx.expression,
821 0,
822 ErrorReason::Parse("Expected number".to_owned()),
823 )
824 })?;
825 let x = args[1].as_number().ok_or_else(|| {
826 JmespathError::new(
827 ctx.expression,
828 0,
829 ErrorReason::Parse("Expected number".to_owned()),
830 )
831 })?;
832 Ok(Rc::new(Variable::Number(
833 serde_json::Number::from_f64(y.atan2(x)).unwrap_or_else(|| serde_json::Number::from(0)),
834 )))
835 }
836}
837
838define_function!(DegToRadFn, vec![ArgumentType::Number], None);
839
840impl Function for DegToRadFn {
841 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
842 self.signature.validate(args, ctx)?;
843 let n = args[0].as_number().ok_or_else(|| {
844 JmespathError::new(
845 ctx.expression,
846 0,
847 ErrorReason::Parse("Expected number".to_owned()),
848 )
849 })?;
850 Ok(Rc::new(Variable::Number(
851 serde_json::Number::from_f64(n.to_radians())
852 .unwrap_or_else(|| serde_json::Number::from(0)),
853 )))
854 }
855}
856
857define_function!(RadToDegFn, vec![ArgumentType::Number], None);
858
859impl Function for RadToDegFn {
860 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
861 self.signature.validate(args, ctx)?;
862 let n = args[0].as_number().ok_or_else(|| {
863 JmespathError::new(
864 ctx.expression,
865 0,
866 ErrorReason::Parse("Expected number".to_owned()),
867 )
868 })?;
869 Ok(Rc::new(Variable::Number(
870 serde_json::Number::from_f64(n.to_degrees())
871 .unwrap_or_else(|| serde_json::Number::from(0)),
872 )))
873 }
874}
875
876define_function!(SignFn, vec![ArgumentType::Number], None);
877
878impl Function for SignFn {
879 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
880 self.signature.validate(args, ctx)?;
881 let n = args[0].as_number().ok_or_else(|| {
882 JmespathError::new(
883 ctx.expression,
884 0,
885 ErrorReason::Parse("Expected number".to_owned()),
886 )
887 })?;
888 let sign = if n > 0.0 {
889 1
890 } else if n < 0.0 {
891 -1
892 } else {
893 0
894 };
895 Ok(Rc::new(Variable::Number(serde_json::Number::from(sign))))
896 }
897}
898
899define_function!(
904 AddFn,
905 vec![ArgumentType::Number, ArgumentType::Number],
906 None
907);
908
909impl Function for AddFn {
910 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
911 self.signature.validate(args, ctx)?;
912 let a = args[0].as_number().ok_or_else(|| {
913 JmespathError::new(
914 ctx.expression,
915 0,
916 ErrorReason::Parse("Expected number".to_owned()),
917 )
918 })?;
919 let b = args[1].as_number().ok_or_else(|| {
920 JmespathError::new(
921 ctx.expression,
922 0,
923 ErrorReason::Parse("Expected number".to_owned()),
924 )
925 })?;
926 Ok(Rc::new(Variable::Number(
927 serde_json::Number::from_f64(a + b).unwrap_or_else(|| serde_json::Number::from(0)),
928 )))
929 }
930}
931
932define_function!(
937 SubtractFn,
938 vec![ArgumentType::Number, ArgumentType::Number],
939 None
940);
941
942impl Function for SubtractFn {
943 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
944 self.signature.validate(args, ctx)?;
945 let a = args[0].as_number().ok_or_else(|| {
946 JmespathError::new(
947 ctx.expression,
948 0,
949 ErrorReason::Parse("Expected number".to_owned()),
950 )
951 })?;
952 let b = args[1].as_number().ok_or_else(|| {
953 JmespathError::new(
954 ctx.expression,
955 0,
956 ErrorReason::Parse("Expected number".to_owned()),
957 )
958 })?;
959 Ok(Rc::new(Variable::Number(
960 serde_json::Number::from_f64(a - b).unwrap_or_else(|| serde_json::Number::from(0)),
961 )))
962 }
963}
964
965define_function!(
970 MultiplyFn,
971 vec![ArgumentType::Number, ArgumentType::Number],
972 None
973);
974
975impl Function for MultiplyFn {
976 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
977 self.signature.validate(args, ctx)?;
978 let a = args[0].as_number().ok_or_else(|| {
979 JmespathError::new(
980 ctx.expression,
981 0,
982 ErrorReason::Parse("Expected number".to_owned()),
983 )
984 })?;
985 let b = args[1].as_number().ok_or_else(|| {
986 JmespathError::new(
987 ctx.expression,
988 0,
989 ErrorReason::Parse("Expected number".to_owned()),
990 )
991 })?;
992 Ok(Rc::new(Variable::Number(
993 serde_json::Number::from_f64(a * b).unwrap_or_else(|| serde_json::Number::from(0)),
994 )))
995 }
996}
997
998define_function!(
1003 DivideFn,
1004 vec![ArgumentType::Number, ArgumentType::Number],
1005 None
1006);
1007
1008impl Function for DivideFn {
1009 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1010 self.signature.validate(args, ctx)?;
1011 let a = args[0].as_number().ok_or_else(|| {
1012 JmespathError::new(
1013 ctx.expression,
1014 0,
1015 ErrorReason::Parse("Expected number".to_owned()),
1016 )
1017 })?;
1018 let b = args[1].as_number().ok_or_else(|| {
1019 JmespathError::new(
1020 ctx.expression,
1021 0,
1022 ErrorReason::Parse("Expected number".to_owned()),
1023 )
1024 })?;
1025 if b == 0.0 {
1026 return Err(JmespathError::new(
1027 ctx.expression,
1028 0,
1029 ErrorReason::Parse("Division by zero".to_owned()),
1030 ));
1031 }
1032 Ok(Rc::new(Variable::Number(
1033 serde_json::Number::from_f64(a / b).unwrap_or_else(|| serde_json::Number::from(0)),
1034 )))
1035 }
1036}
1037
1038define_function!(ModeFn, vec![ArgumentType::Array], None);
1043
1044impl Function for ModeFn {
1045 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1046 self.signature.validate(args, ctx)?;
1047
1048 let arr = args[0].as_array().ok_or_else(|| {
1049 JmespathError::new(
1050 ctx.expression,
1051 0,
1052 ErrorReason::Parse("Expected array argument".to_owned()),
1053 )
1054 })?;
1055
1056 if arr.is_empty() {
1057 return Ok(Rc::new(Variable::Null));
1058 }
1059
1060 let mut counts: std::collections::HashMap<String, (usize, Rcvar)> =
1062 std::collections::HashMap::new();
1063
1064 for item in arr.iter() {
1065 let key = serde_json::to_string(&**item).unwrap_or_default();
1066 counts
1067 .entry(key)
1068 .and_modify(|(count, _)| *count += 1)
1069 .or_insert((1, item.clone()));
1070 }
1071
1072 let (_, (_, mode_value)) = counts
1074 .into_iter()
1075 .max_by_key(|(_, (count, _))| *count)
1076 .unwrap();
1077
1078 Ok(mode_value)
1079 }
1080}
1081
1082define_function!(
1088 ToFixedFn,
1089 vec![ArgumentType::Number, ArgumentType::Number],
1090 None
1091);
1092
1093impl Function for ToFixedFn {
1094 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1095 self.signature.validate(args, ctx)?;
1096
1097 let num = args[0].as_number().ok_or_else(|| {
1098 JmespathError::new(
1099 ctx.expression,
1100 0,
1101 ErrorReason::Parse("Expected number argument".to_owned()),
1102 )
1103 })?;
1104
1105 let precision = args[1].as_number().ok_or_else(|| {
1106 JmespathError::new(
1107 ctx.expression,
1108 0,
1109 ErrorReason::Parse("Expected precision argument".to_owned()),
1110 )
1111 })? as usize;
1112
1113 let result = format!("{:.prec$}", num, prec = precision);
1114 Ok(Rc::new(Variable::String(result)))
1115 }
1116}
1117
1118define_function!(
1124 FormatNumberFn,
1125 vec![ArgumentType::Number],
1126 Some(ArgumentType::Any)
1127);
1128
1129impl Function for FormatNumberFn {
1130 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1131 self.signature.validate(args, ctx)?;
1132
1133 let num = args[0].as_number().ok_or_else(|| {
1134 JmespathError::new(
1135 ctx.expression,
1136 0,
1137 ErrorReason::Parse("Expected number argument".to_owned()),
1138 )
1139 })?;
1140
1141 let precision = args
1142 .get(1)
1143 .and_then(|v| v.as_number())
1144 .map(|n| n as usize)
1145 .unwrap_or(0);
1146
1147 let suffix = args
1148 .get(2)
1149 .and_then(|v| v.as_string())
1150 .map(|s| s.to_string());
1151
1152 let (scaled_num, auto_suffix) = if let Some(ref s) = suffix {
1154 match s.as_str() {
1155 "k" | "K" => (num / 1_000.0, "k"),
1156 "M" => (num / 1_000_000.0, "M"),
1157 "B" => (num / 1_000_000_000.0, "B"),
1158 "T" => (num / 1_000_000_000_000.0, "T"),
1159 "auto" => {
1160 let abs_num = num.abs();
1162 if abs_num >= 1_000_000_000_000.0 {
1163 (num / 1_000_000_000_000.0, "T")
1164 } else if abs_num >= 1_000_000_000.0 {
1165 (num / 1_000_000_000.0, "B")
1166 } else if abs_num >= 1_000_000.0 {
1167 (num / 1_000_000.0, "M")
1168 } else if abs_num >= 1_000.0 {
1169 (num / 1_000.0, "k")
1170 } else {
1171 (num, "")
1172 }
1173 }
1174 _ => (num, s.as_str()),
1175 }
1176 } else {
1177 (num, "")
1178 };
1179
1180 let formatted = format!("{:.prec$}", scaled_num, prec = precision);
1182
1183 let result = if suffix.is_none() || suffix.as_deref() == Some("") {
1185 add_thousand_separators(&formatted)
1186 } else {
1187 format!("{}{}", formatted, auto_suffix)
1188 };
1189
1190 Ok(Rc::new(Variable::String(result)))
1191 }
1192}
1193
1194fn add_thousand_separators(s: &str) -> String {
1196 let parts: Vec<&str> = s.split('.').collect();
1197 let int_part = parts[0];
1198 let dec_part = parts.get(1);
1199
1200 let (sign, digits) = if let Some(stripped) = int_part.strip_prefix('-') {
1202 ("-", stripped)
1203 } else {
1204 ("", int_part)
1205 };
1206
1207 let digit_chars: Vec<char> = digits.chars().collect();
1209 let len = digit_chars.len();
1210 let with_commas: String = digit_chars
1211 .iter()
1212 .enumerate()
1213 .map(|(i, c)| {
1214 let pos_from_right = len - 1 - i;
1215 if pos_from_right > 0 && pos_from_right % 3 == 0 {
1216 format!("{},", c)
1217 } else {
1218 c.to_string()
1219 }
1220 })
1221 .collect();
1222
1223 match dec_part {
1224 Some(dec) => format!("{}{}.{}", sign, with_commas, dec),
1225 None => format!("{}{}", sign, with_commas),
1226 }
1227}
1228
1229define_function!(
1235 HistogramFn,
1236 vec![ArgumentType::Array, ArgumentType::Number],
1237 None
1238);
1239
1240impl Function for HistogramFn {
1241 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1242 self.signature.validate(args, ctx)?;
1243
1244 let arr = args[0].as_array().ok_or_else(|| {
1245 JmespathError::new(
1246 ctx.expression,
1247 0,
1248 ErrorReason::Parse("Expected array argument".to_owned()),
1249 )
1250 })?;
1251
1252 let num_bins = args[1].as_number().ok_or_else(|| {
1253 JmespathError::new(
1254 ctx.expression,
1255 0,
1256 ErrorReason::Parse("Expected number of bins".to_owned()),
1257 )
1258 })? as usize;
1259
1260 if num_bins == 0 {
1261 return Err(JmespathError::new(
1262 ctx.expression,
1263 0,
1264 ErrorReason::Parse("Number of bins must be greater than 0".to_owned()),
1265 ));
1266 }
1267
1268 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1270
1271 if values.is_empty() {
1272 return Ok(Rc::new(Variable::Array(vec![])));
1273 }
1274
1275 let min_val = values.iter().cloned().fold(f64::INFINITY, f64::min);
1276 let max_val = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
1277
1278 let bin_width = if (max_val - min_val).abs() < f64::EPSILON {
1280 1.0
1281 } else {
1282 (max_val - min_val) / num_bins as f64
1283 };
1284
1285 let mut bins: Vec<(f64, f64, usize)> = (0..num_bins)
1287 .map(|i| {
1288 let bin_min = min_val + (i as f64 * bin_width);
1289 let bin_max = if i == num_bins - 1 {
1290 max_val
1291 } else {
1292 min_val + ((i + 1) as f64 * bin_width)
1293 };
1294 (bin_min, bin_max, 0)
1295 })
1296 .collect();
1297
1298 for val in &values {
1300 let bin_idx = if (max_val - min_val).abs() < f64::EPSILON {
1301 0
1302 } else {
1303 let idx = ((val - min_val) / bin_width) as usize;
1304 idx.min(num_bins - 1)
1305 };
1306 bins[bin_idx].2 += 1;
1307 }
1308
1309 let result: Vec<Rcvar> = bins
1311 .into_iter()
1312 .map(|(bin_min, bin_max, count)| {
1313 let mut map = std::collections::BTreeMap::new();
1314 map.insert(
1315 "min".to_string(),
1316 Rc::new(Variable::Number(
1317 serde_json::Number::from_f64(bin_min)
1318 .unwrap_or_else(|| serde_json::Number::from(0)),
1319 )) as Rcvar,
1320 );
1321 map.insert(
1322 "max".to_string(),
1323 Rc::new(Variable::Number(
1324 serde_json::Number::from_f64(bin_max)
1325 .unwrap_or_else(|| serde_json::Number::from(0)),
1326 )) as Rcvar,
1327 );
1328 map.insert(
1329 "count".to_string(),
1330 Rc::new(Variable::Number(serde_json::Number::from(count))) as Rcvar,
1331 );
1332 Rc::new(Variable::Object(map)) as Rcvar
1333 })
1334 .collect();
1335
1336 Ok(Rc::new(Variable::Array(result)))
1337 }
1338}
1339
1340define_function!(NormalizeFn, vec![ArgumentType::Array], None);
1346
1347impl Function for NormalizeFn {
1348 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1349 self.signature.validate(args, ctx)?;
1350
1351 let arr = args[0].as_array().ok_or_else(|| {
1352 JmespathError::new(
1353 ctx.expression,
1354 0,
1355 ErrorReason::Parse("Expected array argument".to_owned()),
1356 )
1357 })?;
1358
1359 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1361
1362 if values.is_empty() {
1363 return Ok(Rc::new(Variable::Array(vec![])));
1364 }
1365
1366 let min_val = values.iter().cloned().fold(f64::INFINITY, f64::min);
1367 let max_val = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
1368 let range = max_val - min_val;
1369
1370 let result: Vec<Rcvar> = values
1371 .iter()
1372 .map(|v| {
1373 let normalized = if range.abs() < f64::EPSILON {
1374 0.0 } else {
1376 (v - min_val) / range
1377 };
1378 Rc::new(Variable::Number(
1379 serde_json::Number::from_f64(normalized)
1380 .unwrap_or_else(|| serde_json::Number::from(0)),
1381 )) as Rcvar
1382 })
1383 .collect();
1384
1385 Ok(Rc::new(Variable::Array(result)))
1386 }
1387}
1388
1389define_function!(ZScoreFn, vec![ArgumentType::Array], None);
1395
1396impl Function for ZScoreFn {
1397 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1398 self.signature.validate(args, ctx)?;
1399
1400 let arr = args[0].as_array().ok_or_else(|| {
1401 JmespathError::new(
1402 ctx.expression,
1403 0,
1404 ErrorReason::Parse("Expected array argument".to_owned()),
1405 )
1406 })?;
1407
1408 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1410
1411 if values.is_empty() {
1412 return Ok(Rc::new(Variable::Array(vec![])));
1413 }
1414
1415 let n = values.len() as f64;
1416 let mean = values.iter().sum::<f64>() / n;
1417 let variance = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / n;
1418 let stddev = variance.sqrt();
1419
1420 let result: Vec<Rcvar> = values
1421 .iter()
1422 .map(|v| {
1423 let z = if stddev.abs() < f64::EPSILON {
1424 0.0 } else {
1426 (v - mean) / stddev
1427 };
1428 Rc::new(Variable::Number(
1429 serde_json::Number::from_f64(z).unwrap_or_else(|| serde_json::Number::from(0)),
1430 )) as Rcvar
1431 })
1432 .collect();
1433
1434 Ok(Rc::new(Variable::Array(result)))
1435 }
1436}
1437
1438define_function!(
1444 CorrelationFn,
1445 vec![ArgumentType::Array, ArgumentType::Array],
1446 None
1447);
1448
1449impl Function for CorrelationFn {
1450 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1451 self.signature.validate(args, ctx)?;
1452
1453 let arr1 = args[0].as_array().ok_or_else(|| {
1454 JmespathError::new(
1455 ctx.expression,
1456 0,
1457 ErrorReason::Parse("Expected array argument".to_owned()),
1458 )
1459 })?;
1460
1461 let arr2 = args[1].as_array().ok_or_else(|| {
1462 JmespathError::new(
1463 ctx.expression,
1464 0,
1465 ErrorReason::Parse("Expected array argument".to_owned()),
1466 )
1467 })?;
1468
1469 let values1: Vec<f64> = arr1.iter().filter_map(|v| v.as_number()).collect();
1471 let values2: Vec<f64> = arr2.iter().filter_map(|v| v.as_number()).collect();
1472
1473 if values1.is_empty() || values2.is_empty() {
1474 return Ok(Rc::new(Variable::Null));
1475 }
1476
1477 let n = values1.len().min(values2.len());
1479 if n == 0 {
1480 return Ok(Rc::new(Variable::Null));
1481 }
1482
1483 let values1 = &values1[..n];
1484 let values2 = &values2[..n];
1485
1486 let mean1 = values1.iter().sum::<f64>() / n as f64;
1487 let mean2 = values2.iter().sum::<f64>() / n as f64;
1488
1489 let mut cov = 0.0;
1490 let mut var1 = 0.0;
1491 let mut var2 = 0.0;
1492
1493 for i in 0..n {
1494 let d1 = values1[i] - mean1;
1495 let d2 = values2[i] - mean2;
1496 cov += d1 * d2;
1497 var1 += d1 * d1;
1498 var2 += d2 * d2;
1499 }
1500
1501 let denom = (var1 * var2).sqrt();
1502 let correlation = if denom.abs() < f64::EPSILON {
1503 0.0 } else {
1505 cov / denom
1506 };
1507
1508 Ok(Rc::new(Variable::Number(
1509 serde_json::Number::from_f64(correlation)
1510 .unwrap_or_else(|| serde_json::Number::from(0)),
1511 )))
1512 }
1513}
1514
1515define_function!(
1521 QuantileFn,
1522 vec![ArgumentType::Array, ArgumentType::Number],
1523 None
1524);
1525
1526impl Function for QuantileFn {
1527 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1528 self.signature.validate(args, ctx)?;
1529
1530 let arr = args[0].as_array().ok_or_else(|| {
1531 JmespathError::new(
1532 ctx.expression,
1533 0,
1534 ErrorReason::Parse("Expected array argument".to_owned()),
1535 )
1536 })?;
1537
1538 let q = args[1].as_number().ok_or_else(|| {
1539 JmespathError::new(
1540 ctx.expression,
1541 0,
1542 ErrorReason::Parse("Expected number for quantile".to_owned()),
1543 )
1544 })?;
1545
1546 if !(0.0..=1.0).contains(&q) {
1547 return Ok(Rc::new(Variable::Null));
1548 }
1549
1550 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1551
1552 if values.is_empty() {
1553 return Ok(Rc::new(Variable::Null));
1554 }
1555
1556 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1557
1558 let n = values.len();
1559 let pos = q * (n - 1) as f64;
1560 let lower = pos.floor() as usize;
1561 let upper = pos.ceil() as usize;
1562 let frac = pos - lower as f64;
1563
1564 let result = if lower == upper {
1565 values[lower]
1566 } else {
1567 values[lower] * (1.0 - frac) + values[upper] * frac
1568 };
1569
1570 Ok(Rc::new(Variable::Number(
1571 serde_json::Number::from_f64(result).unwrap_or_else(|| serde_json::Number::from(0)),
1572 )))
1573 }
1574}
1575
1576define_function!(
1582 MovingAvgFn,
1583 vec![ArgumentType::Array, ArgumentType::Number],
1584 None
1585);
1586
1587impl Function for MovingAvgFn {
1588 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1589 self.signature.validate(args, ctx)?;
1590
1591 let arr = args[0].as_array().ok_or_else(|| {
1592 JmespathError::new(
1593 ctx.expression,
1594 0,
1595 ErrorReason::Parse("Expected array argument".to_owned()),
1596 )
1597 })?;
1598
1599 let window = args[1].as_number().ok_or_else(|| {
1600 JmespathError::new(
1601 ctx.expression,
1602 0,
1603 ErrorReason::Parse("Expected number for window size".to_owned()),
1604 )
1605 })? as usize;
1606
1607 if window == 0 {
1608 return Ok(Rc::new(Variable::Null));
1609 }
1610
1611 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1612
1613 if values.is_empty() || window > values.len() {
1614 return Ok(Rc::new(Variable::Array(vec![])));
1615 }
1616
1617 let mut result: Vec<Rcvar> = Vec::new();
1618
1619 for i in 0..values.len() {
1621 if i + 1 < window {
1622 result.push(Rc::new(Variable::Null));
1624 } else {
1625 let start = i + 1 - window;
1626 let sum: f64 = values[start..=i].iter().sum();
1627 let avg = sum / window as f64;
1628 result.push(Rc::new(Variable::Number(
1629 serde_json::Number::from_f64(avg)
1630 .unwrap_or_else(|| serde_json::Number::from(0)),
1631 )));
1632 }
1633 }
1634
1635 Ok(Rc::new(Variable::Array(result)))
1636 }
1637}
1638
1639define_function!(
1645 EwmaFn,
1646 vec![ArgumentType::Array, ArgumentType::Number],
1647 None
1648);
1649
1650impl Function for EwmaFn {
1651 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1652 self.signature.validate(args, ctx)?;
1653
1654 let arr = args[0].as_array().ok_or_else(|| {
1655 JmespathError::new(
1656 ctx.expression,
1657 0,
1658 ErrorReason::Parse("Expected array argument".to_owned()),
1659 )
1660 })?;
1661
1662 let alpha = args[1].as_number().ok_or_else(|| {
1663 JmespathError::new(
1664 ctx.expression,
1665 0,
1666 ErrorReason::Parse("Expected number for alpha".to_owned()),
1667 )
1668 })?;
1669
1670 if !(0.0..=1.0).contains(&alpha) {
1671 return Ok(Rc::new(Variable::Null));
1672 }
1673
1674 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1675
1676 if values.is_empty() {
1677 return Ok(Rc::new(Variable::Array(vec![])));
1678 }
1679
1680 let mut result: Vec<Rcvar> = Vec::new();
1681 let mut ewma = values[0];
1682
1683 for value in &values {
1684 ewma = alpha * value + (1.0 - alpha) * ewma;
1685 result.push(Rc::new(Variable::Number(
1686 serde_json::Number::from_f64(ewma).unwrap_or_else(|| serde_json::Number::from(0)),
1687 )));
1688 }
1689
1690 Ok(Rc::new(Variable::Array(result)))
1691 }
1692}
1693
1694define_function!(
1700 CovarianceFn,
1701 vec![ArgumentType::Array, ArgumentType::Array],
1702 None
1703);
1704
1705impl Function for CovarianceFn {
1706 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1707 self.signature.validate(args, ctx)?;
1708
1709 let arr1 = args[0].as_array().ok_or_else(|| {
1710 JmespathError::new(
1711 ctx.expression,
1712 0,
1713 ErrorReason::Parse("Expected array argument".to_owned()),
1714 )
1715 })?;
1716
1717 let arr2 = args[1].as_array().ok_or_else(|| {
1718 JmespathError::new(
1719 ctx.expression,
1720 0,
1721 ErrorReason::Parse("Expected array argument".to_owned()),
1722 )
1723 })?;
1724
1725 let values1: Vec<f64> = arr1.iter().filter_map(|v| v.as_number()).collect();
1726 let values2: Vec<f64> = arr2.iter().filter_map(|v| v.as_number()).collect();
1727
1728 if values1.is_empty() || values1.len() != values2.len() {
1729 return Ok(Rc::new(Variable::Null));
1730 }
1731
1732 let n = values1.len() as f64;
1733 let mean1: f64 = values1.iter().sum::<f64>() / n;
1734 let mean2: f64 = values2.iter().sum::<f64>() / n;
1735
1736 let cov: f64 = values1
1737 .iter()
1738 .zip(values2.iter())
1739 .map(|(x, y)| (x - mean1) * (y - mean2))
1740 .sum::<f64>()
1741 / n;
1742
1743 Ok(Rc::new(Variable::Number(
1744 serde_json::Number::from_f64(cov).unwrap_or_else(|| serde_json::Number::from(0)),
1745 )))
1746 }
1747}
1748
1749define_function!(StandardizeFn, vec![ArgumentType::Array], None);
1755
1756impl Function for StandardizeFn {
1757 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1758 self.signature.validate(args, ctx)?;
1759
1760 let arr = args[0].as_array().ok_or_else(|| {
1761 JmespathError::new(
1762 ctx.expression,
1763 0,
1764 ErrorReason::Parse("Expected array argument".to_owned()),
1765 )
1766 })?;
1767
1768 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1769
1770 if values.is_empty() {
1771 return Ok(Rc::new(Variable::Array(vec![])));
1772 }
1773
1774 let n = values.len() as f64;
1775 let mean: f64 = values.iter().sum::<f64>() / n;
1776 let variance: f64 = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n;
1777 let std_dev = variance.sqrt();
1778
1779 let result: Vec<Rcvar> = values
1780 .iter()
1781 .map(|x| {
1782 let standardized = if std_dev.abs() < f64::EPSILON {
1783 0.0
1784 } else {
1785 (x - mean) / std_dev
1786 };
1787 Rc::new(Variable::Number(
1788 serde_json::Number::from_f64(standardized)
1789 .unwrap_or_else(|| serde_json::Number::from(0)),
1790 ))
1791 })
1792 .collect();
1793
1794 Ok(Rc::new(Variable::Array(result)))
1795 }
1796}
1797
1798define_function!(QuartilesFn, vec![ArgumentType::Array], None);
1803
1804impl Function for QuartilesFn {
1805 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1806 self.signature.validate(args, ctx)?;
1807
1808 let arr = args[0].as_array().ok_or_else(|| {
1809 JmespathError::new(
1810 ctx.expression,
1811 0,
1812 ErrorReason::Parse("Expected array argument".to_owned()),
1813 )
1814 })?;
1815
1816 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1817
1818 if values.is_empty() {
1819 return Ok(Rc::new(Variable::Null));
1820 }
1821
1822 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1823
1824 let n = values.len();
1825 let min = values[0];
1826 let max = values[n - 1];
1827
1828 let q1 = percentile_value(&values, 25.0);
1829 let q2 = percentile_value(&values, 50.0);
1830 let q3 = percentile_value(&values, 75.0);
1831 let iqr = q3 - q1;
1832
1833 let mut result = std::collections::BTreeMap::new();
1834 result.insert(
1835 "min".to_string(),
1836 Rc::new(Variable::Number(
1837 serde_json::Number::from_f64(min).unwrap_or_else(|| serde_json::Number::from(0)),
1838 )) as Rcvar,
1839 );
1840 result.insert(
1841 "q1".to_string(),
1842 Rc::new(Variable::Number(
1843 serde_json::Number::from_f64(q1).unwrap_or_else(|| serde_json::Number::from(0)),
1844 )) as Rcvar,
1845 );
1846 result.insert(
1847 "q2".to_string(),
1848 Rc::new(Variable::Number(
1849 serde_json::Number::from_f64(q2).unwrap_or_else(|| serde_json::Number::from(0)),
1850 )) as Rcvar,
1851 );
1852 result.insert(
1853 "q3".to_string(),
1854 Rc::new(Variable::Number(
1855 serde_json::Number::from_f64(q3).unwrap_or_else(|| serde_json::Number::from(0)),
1856 )) as Rcvar,
1857 );
1858 result.insert(
1859 "max".to_string(),
1860 Rc::new(Variable::Number(
1861 serde_json::Number::from_f64(max).unwrap_or_else(|| serde_json::Number::from(0)),
1862 )) as Rcvar,
1863 );
1864 result.insert(
1865 "iqr".to_string(),
1866 Rc::new(Variable::Number(
1867 serde_json::Number::from_f64(iqr).unwrap_or_else(|| serde_json::Number::from(0)),
1868 )) as Rcvar,
1869 );
1870
1871 Ok(Rc::new(Variable::Object(result)))
1872 }
1873}
1874
1875fn percentile_value(sorted_values: &[f64], p: f64) -> f64 {
1876 let n = sorted_values.len();
1877 if n == 1 {
1878 return sorted_values[0];
1879 }
1880
1881 let k = (p / 100.0) * (n - 1) as f64;
1882 let f = k.floor() as usize;
1883 let c = k.ceil() as usize;
1884
1885 if f == c {
1886 sorted_values[f]
1887 } else {
1888 let d = k - f as f64;
1889 sorted_values[f] * (1.0 - d) + sorted_values[c] * d
1890 }
1891}
1892
1893define_function!(
1898 OutliersIqrFn,
1899 vec![ArgumentType::Array],
1900 Some(ArgumentType::Number)
1901);
1902
1903impl Function for OutliersIqrFn {
1904 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1905 self.signature.validate(args, ctx)?;
1906
1907 let arr = args[0].as_array().ok_or_else(|| {
1908 JmespathError::new(
1909 ctx.expression,
1910 0,
1911 ErrorReason::Parse("Expected array argument".to_owned()),
1912 )
1913 })?;
1914
1915 let multiplier = if args.len() > 1 {
1916 args[1].as_number().unwrap_or(1.5)
1917 } else {
1918 1.5
1919 };
1920
1921 let mut values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1922
1923 if values.is_empty() {
1924 return Ok(Rc::new(Variable::Array(vec![])));
1925 }
1926
1927 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1928
1929 let q1 = percentile_value(&values, 25.0);
1930 let q3 = percentile_value(&values, 75.0);
1931 let iqr = q3 - q1;
1932
1933 let lower_bound = q1 - multiplier * iqr;
1934 let upper_bound = q3 + multiplier * iqr;
1935
1936 let outliers: Vec<Rcvar> = arr
1937 .iter()
1938 .filter(|v| {
1939 if let Some(n) = v.as_number() {
1940 n < lower_bound || n > upper_bound
1941 } else {
1942 false
1943 }
1944 })
1945 .cloned()
1946 .collect();
1947
1948 Ok(Rc::new(Variable::Array(outliers)))
1949 }
1950}
1951
1952define_function!(
1957 OutliersZscoreFn,
1958 vec![ArgumentType::Array],
1959 Some(ArgumentType::Number)
1960);
1961
1962impl Function for OutliersZscoreFn {
1963 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
1964 self.signature.validate(args, ctx)?;
1965
1966 let arr = args[0].as_array().ok_or_else(|| {
1967 JmespathError::new(
1968 ctx.expression,
1969 0,
1970 ErrorReason::Parse("Expected array argument".to_owned()),
1971 )
1972 })?;
1973
1974 let threshold = if args.len() > 1 {
1975 args[1].as_number().unwrap_or(2.0)
1976 } else {
1977 2.0
1978 };
1979
1980 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
1981
1982 if values.is_empty() {
1983 return Ok(Rc::new(Variable::Array(vec![])));
1984 }
1985
1986 let n = values.len() as f64;
1987 let mean: f64 = values.iter().sum::<f64>() / n;
1988 let variance: f64 = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / n;
1989 let stddev = variance.sqrt();
1990
1991 if stddev.abs() < f64::EPSILON {
1992 return Ok(Rc::new(Variable::Array(vec![])));
1993 }
1994
1995 let outliers: Vec<Rcvar> = arr
1996 .iter()
1997 .filter(|v| {
1998 if let Some(n) = v.as_number() {
1999 let z = (n - mean) / stddev;
2000 z.abs() > threshold
2001 } else {
2002 false
2003 }
2004 })
2005 .cloned()
2006 .collect();
2007
2008 Ok(Rc::new(Variable::Array(outliers)))
2009 }
2010}
2011
2012define_function!(TrendFn, vec![ArgumentType::Array], None);
2017
2018impl Function for TrendFn {
2019 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
2020 self.signature.validate(args, ctx)?;
2021
2022 let arr = args[0].as_array().ok_or_else(|| {
2023 JmespathError::new(
2024 ctx.expression,
2025 0,
2026 ErrorReason::Parse("Expected array of numbers".to_owned()),
2027 )
2028 })?;
2029
2030 if arr.len() < 2 {
2031 return Ok(Rc::new(Variable::String("stable".to_string())));
2032 }
2033
2034 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
2035
2036 if values.len() < 2 {
2037 return Ok(Rc::new(Variable::String("stable".to_string())));
2038 }
2039
2040 let n = values.len() as f64;
2042 let sum_x: f64 = (0..values.len()).map(|i| i as f64).sum();
2043 let sum_y: f64 = values.iter().sum();
2044 let sum_xy: f64 = values.iter().enumerate().map(|(i, y)| i as f64 * y).sum();
2045 let sum_x2: f64 = (0..values.len()).map(|i| (i as f64).powi(2)).sum();
2046
2047 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x.powi(2));
2048
2049 let mean: f64 = sum_y / n;
2051 let threshold = mean.abs() * 0.01; let trend = if slope > threshold {
2054 "increasing"
2055 } else if slope < -threshold {
2056 "decreasing"
2057 } else {
2058 "stable"
2059 };
2060
2061 Ok(Rc::new(Variable::String(trend.to_string())))
2062 }
2063}
2064
2065define_function!(TrendSlopeFn, vec![ArgumentType::Array], None);
2070
2071impl Function for TrendSlopeFn {
2072 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
2073 self.signature.validate(args, ctx)?;
2074
2075 let arr = args[0].as_array().ok_or_else(|| {
2076 JmespathError::new(
2077 ctx.expression,
2078 0,
2079 ErrorReason::Parse("Expected array of numbers".to_owned()),
2080 )
2081 })?;
2082
2083 if arr.len() < 2 {
2084 return Ok(Rc::new(Variable::Number(
2085 serde_json::Number::from_f64(0.0).unwrap(),
2086 )));
2087 }
2088
2089 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
2090
2091 if values.len() < 2 {
2092 return Ok(Rc::new(Variable::Number(
2093 serde_json::Number::from_f64(0.0).unwrap(),
2094 )));
2095 }
2096
2097 let n = values.len() as f64;
2099 let sum_x: f64 = (0..values.len()).map(|i| i as f64).sum();
2100 let sum_y: f64 = values.iter().sum();
2101 let sum_xy: f64 = values.iter().enumerate().map(|(i, y)| i as f64 * y).sum();
2102 let sum_x2: f64 = (0..values.len()).map(|i| (i as f64).powi(2)).sum();
2103
2104 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x.powi(2));
2105
2106 Ok(Rc::new(Variable::Number(
2107 serde_json::Number::from_f64(slope).unwrap_or_else(|| serde_json::Number::from(0)),
2108 )))
2109 }
2110}
2111
2112define_function!(RateOfChangeFn, vec![ArgumentType::Array], None);
2117
2118impl Function for RateOfChangeFn {
2119 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
2120 self.signature.validate(args, ctx)?;
2121
2122 let arr = args[0].as_array().ok_or_else(|| {
2123 JmespathError::new(
2124 ctx.expression,
2125 0,
2126 ErrorReason::Parse("Expected array of numbers".to_owned()),
2127 )
2128 })?;
2129
2130 if arr.len() < 2 {
2131 return Ok(Rc::new(Variable::Array(vec![])));
2132 }
2133
2134 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
2135
2136 if values.len() < 2 {
2137 return Ok(Rc::new(Variable::Array(vec![])));
2138 }
2139
2140 let changes: Vec<Rcvar> = values
2141 .windows(2)
2142 .map(|w| {
2143 let prev = w[0];
2144 let curr = w[1];
2145 let pct_change = if prev != 0.0 {
2146 ((curr - prev) / prev) * 100.0
2147 } else {
2148 0.0
2149 };
2150 Rc::new(Variable::Number(
2151 serde_json::Number::from_f64(pct_change)
2152 .unwrap_or_else(|| serde_json::Number::from(0)),
2153 )) as Rcvar
2154 })
2155 .collect();
2156
2157 Ok(Rc::new(Variable::Array(changes)))
2158 }
2159}
2160
2161define_function!(CumulativeSumFn, vec![ArgumentType::Array], None);
2166
2167impl Function for CumulativeSumFn {
2168 fn evaluate(&self, args: &[Rcvar], ctx: &mut Context<'_>) -> Result<Rcvar, JmespathError> {
2169 self.signature.validate(args, ctx)?;
2170
2171 let arr = args[0].as_array().ok_or_else(|| {
2172 JmespathError::new(
2173 ctx.expression,
2174 0,
2175 ErrorReason::Parse("Expected array of numbers".to_owned()),
2176 )
2177 })?;
2178
2179 let values: Vec<f64> = arr.iter().filter_map(|v| v.as_number()).collect();
2180
2181 let mut running_sum = 0.0;
2182 let cumsum: Vec<Rcvar> = values
2183 .iter()
2184 .map(|v| {
2185 running_sum += v;
2186 Rc::new(Variable::Number(
2187 serde_json::Number::from_f64(running_sum)
2188 .unwrap_or_else(|| serde_json::Number::from(0)),
2189 )) as Rcvar
2190 })
2191 .collect();
2192
2193 Ok(Rc::new(Variable::Array(cumsum)))
2194 }
2195}
2196
2197#[cfg(test)]
2198mod tests {
2199 use super::*;
2200 use jmespath::Runtime;
2201
2202 fn setup_runtime() -> Runtime {
2203 let mut runtime = Runtime::new();
2204 runtime.register_builtin_functions();
2205 register(&mut runtime);
2206 runtime
2207 }
2208
2209 #[test]
2210 #[allow(clippy::approx_constant)]
2211 fn test_round() {
2212 let runtime = setup_runtime();
2213 let expr = runtime.compile("round(`3.14159`, `2`)").unwrap();
2214 let result = expr.search(&Variable::Null).unwrap();
2215 assert!((result.as_number().unwrap() - 3.14_f64).abs() < 0.001);
2216 }
2217
2218 #[test]
2219 fn test_sqrt() {
2220 let runtime = setup_runtime();
2221 let expr = runtime.compile("sqrt(`16`)").unwrap();
2222 let result = expr.search(&Variable::Null).unwrap();
2223 assert_eq!(result.as_number().unwrap() as i64, 4);
2224 }
2225
2226 #[test]
2227 fn test_clamp() {
2228 let runtime = setup_runtime();
2229 let expr = runtime.compile("clamp(`5`, `0`, `3`)").unwrap();
2230 let result = expr.search(&Variable::Null).unwrap();
2231 assert_eq!(result.as_number().unwrap() as i64, 3);
2232 }
2233
2234 #[test]
2235 fn test_add() {
2236 let runtime = setup_runtime();
2237 let expr = runtime.compile("add(`1`, `2`)").unwrap();
2238 let result = expr.search(&Variable::Null).unwrap();
2239 assert_eq!(result.as_number().unwrap() as i64, 3);
2240 }
2241
2242 #[test]
2243 fn test_subtract() {
2244 let runtime = setup_runtime();
2245 let expr = runtime.compile("subtract(`10`, `3`)").unwrap();
2246 let result = expr.search(&Variable::Null).unwrap();
2247 assert_eq!(result.as_number().unwrap() as i64, 7);
2248 }
2249
2250 #[test]
2251 fn test_multiply() {
2252 let runtime = setup_runtime();
2253 let expr = runtime.compile("multiply(`4`, `5`)").unwrap();
2254 let result = expr.search(&Variable::Null).unwrap();
2255 assert_eq!(result.as_number().unwrap() as i64, 20);
2256 }
2257
2258 #[test]
2259 fn test_divide() {
2260 let runtime = setup_runtime();
2261 let expr = runtime.compile("divide(`10`, `4`)").unwrap();
2262 let result = expr.search(&Variable::Null).unwrap();
2263 assert_eq!(result.as_number().unwrap(), 2.5);
2264 }
2265
2266 #[test]
2267 fn test_mode_numbers() {
2268 let runtime = setup_runtime();
2269 let expr = runtime.compile("mode(`[1, 2, 2, 3]`)").unwrap();
2270 let result = expr.search(&Variable::Null).unwrap();
2271 assert_eq!(result.as_number().unwrap() as i64, 2);
2272 }
2273
2274 #[test]
2275 fn test_mode_strings() {
2276 let runtime = setup_runtime();
2277 let expr = runtime
2278 .compile("mode(`[\"a\", \"b\", \"a\", \"c\"]`)")
2279 .unwrap();
2280 let result = expr.search(&Variable::Null).unwrap();
2281 assert_eq!(result.as_string().unwrap(), "a");
2282 }
2283
2284 #[test]
2285 fn test_mode_empty() {
2286 let runtime = setup_runtime();
2287 let expr = runtime.compile("mode(`[]`)").unwrap();
2288 let result = expr.search(&Variable::Null).unwrap();
2289 assert!(result.is_null());
2290 }
2291
2292 #[test]
2293 fn test_to_fixed() {
2294 let runtime = setup_runtime();
2295 let expr = runtime.compile("to_fixed(`3.14159`, `2`)").unwrap();
2296 let result = expr.search(&Variable::Null).unwrap();
2297 assert_eq!(result.as_string().unwrap(), "3.14");
2298 }
2299
2300 #[test]
2301 fn test_to_fixed_padding() {
2302 let runtime = setup_runtime();
2303 let expr = runtime.compile("to_fixed(`3.1`, `3`)").unwrap();
2304 let result = expr.search(&Variable::Null).unwrap();
2305 assert_eq!(result.as_string().unwrap(), "3.100");
2306 }
2307
2308 #[test]
2309 fn test_format_number_with_separators() {
2310 let runtime = setup_runtime();
2311 let expr = runtime.compile("format_number(`1234567.89`, `2`)").unwrap();
2312 let result = expr.search(&Variable::Null).unwrap();
2313 assert_eq!(result.as_string().unwrap(), "1,234,567.89");
2314 }
2315
2316 #[test]
2317 fn test_format_number_with_k_suffix() {
2318 let runtime = setup_runtime();
2319 let expr = runtime.compile("format_number(`1500`, `1`, 'k')").unwrap();
2320 let result = expr.search(&Variable::Null).unwrap();
2321 assert_eq!(result.as_string().unwrap(), "1.5k");
2322 }
2323
2324 #[test]
2325 fn test_format_number_with_m_suffix() {
2326 let runtime = setup_runtime();
2327 let expr = runtime
2328 .compile("format_number(`1500000`, `1`, 'M')")
2329 .unwrap();
2330 let result = expr.search(&Variable::Null).unwrap();
2331 assert_eq!(result.as_string().unwrap(), "1.5M");
2332 }
2333
2334 #[test]
2335 fn test_format_number_auto_suffix() {
2336 let runtime = setup_runtime();
2337 let expr = runtime
2338 .compile("format_number(`1500000000`, `2`, 'auto')")
2339 .unwrap();
2340 let result = expr.search(&Variable::Null).unwrap();
2341 assert_eq!(result.as_string().unwrap(), "1.50B");
2342 }
2343
2344 #[test]
2345 fn test_histogram() {
2346 let runtime = setup_runtime();
2347 let expr = runtime.compile("histogram(@, `3`)").unwrap();
2348 let data: Variable = serde_json::from_str("[1, 2, 3, 4, 5, 6, 7, 8, 9]").unwrap();
2349 let result = expr.search(&data).unwrap();
2350 let arr = result.as_array().unwrap();
2351 assert_eq!(arr.len(), 3);
2352 for bin in arr {
2354 let obj = bin.as_object().unwrap();
2355 assert!(obj.contains_key("min"));
2356 assert!(obj.contains_key("max"));
2357 assert!(obj.contains_key("count"));
2358 }
2359 }
2360
2361 #[test]
2362 fn test_normalize() {
2363 let runtime = setup_runtime();
2364 let expr = runtime.compile("normalize(@)").unwrap();
2365 let data: Variable = serde_json::from_str("[0, 50, 100]").unwrap();
2366 let result = expr.search(&data).unwrap();
2367 let arr = result.as_array().unwrap();
2368 assert_eq!(arr.len(), 3);
2369 assert!((arr[0].as_number().unwrap() - 0.0).abs() < 0.001);
2370 assert!((arr[1].as_number().unwrap() - 0.5).abs() < 0.001);
2371 assert!((arr[2].as_number().unwrap() - 1.0).abs() < 0.001);
2372 }
2373
2374 #[test]
2375 fn test_z_score() {
2376 let runtime = setup_runtime();
2377 let expr = runtime.compile("z_score(@)").unwrap();
2378 let data: Variable = serde_json::from_str("[1, 2, 3, 4, 5]").unwrap();
2379 let result = expr.search(&data).unwrap();
2380 let arr = result.as_array().unwrap();
2381 assert_eq!(arr.len(), 5);
2382 assert!((arr[2].as_number().unwrap() - 0.0).abs() < 0.001);
2384 }
2385
2386 #[test]
2387 fn test_correlation_positive() {
2388 let runtime = setup_runtime();
2389 let expr = runtime
2390 .compile("correlation(`[1, 2, 3]`, `[1, 2, 3]`)")
2391 .unwrap();
2392 let result = expr.search(&Variable::Null).unwrap();
2393 assert!((result.as_number().unwrap() - 1.0).abs() < 0.001);
2394 }
2395
2396 #[test]
2397 fn test_correlation_negative() {
2398 let runtime = setup_runtime();
2399 let expr = runtime
2400 .compile("correlation(`[1, 2, 3]`, `[3, 2, 1]`)")
2401 .unwrap();
2402 let result = expr.search(&Variable::Null).unwrap();
2403 assert!((result.as_number().unwrap() - (-1.0)).abs() < 0.001);
2404 }
2405
2406 #[test]
2407 fn test_quantile_median() {
2408 let runtime = setup_runtime();
2409 let expr = runtime
2410 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.5`)")
2411 .unwrap();
2412 let result = expr.search(&Variable::Null).unwrap();
2413 assert_eq!(result.as_number().unwrap(), 3.0);
2414 }
2415
2416 #[test]
2417 fn test_quantile_quartiles() {
2418 let runtime = setup_runtime();
2419 let expr = runtime
2421 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.25`)")
2422 .unwrap();
2423 let result = expr.search(&Variable::Null).unwrap();
2424 assert_eq!(result.as_number().unwrap(), 2.0);
2425
2426 let expr = runtime
2428 .compile("quantile(`[1, 2, 3, 4, 5]`, `0.75`)")
2429 .unwrap();
2430 let result = expr.search(&Variable::Null).unwrap();
2431 assert_eq!(result.as_number().unwrap(), 4.0);
2432 }
2433
2434 #[test]
2435 fn test_moving_avg() {
2436 let runtime = setup_runtime();
2437 let expr = runtime
2438 .compile("moving_avg(`[1, 2, 3, 4, 5, 6]`, `3`)")
2439 .unwrap();
2440 let result = expr.search(&Variable::Null).unwrap();
2441 let arr = result.as_array().unwrap();
2442 assert_eq!(arr.len(), 6);
2443 assert!(arr[0].is_null());
2444 assert!(arr[1].is_null());
2445 assert_eq!(arr[2].as_number().unwrap(), 2.0); assert_eq!(arr[3].as_number().unwrap(), 3.0); assert_eq!(arr[4].as_number().unwrap(), 4.0); assert_eq!(arr[5].as_number().unwrap(), 5.0); }
2450
2451 #[test]
2452 fn test_ewma() {
2453 let runtime = setup_runtime();
2454 let expr = runtime.compile("ewma(`[1, 2, 3, 4, 5]`, `0.5`)").unwrap();
2455 let result = expr.search(&Variable::Null).unwrap();
2456 let arr = result.as_array().unwrap();
2457 assert_eq!(arr.len(), 5);
2458 assert_eq!(arr[0].as_number().unwrap(), 1.0);
2460 assert_eq!(arr[1].as_number().unwrap(), 1.5); assert_eq!(arr[2].as_number().unwrap(), 2.25); }
2464
2465 #[test]
2466 fn test_covariance() {
2467 let runtime = setup_runtime();
2468 let expr = runtime
2469 .compile("covariance(`[1, 2, 3]`, `[1, 2, 3]`)")
2470 .unwrap();
2471 let result = expr.search(&Variable::Null).unwrap();
2472 assert!((result.as_number().unwrap() - 0.666666).abs() < 0.01);
2474 }
2475
2476 #[test]
2477 fn test_covariance_negative() {
2478 let runtime = setup_runtime();
2479 let expr = runtime
2480 .compile("covariance(`[1, 2, 3]`, `[3, 2, 1]`)")
2481 .unwrap();
2482 let result = expr.search(&Variable::Null).unwrap();
2483 assert!((result.as_number().unwrap() - (-0.666666)).abs() < 0.01);
2484 }
2485
2486 #[test]
2487 fn test_standardize() {
2488 let runtime = setup_runtime();
2489 let expr = runtime
2490 .compile("standardize(`[10, 20, 30, 40, 50]`)")
2491 .unwrap();
2492 let result = expr.search(&Variable::Null).unwrap();
2493 let arr = result.as_array().unwrap();
2494 assert_eq!(arr.len(), 5);
2495 assert!((arr[0].as_number().unwrap() - (-1.414)).abs() < 0.01);
2498 assert!(arr[2].as_number().unwrap().abs() < 0.001);
2500 assert!((arr[4].as_number().unwrap() - 1.414).abs() < 0.01);
2502 }
2503
2504 #[test]
2505 fn test_trend_increasing() {
2506 let runtime = setup_runtime();
2507 let expr = runtime.compile("trend(`[1, 2, 3, 5, 8]`)").unwrap();
2508 let result = expr.search(&Variable::Null).unwrap();
2509 assert_eq!(result.as_string().unwrap(), "increasing");
2510 }
2511
2512 #[test]
2513 fn test_trend_decreasing() {
2514 let runtime = setup_runtime();
2515 let expr = runtime.compile("trend(`[10, 9, 8, 7, 6]`)").unwrap();
2516 let result = expr.search(&Variable::Null).unwrap();
2517 assert_eq!(result.as_string().unwrap(), "decreasing");
2518 }
2519
2520 #[test]
2521 fn test_trend_stable() {
2522 let runtime = setup_runtime();
2523 let expr = runtime.compile("trend(`[5, 5, 5, 5, 5]`)").unwrap();
2524 let result = expr.search(&Variable::Null).unwrap();
2525 assert_eq!(result.as_string().unwrap(), "stable");
2526 }
2527
2528 #[test]
2529 fn test_trend_slope() {
2530 let runtime = setup_runtime();
2531 let expr = runtime.compile("trend_slope(`[0, 1, 2, 3, 4]`)").unwrap();
2532 let result = expr.search(&Variable::Null).unwrap();
2533 assert!((result.as_number().unwrap() - 1.0).abs() < 0.001);
2535 }
2536
2537 #[test]
2538 fn test_rate_of_change() {
2539 let runtime = setup_runtime();
2540 let expr = runtime
2541 .compile("rate_of_change(`[100, 110, 105]`)")
2542 .unwrap();
2543 let result = expr.search(&Variable::Null).unwrap();
2544 let arr = result.as_array().unwrap();
2545 assert_eq!(arr.len(), 2);
2546 assert!((arr[0].as_number().unwrap() - 10.0).abs() < 0.01);
2548 assert!((arr[1].as_number().unwrap() - (-4.545)).abs() < 0.01);
2550 }
2551
2552 #[test]
2553 fn test_cumulative_sum() {
2554 let runtime = setup_runtime();
2555 let expr = runtime.compile("cumulative_sum(`[1, 2, 3, 4]`)").unwrap();
2556 let result = expr.search(&Variable::Null).unwrap();
2557 let arr = result.as_array().unwrap();
2558 assert_eq!(arr.len(), 4);
2559 assert_eq!(arr[0].as_number().unwrap(), 1.0);
2560 assert_eq!(arr[1].as_number().unwrap(), 3.0);
2561 assert_eq!(arr[2].as_number().unwrap(), 6.0);
2562 assert_eq!(arr[3].as_number().unwrap(), 10.0);
2563 }
2564}