1#![allow(
11 clippy::unnecessary_literal_bound,
12 clippy::too_many_lines,
13 clippy::cast_possible_truncation,
14 clippy::cast_possible_wrap,
15 clippy::suboptimal_flops,
16 clippy::unnecessary_wraps,
17 clippy::match_same_arms,
18 clippy::items_after_statements,
19 clippy::float_cmp
20)]
21
22use fsqlite_error::Result;
23use fsqlite_types::SqliteValue;
24
25use crate::{FunctionRegistry, ScalarFunction};
26
27fn to_f64(v: &SqliteValue) -> Option<f64> {
31 match v {
32 SqliteValue::Null => None,
33 SqliteValue::Integer(i) => Some(*i as f64),
34 SqliteValue::Float(f) => Some(*f),
35 SqliteValue::Text(s) => s.parse::<f64>().ok(),
36 SqliteValue::Blob(_) => Some(0.0),
37 }
38}
39
40fn wrap(v: f64) -> SqliteValue {
42 if v.is_nan() {
43 SqliteValue::Null
44 } else {
45 SqliteValue::Float(v)
46 }
47}
48
49fn unary_math(args: &[SqliteValue], f: fn(f64) -> f64) -> Result<SqliteValue> {
51 let Some(x) = to_f64(&args[0]) else {
52 return Ok(SqliteValue::Null);
53 };
54 Ok(wrap(f(x)))
55}
56
57fn unary_domain(
59 args: &[SqliteValue],
60 domain: fn(f64) -> bool,
61 f: fn(f64) -> f64,
62) -> Result<SqliteValue> {
63 let Some(x) = to_f64(&args[0]) else {
64 return Ok(SqliteValue::Null);
65 };
66 if !domain(x) {
67 return Ok(SqliteValue::Null);
68 }
69 Ok(wrap(f(x)))
70}
71
72pub struct AcosFunc;
75
76impl ScalarFunction for AcosFunc {
77 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
78 unary_domain(args, |x| (-1.0..=1.0).contains(&x), f64::acos)
79 }
80
81 fn num_args(&self) -> i32 {
82 1
83 }
84
85 fn name(&self) -> &str {
86 "acos"
87 }
88}
89
90pub struct AsinFunc;
91
92impl ScalarFunction for AsinFunc {
93 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
94 unary_domain(args, |x| (-1.0..=1.0).contains(&x), f64::asin)
95 }
96
97 fn num_args(&self) -> i32 {
98 1
99 }
100
101 fn name(&self) -> &str {
102 "asin"
103 }
104}
105
106pub struct AtanFunc;
107
108impl ScalarFunction for AtanFunc {
109 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
110 unary_math(args, f64::atan)
111 }
112
113 fn num_args(&self) -> i32 {
114 1
115 }
116
117 fn name(&self) -> &str {
118 "atan"
119 }
120}
121
122pub struct Atan2Func;
123
124impl ScalarFunction for Atan2Func {
125 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
126 let Some(y) = to_f64(&args[0]) else {
127 return Ok(SqliteValue::Null);
128 };
129 let Some(x) = to_f64(&args[1]) else {
130 return Ok(SqliteValue::Null);
131 };
132 Ok(wrap(y.atan2(x)))
133 }
134
135 fn num_args(&self) -> i32 {
136 2
137 }
138
139 fn name(&self) -> &str {
140 "atan2"
141 }
142}
143
144pub struct CosFunc;
145
146impl ScalarFunction for CosFunc {
147 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
148 unary_math(args, f64::cos)
149 }
150
151 fn num_args(&self) -> i32 {
152 1
153 }
154
155 fn name(&self) -> &str {
156 "cos"
157 }
158}
159
160pub struct SinFunc;
161
162impl ScalarFunction for SinFunc {
163 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
164 unary_math(args, f64::sin)
165 }
166
167 fn num_args(&self) -> i32 {
168 1
169 }
170
171 fn name(&self) -> &str {
172 "sin"
173 }
174}
175
176pub struct TanFunc;
177
178impl ScalarFunction for TanFunc {
179 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
180 unary_math(args, f64::tan)
181 }
182
183 fn num_args(&self) -> i32 {
184 1
185 }
186
187 fn name(&self) -> &str {
188 "tan"
189 }
190}
191
192pub struct AcoshFunc;
195
196impl ScalarFunction for AcoshFunc {
197 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
198 unary_domain(args, |x| x >= 1.0, f64::acosh)
199 }
200
201 fn num_args(&self) -> i32 {
202 1
203 }
204
205 fn name(&self) -> &str {
206 "acosh"
207 }
208}
209
210pub struct AsinhFunc;
211
212impl ScalarFunction for AsinhFunc {
213 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
214 unary_math(args, f64::asinh)
215 }
216
217 fn num_args(&self) -> i32 {
218 1
219 }
220
221 fn name(&self) -> &str {
222 "asinh"
223 }
224}
225
226pub struct AtanhFunc;
227
228impl ScalarFunction for AtanhFunc {
229 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
230 unary_domain(args, |x| x > -1.0 && x < 1.0, f64::atanh)
233 }
234
235 fn num_args(&self) -> i32 {
236 1
237 }
238
239 fn name(&self) -> &str {
240 "atanh"
241 }
242}
243
244pub struct CoshFunc;
245
246impl ScalarFunction for CoshFunc {
247 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
248 unary_math(args, f64::cosh)
249 }
250
251 fn num_args(&self) -> i32 {
252 1
253 }
254
255 fn name(&self) -> &str {
256 "cosh"
257 }
258}
259
260pub struct SinhFunc;
261
262impl ScalarFunction for SinhFunc {
263 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
264 unary_math(args, f64::sinh)
265 }
266
267 fn num_args(&self) -> i32 {
268 1
269 }
270
271 fn name(&self) -> &str {
272 "sinh"
273 }
274}
275
276pub struct TanhFunc;
277
278impl ScalarFunction for TanhFunc {
279 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
280 unary_math(args, f64::tanh)
281 }
282
283 fn num_args(&self) -> i32 {
284 1
285 }
286
287 fn name(&self) -> &str {
288 "tanh"
289 }
290}
291
292pub struct CeilFunc;
297
298impl ScalarFunction for CeilFunc {
299 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
300 match &args[0] {
301 SqliteValue::Null => Ok(SqliteValue::Null),
302 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(*i)),
303 other => {
304 let Some(x) = to_f64(other) else {
305 return Ok(SqliteValue::Null);
306 };
307 Ok(wrap(x.ceil()))
308 }
309 }
310 }
311
312 fn num_args(&self) -> i32 {
313 1
314 }
315
316 fn name(&self) -> &str {
317 "ceil"
318 }
319}
320
321pub struct FloorFunc;
322
323impl ScalarFunction for FloorFunc {
324 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
325 match &args[0] {
326 SqliteValue::Null => Ok(SqliteValue::Null),
327 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(*i)),
328 other => {
329 let Some(x) = to_f64(other) else {
330 return Ok(SqliteValue::Null);
331 };
332 Ok(wrap(x.floor()))
333 }
334 }
335 }
336
337 fn num_args(&self) -> i32 {
338 1
339 }
340
341 fn name(&self) -> &str {
342 "floor"
343 }
344}
345
346pub struct TruncFunc;
347
348impl ScalarFunction for TruncFunc {
349 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
350 match &args[0] {
351 SqliteValue::Null => Ok(SqliteValue::Null),
352 SqliteValue::Integer(i) => Ok(SqliteValue::Integer(*i)),
353 other => {
354 let Some(x) = to_f64(other) else {
355 return Ok(SqliteValue::Null);
356 };
357 Ok(wrap(x.trunc()))
358 }
359 }
360 }
361
362 fn num_args(&self) -> i32 {
363 1
364 }
365
366 fn name(&self) -> &str {
367 "trunc"
368 }
369}
370
371pub struct LnFunc;
374
375impl ScalarFunction for LnFunc {
376 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
377 unary_domain(args, |x| x > 0.0, f64::ln)
378 }
379
380 fn num_args(&self) -> i32 {
381 1
382 }
383
384 fn name(&self) -> &str {
385 "ln"
386 }
387}
388
389pub struct Log10Func;
391
392impl ScalarFunction for Log10Func {
393 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
394 unary_domain(args, |x| x > 0.0, f64::log10)
395 }
396
397 fn num_args(&self) -> i32 {
398 1
399 }
400
401 fn name(&self) -> &str {
402 "log10"
403 }
404}
405
406pub struct LogFunc;
408
409impl ScalarFunction for LogFunc {
410 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
411 if args.len() == 1 {
412 return Log10Func.invoke(args);
414 }
415 let Some(b) = to_f64(&args[0]) else {
417 return Ok(SqliteValue::Null);
418 };
419 let Some(x) = to_f64(&args[1]) else {
420 return Ok(SqliteValue::Null);
421 };
422 if b <= 0.0 || b == 1.0 || x <= 0.0 {
423 return Ok(SqliteValue::Null);
424 }
425 Ok(wrap(x.ln() / b.ln()))
426 }
427
428 fn num_args(&self) -> i32 {
429 -1 }
431
432 fn name(&self) -> &str {
433 "log"
434 }
435}
436
437pub struct Log2Func;
438
439impl ScalarFunction for Log2Func {
440 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
441 unary_domain(args, |x| x > 0.0, f64::log2)
442 }
443
444 fn num_args(&self) -> i32 {
445 1
446 }
447
448 fn name(&self) -> &str {
449 "log2"
450 }
451}
452
453pub struct ExpFunc;
454
455impl ScalarFunction for ExpFunc {
456 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
457 unary_math(args, f64::exp)
459 }
460
461 fn num_args(&self) -> i32 {
462 1
463 }
464
465 fn name(&self) -> &str {
466 "exp"
467 }
468}
469
470pub struct PowFunc;
471
472impl ScalarFunction for PowFunc {
473 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
474 let Some(x) = to_f64(&args[0]) else {
475 return Ok(SqliteValue::Null);
476 };
477 let Some(y) = to_f64(&args[1]) else {
478 return Ok(SqliteValue::Null);
479 };
480 Ok(wrap(x.powf(y)))
481 }
482
483 fn num_args(&self) -> i32 {
484 2
485 }
486
487 fn name(&self) -> &str {
488 "pow"
489 }
490}
491
492pub struct SqrtFunc;
493
494impl ScalarFunction for SqrtFunc {
495 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
496 unary_domain(args, |x| x >= 0.0, f64::sqrt)
497 }
498
499 fn num_args(&self) -> i32 {
500 1
501 }
502
503 fn name(&self) -> &str {
504 "sqrt"
505 }
506}
507
508pub struct DegreesFunc;
511
512impl ScalarFunction for DegreesFunc {
513 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
514 unary_math(args, f64::to_degrees)
515 }
516
517 fn num_args(&self) -> i32 {
518 1
519 }
520
521 fn name(&self) -> &str {
522 "degrees"
523 }
524}
525
526pub struct RadiansFunc;
527
528impl ScalarFunction for RadiansFunc {
529 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
530 unary_math(args, f64::to_radians)
531 }
532
533 fn num_args(&self) -> i32 {
534 1
535 }
536
537 fn name(&self) -> &str {
538 "radians"
539 }
540}
541
542pub struct ModFunc;
543
544impl ScalarFunction for ModFunc {
545 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
546 let Some(x) = to_f64(&args[0]) else {
547 return Ok(SqliteValue::Null);
548 };
549 let Some(y) = to_f64(&args[1]) else {
550 return Ok(SqliteValue::Null);
551 };
552 if y == 0.0 {
553 return Ok(SqliteValue::Null);
554 }
555 Ok(wrap(x % y))
556 }
557
558 fn num_args(&self) -> i32 {
559 2
560 }
561
562 fn name(&self) -> &str {
563 "mod"
564 }
565}
566
567pub struct PiFunc;
568
569impl ScalarFunction for PiFunc {
570 fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
571 Ok(SqliteValue::Float(std::f64::consts::PI))
572 }
573
574 fn num_args(&self) -> i32 {
575 0
576 }
577
578 fn name(&self) -> &str {
579 "pi"
580 }
581}
582
583pub fn register_math_builtins(registry: &mut FunctionRegistry) {
587 registry.register_scalar(AcosFunc);
589 registry.register_scalar(AsinFunc);
590 registry.register_scalar(AtanFunc);
591 registry.register_scalar(Atan2Func);
592 registry.register_scalar(CosFunc);
593 registry.register_scalar(SinFunc);
594 registry.register_scalar(TanFunc);
595
596 registry.register_scalar(AcoshFunc);
598 registry.register_scalar(AsinhFunc);
599 registry.register_scalar(AtanhFunc);
600 registry.register_scalar(CoshFunc);
601 registry.register_scalar(SinhFunc);
602 registry.register_scalar(TanhFunc);
603
604 registry.register_scalar(CeilFunc);
606 registry.register_scalar(FloorFunc);
607 registry.register_scalar(TruncFunc);
608
609 registry.register_scalar(LnFunc);
611 registry.register_scalar(LogFunc);
612 registry.register_scalar(Log10Func);
613 registry.register_scalar(Log2Func);
614 registry.register_scalar(ExpFunc);
615 registry.register_scalar(PowFunc);
616 registry.register_scalar(SqrtFunc);
617
618 registry.register_scalar(DegreesFunc);
620 registry.register_scalar(RadiansFunc);
621 registry.register_scalar(ModFunc);
622 registry.register_scalar(PiFunc);
623
624 struct CeilingFunc;
627 impl ScalarFunction for CeilingFunc {
628 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
629 CeilFunc.invoke(args)
630 }
631
632 fn num_args(&self) -> i32 {
633 1
634 }
635
636 fn name(&self) -> &str {
637 "ceiling"
638 }
639 }
640 registry.register_scalar(CeilingFunc);
641
642 struct PowerFunc;
644 impl ScalarFunction for PowerFunc {
645 fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
646 PowFunc.invoke(args)
647 }
648
649 fn num_args(&self) -> i32 {
650 2
651 }
652
653 fn name(&self) -> &str {
654 "power"
655 }
656 }
657 registry.register_scalar(PowerFunc);
658}
659
660#[cfg(test)]
663mod tests {
664 use super::*;
665
666 const EPS: f64 = 1e-12;
668
669 fn assert_float_eq(result: &SqliteValue, expected: f64) {
670 match result {
671 SqliteValue::Float(v) => {
672 assert!((v - expected).abs() < EPS, "expected {expected}, got {v}");
673 }
674 other => panic!("expected Float({expected}), got {other:?}"),
675 }
676 }
677
678 fn assert_null(result: &SqliteValue) {
679 assert_eq!(result, &SqliteValue::Null, "expected NULL");
680 }
681
682 fn float(v: f64) -> SqliteValue {
683 SqliteValue::Float(v)
684 }
685
686 fn int(v: i64) -> SqliteValue {
687 SqliteValue::Integer(v)
688 }
689
690 fn null() -> SqliteValue {
691 SqliteValue::Null
692 }
693
694 #[test]
697 fn test_acos_valid() {
698 let r = AcosFunc.invoke(&[float(0.5)]).unwrap();
699 assert_float_eq(&r, 0.5_f64.acos());
700 }
701
702 #[test]
703 fn test_acos_domain_error() {
704 assert_null(&AcosFunc.invoke(&[float(2.0)]).unwrap());
705 }
706
707 #[test]
708 fn test_acos_null() {
709 assert_null(&AcosFunc.invoke(&[null()]).unwrap());
710 }
711
712 #[test]
713 fn test_acosh_valid() {
714 let r = AcoshFunc.invoke(&[float(2.0)]).unwrap();
715 assert_float_eq(&r, 2.0_f64.acosh());
716 }
717
718 #[test]
719 fn test_acosh_domain_error() {
720 assert_null(&AcoshFunc.invoke(&[float(0.5)]).unwrap());
721 }
722
723 #[test]
724 fn test_asin_valid() {
725 let r = AsinFunc.invoke(&[float(0.5)]).unwrap();
726 assert_float_eq(&r, 0.5_f64.asin());
727 }
728
729 #[test]
730 fn test_asin_domain_error() {
731 assert_null(&AsinFunc.invoke(&[float(2.0)]).unwrap());
732 }
733
734 #[test]
735 fn test_asinh_all_reals() {
736 let r = AsinhFunc.invoke(&[float(1.0)]).unwrap();
737 assert_float_eq(&r, 1.0_f64.asinh());
738 }
739
740 #[test]
741 fn test_atan_basic() {
742 let r = AtanFunc.invoke(&[float(1.0)]).unwrap();
743 assert_float_eq(&r, std::f64::consts::FRAC_PI_4);
744 }
745
746 #[test]
747 fn test_atan2_quadrants() {
748 let r = Atan2Func.invoke(&[float(1.0), float(1.0)]).unwrap();
750 assert_float_eq(&r, std::f64::consts::FRAC_PI_4);
751
752 let r = Atan2Func.invoke(&[float(1.0), float(-1.0)]).unwrap();
754 assert_float_eq(&r, 3.0 * std::f64::consts::FRAC_PI_4);
755
756 let r = Atan2Func.invoke(&[float(-1.0), float(-1.0)]).unwrap();
758 assert_float_eq(&r, -3.0 * std::f64::consts::FRAC_PI_4);
759
760 let r = Atan2Func.invoke(&[float(-1.0), float(1.0)]).unwrap();
762 assert_float_eq(&r, -std::f64::consts::FRAC_PI_4);
763 }
764
765 #[test]
766 fn test_atanh_valid() {
767 let r = AtanhFunc.invoke(&[float(0.5)]).unwrap();
768 assert_float_eq(&r, 0.5_f64.atanh());
769 }
770
771 #[test]
772 fn test_atanh_domain_error() {
773 assert_null(&AtanhFunc.invoke(&[float(1.0)]).unwrap());
775 assert_null(&AtanhFunc.invoke(&[float(-1.0)]).unwrap());
776 }
777
778 #[test]
779 fn test_cos_zero() {
780 let r = CosFunc.invoke(&[float(0.0)]).unwrap();
781 assert_float_eq(&r, 1.0);
782 }
783
784 #[test]
785 fn test_cosh_zero() {
786 let r = CoshFunc.invoke(&[float(0.0)]).unwrap();
787 assert_float_eq(&r, 1.0);
788 }
789
790 #[test]
791 fn test_sin_zero() {
792 let r = SinFunc.invoke(&[float(0.0)]).unwrap();
793 assert_float_eq(&r, 0.0);
794 }
795
796 #[test]
797 fn test_sinh_zero() {
798 let r = SinhFunc.invoke(&[float(0.0)]).unwrap();
799 assert_float_eq(&r, 0.0);
800 }
801
802 #[test]
803 fn test_tan_zero() {
804 let r = TanFunc.invoke(&[float(0.0)]).unwrap();
805 assert_float_eq(&r, 0.0);
806 }
807
808 #[test]
809 fn test_tanh_zero() {
810 let r = TanhFunc.invoke(&[float(0.0)]).unwrap();
811 assert_float_eq(&r, 0.0);
812 }
813
814 #[test]
817 fn test_ceil_real() {
818 let r = CeilFunc.invoke(&[float(1.2)]).unwrap();
819 assert_float_eq(&r, 2.0);
820 }
821
822 #[test]
823 fn test_ceil_integer_type() {
824 let r = CeilFunc.invoke(&[int(5)]).unwrap();
825 assert_eq!(r, SqliteValue::Integer(5));
826 }
827
828 #[test]
829 fn test_ceil_negative() {
830 let r = CeilFunc.invoke(&[float(-1.2)]).unwrap();
831 assert_float_eq(&r, -1.0);
832 }
833
834 #[test]
835 fn test_floor_real() {
836 let r = FloorFunc.invoke(&[float(1.7)]).unwrap();
837 assert_float_eq(&r, 1.0);
838 }
839
840 #[test]
841 fn test_floor_integer_type() {
842 let r = FloorFunc.invoke(&[int(5)]).unwrap();
843 assert_eq!(r, SqliteValue::Integer(5));
844 }
845
846 #[test]
847 fn test_floor_negative() {
848 let r = FloorFunc.invoke(&[float(-1.2)]).unwrap();
849 assert_float_eq(&r, -2.0);
850 }
851
852 #[test]
853 fn test_trunc_positive() {
854 let r = TruncFunc.invoke(&[float(2.9)]).unwrap();
855 assert_float_eq(&r, 2.0);
856 }
857
858 #[test]
859 fn test_trunc_negative() {
860 let r = TruncFunc.invoke(&[float(-2.9)]).unwrap();
861 assert_float_eq(&r, -2.0);
862 }
863
864 #[test]
865 fn test_trunc_integer_type() {
866 let r = TruncFunc.invoke(&[int(5)]).unwrap();
867 assert_eq!(r, SqliteValue::Integer(5));
868 }
869
870 #[test]
873 fn test_ln_positive() {
874 let r = LnFunc.invoke(&[float(std::f64::consts::E)]).unwrap();
875 assert_float_eq(&r, 1.0);
876 }
877
878 #[test]
879 fn test_ln_zero() {
880 assert_null(&LnFunc.invoke(&[float(0.0)]).unwrap());
881 }
882
883 #[test]
884 fn test_ln_negative() {
885 assert_null(&LnFunc.invoke(&[float(-1.0)]).unwrap());
886 }
887
888 #[test]
889 fn test_log_single_arg_base10() {
890 let r = LogFunc.invoke(&[float(100.0)]).unwrap();
891 assert_float_eq(&r, 2.0);
892 }
893
894 #[test]
895 fn test_log_two_arg_base() {
896 let r = LogFunc.invoke(&[float(2.0), float(8.0)]).unwrap();
897 assert_float_eq(&r, 3.0);
898 }
899
900 #[test]
901 fn test_log10_alias() {
902 let r = Log10Func.invoke(&[float(1000.0)]).unwrap();
903 assert_float_eq(&r, 3.0);
904 }
905
906 #[test]
907 fn test_log2_basic() {
908 let r = Log2Func.invoke(&[float(8.0)]).unwrap();
909 assert_float_eq(&r, 3.0);
910 }
911
912 #[test]
913 fn test_exp_one() {
914 let r = ExpFunc.invoke(&[float(1.0)]).unwrap();
915 assert_float_eq(&r, std::f64::consts::E);
916 }
917
918 #[test]
919 fn test_exp_overflow() {
920 let r = ExpFunc.invoke(&[float(1000.0)]).unwrap();
921 match r {
922 SqliteValue::Float(v) => assert!(v.is_infinite() && v > 0.0, "+Inf expected"),
923 other => panic!("expected +Inf Float, got {other:?}"),
924 }
925 }
926
927 #[test]
928 fn test_pow_basic() {
929 let r = PowFunc.invoke(&[float(2.0), float(10.0)]).unwrap();
930 assert_float_eq(&r, 1024.0);
931 }
932
933 #[test]
934 fn test_pow_zero_zero() {
935 let r = PowFunc.invoke(&[float(0.0), float(0.0)]).unwrap();
936 assert_float_eq(&r, 1.0);
937 }
938
939 #[test]
940 fn test_power_alias() {
941 let r = PowFunc.invoke(&[float(3.0), float(2.0)]).unwrap();
944 assert_float_eq(&r, 9.0);
945 }
946
947 #[test]
948 fn test_sqrt_positive() {
949 let r = SqrtFunc.invoke(&[float(144.0)]).unwrap();
950 assert_float_eq(&r, 12.0);
951 }
952
953 #[test]
954 fn test_sqrt_negative() {
955 assert_null(&SqrtFunc.invoke(&[float(-1.0)]).unwrap());
956 }
957
958 #[test]
961 #[allow(clippy::approx_constant)]
962 fn test_degrees_pi() {
963 let r = DegreesFunc.invoke(&[float(std::f64::consts::PI)]).unwrap();
964 assert_float_eq(&r, 180.0);
965 }
966
967 #[test]
968 #[allow(clippy::approx_constant)]
969 fn test_radians_180() {
970 let r = RadiansFunc.invoke(&[float(180.0)]).unwrap();
971 assert_float_eq(&r, std::f64::consts::PI);
972 }
973
974 #[test]
975 fn test_mod_basic() {
976 let r = ModFunc.invoke(&[float(10.0), float(3.0)]).unwrap();
977 assert_float_eq(&r, 1.0);
978 }
979
980 #[test]
981 fn test_mod_zero_divisor() {
982 assert_null(&ModFunc.invoke(&[float(10.0), float(0.0)]).unwrap());
983 }
984
985 #[test]
986 fn test_pi_precision() {
987 let r = PiFunc.invoke(&[]).unwrap();
988 assert_float_eq(&r, std::f64::consts::PI);
989 }
990
991 #[test]
994 fn test_nan_normalized_to_null() {
995 assert_null(&SqrtFunc.invoke(&[float(-1.0)]).unwrap());
998 assert_null(&AcosFunc.invoke(&[float(2.0)]).unwrap());
1000 }
1001
1002 #[test]
1003 fn test_inf_propagation() {
1004 let r = ExpFunc.invoke(&[float(1000.0)]).unwrap();
1006 match r {
1007 SqliteValue::Float(v) => assert!(v.is_infinite()),
1008 other => panic!("expected Inf, got {other:?}"),
1009 }
1010 }
1011
1012 #[test]
1013 fn test_neg_inf_propagation() {
1014 let r = SinhFunc.invoke(&[float(1000.0)]).unwrap();
1017 match r {
1018 SqliteValue::Float(v) => assert!(v.is_infinite() && v > 0.0),
1019 other => panic!("expected +Inf, got {other:?}"),
1020 }
1021 let r = SinhFunc.invoke(&[float(-1000.0)]).unwrap();
1022 match r {
1023 SqliteValue::Float(v) => assert!(v.is_infinite() && v < 0.0),
1024 other => panic!("expected -Inf, got {other:?}"),
1025 }
1026 }
1027
1028 #[test]
1029 fn test_ceiling_alias() {
1030 let r1 = CeilFunc.invoke(&[float(1.2)]).unwrap();
1033 let mut reg = FunctionRegistry::new();
1036 register_math_builtins(&mut reg);
1037 let ceiling = reg.find_scalar("ceiling", 1).expect("ceiling registered");
1038 let r2 = ceiling.invoke(&[float(1.2)]).unwrap();
1039 assert_eq!(r1, r2);
1040 }
1041
1042 #[test]
1043 fn test_all_null_input() {
1044 let n = &[null()];
1046 assert_null(&AcosFunc.invoke(n).unwrap());
1047 assert_null(&AsinFunc.invoke(n).unwrap());
1048 assert_null(&AtanFunc.invoke(n).unwrap());
1049 assert_null(&CosFunc.invoke(n).unwrap());
1050 assert_null(&SinFunc.invoke(n).unwrap());
1051 assert_null(&TanFunc.invoke(n).unwrap());
1052 assert_null(&AcoshFunc.invoke(n).unwrap());
1053 assert_null(&AsinhFunc.invoke(n).unwrap());
1054 assert_null(&AtanhFunc.invoke(n).unwrap());
1055 assert_null(&CoshFunc.invoke(n).unwrap());
1056 assert_null(&SinhFunc.invoke(n).unwrap());
1057 assert_null(&TanhFunc.invoke(n).unwrap());
1058 assert_null(&CeilFunc.invoke(n).unwrap());
1059 assert_null(&FloorFunc.invoke(n).unwrap());
1060 assert_null(&TruncFunc.invoke(n).unwrap());
1061 assert_null(&LnFunc.invoke(n).unwrap());
1062 assert_null(&Log10Func.invoke(n).unwrap());
1063 assert_null(&Log2Func.invoke(n).unwrap());
1064 assert_null(&ExpFunc.invoke(n).unwrap());
1065 assert_null(&SqrtFunc.invoke(n).unwrap());
1066 assert_null(&DegreesFunc.invoke(n).unwrap());
1067 assert_null(&RadiansFunc.invoke(n).unwrap());
1068 assert_null(&Atan2Func.invoke(&[null(), float(1.0)]).unwrap());
1070 assert_null(&Atan2Func.invoke(&[float(1.0), null()]).unwrap());
1071 assert_null(&PowFunc.invoke(&[null(), float(1.0)]).unwrap());
1072 assert_null(&ModFunc.invoke(&[null(), float(1.0)]).unwrap());
1073 assert_null(&LogFunc.invoke(&[null()]).unwrap());
1075 assert_null(&LogFunc.invoke(&[null(), float(8.0)]).unwrap());
1076 assert_null(&LogFunc.invoke(&[float(2.0), null()]).unwrap());
1077 }
1078
1079 #[test]
1080 fn test_register_math_builtins_all_present() {
1081 let mut reg = FunctionRegistry::new();
1082 register_math_builtins(&mut reg);
1083
1084 let expected = [
1085 ("acos", 1),
1086 ("asin", 1),
1087 ("atan", 1),
1088 ("atan2", 2),
1089 ("cos", 1),
1090 ("sin", 1),
1091 ("tan", 1),
1092 ("acosh", 1),
1093 ("asinh", 1),
1094 ("atanh", 1),
1095 ("cosh", 1),
1096 ("sinh", 1),
1097 ("tanh", 1),
1098 ("ceil", 1),
1099 ("ceiling", 1),
1100 ("floor", 1),
1101 ("trunc", 1),
1102 ("ln", 1),
1103 ("log10", 1),
1104 ("log2", 1),
1105 ("exp", 1),
1106 ("pow", 2),
1107 ("power", 2),
1108 ("sqrt", 1),
1109 ("degrees", 1),
1110 ("radians", 1),
1111 ("mod", 2),
1112 ("pi", 0),
1113 ];
1114
1115 for (name, arity) in expected {
1116 assert!(
1117 reg.find_scalar(name, arity).is_some(),
1118 "math function '{name}/{arity}' not registered"
1119 );
1120 }
1121
1122 assert!(reg.find_scalar("log", 1).is_some(), "log/1 via variadic");
1124 assert!(reg.find_scalar("log", 2).is_some(), "log/2 via variadic");
1125 }
1126
1127 #[test]
1128 fn test_e2e_registry_invoke_math() {
1129 let mut reg = FunctionRegistry::new();
1130 register_math_builtins(&mut reg);
1131
1132 let pi = reg.find_scalar("pi", 0).unwrap();
1134 let r = pi.invoke(&[]).unwrap();
1135 assert_float_eq(&r, std::f64::consts::PI);
1136
1137 let sqrt = reg.find_scalar("sqrt", 1).unwrap();
1139 let r = sqrt.invoke(&[float(144.0)]).unwrap();
1140 assert_float_eq(&r, 12.0);
1141
1142 let log = reg.find_scalar("log", 2).unwrap();
1144 let r = log.invoke(&[float(2.0), float(8.0)]).unwrap();
1145 assert_float_eq(&r, 3.0);
1146 }
1147}