risingwave_expr_impl/scalar/
trigonometric.rs1use risingwave_common::types::F64;
16use risingwave_expr::function;
17
18#[function("sin(float8) -> float8")]
19pub fn sin_f64(input: F64) -> F64 {
20 f64::sin(input.0).into()
21}
22
23#[function("cos(float8) -> float8")]
24pub fn cos_f64(input: F64) -> F64 {
25 f64::cos(input.0).into()
26}
27
28#[function("tan(float8) -> float8")]
29pub fn tan_f64(input: F64) -> F64 {
30 f64::tan(input.0).into()
31}
32
33#[function("cot(float8) -> float8")]
34pub fn cot_f64(input: F64) -> F64 {
35 let res = 1.0 / f64::tan(input.0);
36 res.into()
37}
38
39#[function("asin(float8) -> float8")]
40pub fn asin_f64(input: F64) -> F64 {
41 f64::asin(input.0).into()
42}
43
44#[function("acos(float8) -> float8")]
45pub fn acos_f64(input: F64) -> F64 {
46 f64::acos(input.0).into()
47}
48
49#[function("atan(float8) -> float8")]
50pub fn atan_f64(input: F64) -> F64 {
51 f64::atan(input.0).into()
52}
53
54#[function("atan2(float8, float8) -> float8")]
55pub fn atan2_f64(input_x: F64, input_y: F64) -> F64 {
56 input_x.0.atan2(input_y.0).into()
57}
58
59#[function("sinh(float8) -> float8")]
60pub fn sinh_f64(input: F64) -> F64 {
61 f64::sinh(input.0).into()
62}
63
64#[function("cosh(float8) -> float8")]
65pub fn cosh_f64(input: F64) -> F64 {
66 f64::cosh(input.0).into()
67}
68
69#[function("tanh(float8) -> float8")]
70pub fn tanh_f64(input: F64) -> F64 {
71 f64::tanh(input.0).into()
72}
73
74#[function("coth(float8) -> float8")]
75pub fn coth_f64(input: F64) -> F64 {
76 if input.0 == 0.0 {
77 return f64::NAN.into();
78 }
79 (f64::cosh(input.0) / f64::sinh(input.0)).into()
81}
82
83#[function("asinh(float8) -> float8")]
84pub fn asinh_f64(input: F64) -> F64 {
85 f64::asinh(input.0).into()
86}
87
88#[function("acosh(float8) -> float8")]
89pub fn acosh_f64(input: F64) -> F64 {
90 f64::acosh(input.0).into()
91}
92
93#[function("atanh(float8) -> float8")]
94pub fn atanh_f64(input: F64) -> F64 {
95 f64::atanh(input.0).into()
96}
97
98static DEGREE_THIRTY: f64 = 30.0;
99static DEGREE_FORTY_FIVE: f64 = 45.0;
100static DEGREE_SIXTY: f64 = 60.0;
101static DEGREE_ONE_HALF: f64 = 0.5;
102static DEGREE_ONE: f64 = 1.0;
103static RADIANS_PER_DEGREE: f64 = 0.017_453_292_519_943_295;
104
105fn sind_30() -> f64 {
109 f64::sin(DEGREE_THIRTY * RADIANS_PER_DEGREE)
110}
111
112fn one_minus_cosd_60() -> f64 {
113 DEGREE_ONE - f64::cos(DEGREE_SIXTY * RADIANS_PER_DEGREE)
114}
115
116fn tand_45() -> f64 {
117 f64::tan(DEGREE_FORTY_FIVE * RADIANS_PER_DEGREE)
118}
119
120fn cotd_45() -> f64 {
121 f64::cos(DEGREE_FORTY_FIVE * RADIANS_PER_DEGREE)
122 / f64::sin(DEGREE_FORTY_FIVE * RADIANS_PER_DEGREE)
123}
124
125fn asin_0_5() -> f64 {
126 f64::asin(DEGREE_ONE_HALF)
127}
128
129fn acos_0_5() -> f64 {
130 f64::acos(DEGREE_ONE_HALF)
131}
132
133fn cosd_0_to_60(x: f64) -> f64 {
136 let one_minus_cos_x: f64 = 1.0 - f64::cos(x * RADIANS_PER_DEGREE);
138 1.0 - (one_minus_cos_x / one_minus_cosd_60()) / 2.0
139}
140
141fn sind_0_to_30(x: f64) -> f64 {
144 let sin_x = f64::sin(x * RADIANS_PER_DEGREE);
146 (sin_x / sind_30()) / 2.0
147}
148
149fn cosd_q1(x: f64) -> f64 {
151 if x <= 60.0 {
157 cosd_0_to_60(x)
158 } else {
159 sind_0_to_30(90.0 - x)
160 }
161}
162
163#[function("cosd(float8) -> float8")]
164pub fn cosd_f64(input: F64) -> F64 {
165 let arg1 = input.0;
167
168 if input.0.is_nan() || input.0.is_infinite() {
170 return F64::from(f64::NAN);
171 }
172
173 let mut sign = 1.0;
175 let mut arg1 = arg1 % 360.0;
176
177 if arg1 < 0.0 {
178 arg1 = -arg1;
180 }
181 if arg1 > 180.0 {
182 arg1 = 360.0 - arg1;
184 }
185 if arg1 > 90.0 {
186 arg1 = 180.0 - arg1;
188 sign = -sign;
189 }
190
191 let result: f64 = sign * cosd_q1(arg1);
192
193 if result.is_infinite() {
194 return F64::from(f64::NAN);
195 }
196
197 result.into()
198}
199
200fn sind_q1(input: f64) -> f64 {
202 if input <= 30.0 {
210 sind_0_to_30(input)
211 } else {
212 cosd_0_to_60(90.0 - input)
213 }
214}
215
216#[function("sind(float8) -> float8")]
217pub fn sind_f64(input: F64) -> F64 {
218 if input.0.is_nan() || input.0.is_infinite() {
222 return f64::NAN.into();
223 }
224
225 let mut arg1 = input.0 % 360.0;
226 let mut sign = 1.0;
227
228 if arg1 < 0.0 {
229 arg1 = -arg1;
231 sign = -sign;
232 }
233 if arg1 > 180.0 {
234 arg1 = 360.0 - arg1;
236 sign = -sign;
237 }
238 if arg1 > 90.0 {
239 arg1 = 180.0 - arg1;
241 }
242
243 let result = sign * sind_q1(arg1);
244
245 if result.is_infinite() {
246 f64::NAN.into()
248 } else {
249 result.into()
250 }
251}
252
253#[function("cotd(float8) -> float8")]
254pub fn cotd_f64(input: F64) -> F64 {
255 if input.0.is_nan() || input.0.is_infinite() {
259 return f64::NAN.into();
260 }
261
262 let mut arg1 = input.0 % 360.0;
263 let mut sign = 1.0;
264
265 if arg1 == 45.0 {
267 return F64::from(1.0);
268 }
269 if arg1 == 135.0 {
270 return F64::from(-1.0);
271 }
272 if arg1 == 225. {
273 return F64::from(1.0);
274 }
275 if arg1 == 315.0 {
276 return F64::from(-1.0);
277 }
278
279 if arg1 < 0.0 {
280 arg1 = -arg1;
282 sign = -sign;
283 }
284
285 if arg1 > 180.0 {
286 arg1 = 360.0 - arg1;
288 sign = -sign;
289 }
290
291 if arg1 > 90.0 {
292 arg1 = 180.0 - arg1;
294 sign = -sign;
295 }
296
297 let cot_arg1 = cosd_q1(arg1) / sind_q1(arg1);
298 let result = sign * (cot_arg1 / cotd_45());
299
300 let result = if result == 0.0 { 0.0 } else { result };
304 result.into()
306}
307
308#[function("tand(float8) -> float8")]
309pub fn tand_f64(input: F64) -> F64 {
310 if input.0.is_nan() || input.0.is_infinite() {
313 return f64::NAN.into();
314 }
315
316 let mut arg1 = input.0 % 360.0;
317 let mut sign = 1.0;
318
319 if arg1 == 45.0 {
321 return F64::from(1.0);
322 }
323 if arg1 == 135.0 {
324 return F64::from(-1.0);
325 }
326 if arg1 == 225. {
327 return F64::from(1.0);
328 }
329 if arg1 == 315.0 {
330 return F64::from(-1.0);
331 }
332
333 if arg1 < 0.0 {
334 arg1 = -arg1;
336 sign = -sign;
337 }
338
339 if arg1 > 180.0 {
340 arg1 = 360.0 - arg1;
342 sign = -sign;
343 }
344
345 if arg1 > 90.0 {
346 arg1 = 180.0 - arg1;
348 sign = -sign;
349 }
350
351 let tan_arg1 = sind_q1(arg1) / cosd_q1(arg1);
352 let result = sign * (tan_arg1 / tand_45());
353
354 let result = if result == 0.0 { 0.0 } else { result };
358 result.into()
359}
360
361pub fn asind_q1(x: f64) -> f64 {
366 if x <= 0.5 {
370 let asin_x = f64::asin(x);
371 return (asin_x / asin_0_5()) * 30.0;
372 }
373
374 let acos_x = f64::acos(x);
375 90.0 - (acos_x / acos_0_5()) * 60.0
376}
377
378#[function("asind(float8) -> float8")]
379pub fn asind_f64(input: F64) -> F64 {
380 let arg1 = input.0;
381
382 if input.0.is_nan() || input.0.is_infinite() {
384 return F64::from(f64::NAN);
385 }
386
387 if !(-1.0..=1.0).contains(&arg1) {
389 return F64::from(f64::NAN);
390 }
391
392 let result = if arg1 >= 0.0 {
393 asind_q1(arg1)
394 } else {
395 -asind_q1(-arg1)
396 };
397
398 if result.is_infinite() {
399 return F64::from(f64::NAN);
400 }
401 result.into()
402}
403
404fn acosd_q1(x: f64) -> f64 {
408 if x <= 0.5 {
412 let asin_x = f64::asin(x);
413 return 90.0 - (asin_x / asin_0_5()) * 30.0;
414 }
415 let acos_x = f64::acos(x);
416 (acos_x / acos_0_5()) * 60.0
417}
418
419#[function("acosd(float8) -> float8")]
420pub fn acosd_f64(input: F64) -> F64 {
421 let arg1 = input.0;
422
423 if input.0.is_nan() || input.0.is_infinite() || !(-1.0..=1.0).contains(&arg1) {
425 return F64::from(f64::NAN);
426 }
427
428 let result = if arg1 >= 0.0 {
429 acosd_q1(arg1)
430 } else {
431 90.0 + asind_q1(-arg1)
432 };
433
434 if result.is_infinite() {
435 return F64::from(f64::NAN);
436 }
437 result.into()
438}
439
440#[function("atand(float8) -> float8")]
444pub fn atand_f64(input: F64) -> F64 {
445 let arg1 = input.0;
446 if arg1.is_nan() {
447 return F64::from(f64::NAN);
448 }
449 let atan_arg1 = f64::atan(arg1);
450 let result = (atan_arg1 / f64::atan(DEGREE_ONE)) * 45.0;
451 if result.is_infinite() {
452 return F64::from(f64::NAN);
453 }
454 result.into()
455}
456
457#[function("atan2d(float8, float8) -> float8")]
460pub fn atan2d_f64(input_x: F64, input_y: F64) -> F64 {
461 let (arg1, arg2) = (input_x.0, input_y.0);
462 if arg1.is_nan() || arg2.is_nan() {
463 return F64::from(f64::NAN);
464 }
465 let atan2_arg1_arg2 = f64::atan2(arg1, arg2);
466 let result = (atan2_arg1_arg2 / f64::atan(DEGREE_ONE)) * 45.0;
467 if result.is_infinite() {
468 return F64::from(f64::NAN);
469 }
470 result.into()
471}
472
473#[function("degrees(float8) -> float8")]
474pub fn degrees_f64(input: F64) -> F64 {
475 input.0.to_degrees().into()
476}
477
478#[function("radians(float8) -> float8")]
479pub fn radians_f64(input: F64) -> F64 {
480 input.0.to_radians().into()
481}
482
483#[cfg(test)]
484mod tests {
485 use risingwave_common::types::FloatExt;
486
487 use crate::scalar::trigonometric::*;
488
489 fn precision() -> f64 {
490 1e-12
491 }
492
493 fn assert_similar(lhs: F64, rhs: F64) {
495 if lhs == F64::from(f64::NAN) && rhs == F64::from(f64::NAN) {
496 return;
497 }
498 let x = (lhs.0 - rhs.0).abs() <= precision();
499 assert!(
500 x,
501 "{:?} != {:?}. Required precision is {:?}",
502 lhs.0,
503 rhs.0,
504 precision()
505 );
506 }
507
508 #[test]
509 fn test_degrees() {
510 let d = F64::from(180);
511 let pi = F64::from(core::f64::consts::PI);
512
513 assert_similar(sin_f64(50_f64.to_radians().into()), sind_f64(F64::from(50)));
515 assert_similar(
516 sin_f64(100_f64.to_radians().into()),
517 sind_f64(F64::from(100)),
518 );
519 assert_similar(
520 sin_f64(250_f64.to_radians().into()),
521 sind_f64(F64::from(250)),
522 );
523 assert_similar(sin_f64(pi), sind_f64(d));
524
525 assert_eq!(sind_f64(F64::from(30)).0, 0.5);
527 assert_eq!(sind_f64(F64::from(90)).0, 1.0);
528 assert_eq!(sind_f64(F64::from(180)).0, 0.0);
529 assert_eq!(sind_f64(F64::from(270)).0, -1.0);
530
531 assert_eq!(cos_f64(pi), cosd_f64(d));
533 assert_similar(
534 cos_f64((-180_f64).to_radians().into()),
535 cosd_f64(F64::from(-180)),
536 );
537 assert_similar(
538 cos_f64((-190_f64).to_radians().into()),
539 cosd_f64(F64::from(-190)),
540 );
541 assert_similar(cos_f64(50_f64.to_radians().into()), cosd_f64(F64::from(50)));
542 assert_similar(
543 cos_f64(100_f64.to_radians().into()),
544 cosd_f64(F64::from(100)),
545 );
546 assert_similar(
547 cos_f64(250_f64.to_radians().into()),
548 cosd_f64(F64::from(250)),
549 );
550
551 assert_eq!(cosd_f64(F64::from(0)).0, 1.0);
553 assert_eq!(cosd_f64(F64::from(90)).0, 0.0);
554
555 assert_eq!(F64::from(-f64::INFINITY), cotd_f64(d));
557 assert!(cotd_f64(F64::from(-180)).is_infinite());
558 assert!(
559 (cotd_f64(F64::from(-190)) + F64::from(5.671281819617705))
560 .abs()
561 .0
562 <= precision(),
563 );
564 assert_similar(cot_f64(50_f64.to_radians().into()), cotd_f64(F64::from(50)));
565 assert_similar(
566 cot_f64(100_f64.to_radians().into()),
567 cotd_f64(F64::from(100)),
568 );
569 assert_similar(
570 cot_f64(250_f64.to_radians().into()),
571 cotd_f64(F64::from(250)),
572 );
573
574 assert_similar(
576 tan_f64((-10_f64).to_radians().into()),
577 tand_f64(F64::from(-10)),
578 );
579 assert_similar(tan_f64(50_f64.to_radians().into()), tand_f64(F64::from(50)));
580 assert!(
581 (tan_f64(250_f64.to_radians().into()) - tand_f64(F64::from(250)))
582 .0
583 .abs()
584 < precision()
585 );
586 assert_similar(
587 tan_f64(360_f64.to_radians().into()),
588 tand_f64(F64::from(360)),
589 );
590
591 assert_similar(asind_f64(F64::from(-1)), F64::from(-90));
593 assert_similar(asind_f64(F64::from(-0.5)), F64::from(-30));
594 assert_similar(asind_f64(F64::from(0)), F64::from(0));
595 assert_similar(asind_f64(F64::from(0.5)), F64::from(30));
596 assert_similar(asind_f64(F64::from(0.75)), F64::from(48.590377890729));
597 assert_similar(asind_f64(F64::from(1)), F64::from(90));
598
599 assert_eq!(acosd_f64(F64::from(-1)), F64::from(180));
601 assert_similar(acosd_f64(F64::from(-0.75)), F64::from(138.59037789072914));
602 assert_eq!(acosd_f64(F64::from(-0.5)), F64::from(120));
603 assert_eq!(acosd_f64(F64::from(0.0)), F64::from(90));
604 assert_eq!(acosd_f64(F64::from(0.5)), F64::from(60));
605 assert_eq!(acosd_f64(F64::from(1)), F64::from(0));
606
607 assert!(tand_f64(F64::from(-270)).0.is_infinite());
609 assert_eq!(tand_f64(F64::from(-180)), 0.0);
610 assert_eq!(tand_f64(F64::from(180)), 0.0);
611 assert!(tand_f64(F64::from(-90)).0.is_infinite());
612 assert!(tand_f64(F64::from(90)).0.is_infinite());
613 assert!(tand_f64(F64::from(270)).0.is_infinite());
614 assert!(tand_f64(F64::from(450)).0.is_infinite());
615 assert!(tand_f64(F64::from(90)).0.is_infinite());
616 }
617
618 #[test]
619 fn test_trigonometric_funcs() {
620 let x = F64::from(1);
622 let y = F64::from(3);
623 let one = F64::from(1);
624 assert_similar(
625 sin_f64(x + y),
626 sin_f64(x) * cos_f64(y) + cos_f64(x) * sin_f64(y),
627 );
628 assert_similar(
629 cos_f64(x + y),
630 cos_f64(x) * cos_f64(y) - sin_f64(x) * sin_f64(y),
631 );
632 assert_similar(
633 tan_f64(x + y),
634 (tan_f64(x) + tan_f64(y)) / (one - tan_f64(x) * tan_f64(y)),
635 );
636 }
637
638 #[test]
639 fn test_inverse_trigonometric_funcs() {
640 let x = F64::from(1);
641 let y = F64::from(3);
642 let two = F64::from(2);
643 assert_similar(x, sin_f64(asin_f64(x)));
645 assert_similar(x, cos_f64(acos_f64(x)));
646 assert_similar(x, tan_f64(atan_f64(x)));
647
648 assert_similar(
650 atan2_f64(y, x),
651 two * atan_f64(y / (F64::from((x.0.powi(2) + y.0.powi(2)).sqrt()) + x)),
652 )
653 }
654
655 #[test]
656 fn test_degrees_and_radians() {
657 let full_angle = F64::from(360);
658 let tau = F64::from(std::f64::consts::TAU);
659 assert_similar(degrees_f64(tau), full_angle);
660 assert_similar(radians_f64(full_angle), tau);
661
662 let straight_angle = F64::from(180);
663 let pi = F64::from(std::f64::consts::PI);
664 assert_similar(degrees_f64(pi), straight_angle);
665 assert_similar(radians_f64(straight_angle), pi);
666
667 let right_angle = F64::from(90);
668 let half_pi = F64::from(std::f64::consts::PI / 2.);
669 assert_similar(degrees_f64(half_pi), right_angle);
670 assert_similar(radians_f64(right_angle), half_pi);
671
672 let zero = F64::from(0);
673 assert_similar(degrees_f64(zero), zero);
674 assert_similar(radians_f64(zero), zero);
675 }
676
677 #[test]
678 fn test_hyperbolic_trigonometric_funcs() {
679 let two = F64::from(2);
680 let one = F64::from(1);
681 let x = F64::from(5);
682 let y = F64::from(3);
683 assert_similar(
685 sinh_f64(x + y),
686 sinh_f64(x) * cosh_f64(y) + cosh_f64(x) * sinh_f64(y),
687 );
688 assert_similar(
689 cosh_f64(x + y),
690 cosh_f64(x) * cosh_f64(y) + sinh_f64(x) * sinh_f64(y),
691 );
692 assert_similar(
693 tanh_f64(x + y),
694 (tanh_f64(x) + tanh_f64(y)) / (one + tanh_f64(x) * tanh_f64(y)),
695 );
696 assert_similar(coth_f64(-x), -coth_f64(x));
698 assert_similar(tanh_f64(-x), -tanh_f64(x));
699 assert_similar(two * acosh_f64(x), acosh_f64(two * x.powi(2) - one)); assert_similar(two * asinh_f64(x), acosh_f64(two * x.powi(2) + one)); let x = x.powi(2).0;
704
705 assert_similar(
706 asinh_f64(F64::from(x.powi(2) - 1.0) / (two * x)),
707 atanh_f64(F64::from(x.powi(2) - 1.0) / F64::from(x.powi(2) + 1.0)),
708 );
709 }
710
711 #[test]
712 fn test_exact() {
713 assert_eq!(cotd_f64(F64::from(135.0)).0, -1.0);
714 assert_eq!(cotd_f64(F64::from(225.0)).0, 1.0);
715 assert_eq!(cotd_f64(F64::from(315.0)).0, -1.0);
716 assert_eq!(cotd_f64(F64::from(45.0)).0, 1.0);
717 assert_eq!(tand_f64(F64::from(45.0)).0, 1.0);
718 assert_eq!(tand_f64(F64::from(135.0)).0, -1.0);
719 assert_eq!(tand_f64(F64::from(225.0)).0, 1.0);
720 assert_eq!(tand_f64(F64::from(315.0)).0, -1.0);
721 }
722}