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