1use std::sync::LazyLock;
19
20use datafusion_common::{exec_err, Result, ScalarValue};
21use datafusion_expr::interval_arithmetic::Interval;
22use datafusion_expr::scalar_doc_sections::DOC_SECTION_MATH;
23use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
24use datafusion_expr::Documentation;
25
26pub fn acos_order(input: &[ExprProperties]) -> Result<SortProperties> {
28 let arg = &input[0];
29 let range = &arg.range;
30
31 let valid_domain =
32 Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
33
34 if valid_domain.contains(range)? == Interval::CERTAINLY_TRUE {
35 Ok(-arg.sort_properties)
36 } else {
37 exec_err!("Input range of ACOS contains out-of-domain values")
38 }
39}
40
41static DOCUMENTATION_ACOS: LazyLock<Documentation> = LazyLock::new(|| {
42 Documentation::builder(
43 DOC_SECTION_MATH,
44 "Returns the arc cosine or inverse cosine of a number.",
45 "acos(numeric_expression)",
46 )
47 .with_standard_argument("numeric_expression", Some("Numeric"))
48 .build()
49});
50
51pub fn get_acos_doc() -> &'static Documentation {
52 &DOCUMENTATION_ACOS
53}
54
55pub fn acosh_order(input: &[ExprProperties]) -> Result<SortProperties> {
57 let arg = &input[0];
58 let range = &arg.range;
59
60 let valid_domain = Interval::try_new(
61 ScalarValue::new_one(&range.lower().data_type())?,
62 ScalarValue::try_from(&range.upper().data_type())?,
63 )?;
64
65 if valid_domain.contains(range)? == Interval::CERTAINLY_TRUE {
66 Ok(arg.sort_properties)
67 } else {
68 exec_err!("Input range of ACOSH contains out-of-domain values")
69 }
70}
71
72static DOCUMENTATION_ACOSH: LazyLock<Documentation> = LazyLock::new(|| {
73 Documentation::builder(
74 DOC_SECTION_MATH,
75 "Returns the area hyperbolic cosine or inverse hyperbolic cosine of a number.",
76 "acosh(numeric_expression)",
77 )
78 .with_standard_argument("numeric_expression", Some("Numeric"))
79 .build()
80});
81
82pub fn get_acosh_doc() -> &'static Documentation {
83 &DOCUMENTATION_ACOSH
84}
85
86pub fn asin_order(input: &[ExprProperties]) -> Result<SortProperties> {
88 let arg = &input[0];
89 let range = &arg.range;
90
91 let valid_domain =
92 Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
93
94 if valid_domain.contains(range)? == Interval::CERTAINLY_TRUE {
95 Ok(arg.sort_properties)
96 } else {
97 exec_err!("Input range of ASIN contains out-of-domain values")
98 }
99}
100
101static DOCUMENTATION_ASIN: LazyLock<Documentation> = LazyLock::new(|| {
102 Documentation::builder(
103 DOC_SECTION_MATH,
104 "Returns the arc sine or inverse sine of a number.",
105 "asin(numeric_expression)",
106 )
107 .with_standard_argument("numeric_expression", Some("Numeric"))
108 .build()
109});
110
111pub fn get_asin_doc() -> &'static Documentation {
112 &DOCUMENTATION_ASIN
113}
114
115pub fn asinh_order(input: &[ExprProperties]) -> Result<SortProperties> {
117 Ok(input[0].sort_properties)
118}
119
120static DOCUMENTATION_ASINH: LazyLock<Documentation> = LazyLock::new(|| {
121 Documentation::builder(
122 DOC_SECTION_MATH,
123 "Returns the area hyperbolic sine or inverse hyperbolic sine of a number.",
124 "asinh(numeric_expression)",
125 )
126 .with_standard_argument("numeric_expression", Some("Numeric"))
127 .build()
128});
129
130pub fn get_asinh_doc() -> &'static Documentation {
131 &DOCUMENTATION_ASINH
132}
133
134pub fn atan_order(input: &[ExprProperties]) -> Result<SortProperties> {
136 Ok(input[0].sort_properties)
137}
138
139static DOCUMENTATION_ATAN: LazyLock<Documentation> = LazyLock::new(|| {
140 Documentation::builder(
141 DOC_SECTION_MATH,
142 "Returns the arc tangent or inverse tangent of a number.",
143 "atan(numeric_expression)",
144 )
145 .with_standard_argument("numeric_expression", Some("Numeric"))
146 .build()
147});
148
149pub fn get_atan_doc() -> &'static Documentation {
150 &DOCUMENTATION_ATAN
151}
152
153pub fn atanh_order(input: &[ExprProperties]) -> Result<SortProperties> {
155 let arg = &input[0];
156 let range = &arg.range;
157
158 let valid_domain =
159 Interval::make_symmetric_unit_interval(&range.lower().data_type())?;
160
161 if valid_domain.contains(range)? == Interval::CERTAINLY_TRUE {
162 Ok(arg.sort_properties)
163 } else {
164 exec_err!("Input range of ATANH contains out-of-domain values")
165 }
166}
167
168static DOCUMENTATION_ATANH: LazyLock<Documentation> = LazyLock::new(|| {
169 Documentation::builder(
170 DOC_SECTION_MATH,
171 "Returns the area hyperbolic tangent or inverse hyperbolic tangent of a number.",
172 "atanh(numeric_expression)",
173 )
174 .with_standard_argument("numeric_expression", Some("Numeric"))
175 .build()
176});
177
178pub fn get_atanh_doc() -> &'static Documentation {
179 &DOCUMENTATION_ATANH
180}
181
182pub fn atan2_order(_input: &[ExprProperties]) -> Result<SortProperties> {
185 Ok(SortProperties::Unordered)
186}
187
188static DOCUMENTATION_ATANH2: LazyLock<Documentation> = LazyLock::new(|| {
189 Documentation::builder(
190 DOC_SECTION_MATH,
191 "Returns the arc tangent or inverse tangent of `expression_y / expression_x`.",
192 "atan2(expression_y, expression_x)",
193 )
194 .with_argument(
195 "expression_y",
196 r#"First numeric expression to operate on.
197Can be a constant, column, or function, and any combination of arithmetic operators."#,
198 )
199 .with_argument(
200 "expression_x",
201 r#"Second numeric expression to operate on.
202Can be a constant, column, or function, and any combination of arithmetic operators."#,
203 )
204 .build()
205});
206
207pub fn get_atan2_doc() -> &'static Documentation {
208 &DOCUMENTATION_ATANH2
209}
210
211pub fn cbrt_order(input: &[ExprProperties]) -> Result<SortProperties> {
213 Ok(input[0].sort_properties)
214}
215
216static DOCUMENTATION_CBRT: LazyLock<Documentation> = LazyLock::new(|| {
217 Documentation::builder(
218 DOC_SECTION_MATH,
219 "Returns the cube root of a number.",
220 "cbrt(numeric_expression)",
221 )
222 .with_standard_argument("numeric_expression", Some("Numeric"))
223 .build()
224});
225
226pub fn get_cbrt_doc() -> &'static Documentation {
227 &DOCUMENTATION_CBRT
228}
229
230pub fn ceil_order(input: &[ExprProperties]) -> Result<SortProperties> {
232 Ok(input[0].sort_properties)
233}
234
235static DOCUMENTATION_CEIL: LazyLock<Documentation> = LazyLock::new(|| {
236 Documentation::builder(
237 DOC_SECTION_MATH,
238 "Returns the nearest integer greater than or equal to a number.",
239 "ceil(numeric_expression)",
240 )
241 .with_standard_argument("numeric_expression", Some("Numeric"))
242 .build()
243});
244
245pub fn get_ceil_doc() -> &'static Documentation {
246 &DOCUMENTATION_CEIL
247}
248
249pub fn cos_order(_input: &[ExprProperties]) -> Result<SortProperties> {
253 Ok(SortProperties::Unordered)
254}
255
256static DOCUMENTATION_COS: LazyLock<Documentation> = LazyLock::new(|| {
257 Documentation::builder(
258 DOC_SECTION_MATH,
259 "Returns the cosine of a number.",
260 "cos(numeric_expression)",
261 )
262 .with_standard_argument("numeric_expression", Some("Numeric"))
263 .build()
264});
265
266pub fn get_cos_doc() -> &'static Documentation {
267 &DOCUMENTATION_COS
268}
269
270pub fn cosh_order(input: &[ExprProperties]) -> Result<SortProperties> {
272 let arg = &input[0];
273 let range = &arg.range;
274
275 let zero_point = Interval::make_zero(&range.lower().data_type())?;
276
277 if range.gt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
278 Ok(arg.sort_properties)
279 } else if range.lt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
280 Ok(-arg.sort_properties)
281 } else {
282 Ok(SortProperties::Unordered)
283 }
284}
285
286static DOCUMENTATION_COSH: LazyLock<Documentation> = LazyLock::new(|| {
287 Documentation::builder(
288 DOC_SECTION_MATH,
289 "Returns the hyperbolic cosine of a number.",
290 "cosh(numeric_expression)",
291 )
292 .with_standard_argument("numeric_expression", Some("Numeric"))
293 .build()
294});
295
296pub fn get_cosh_doc() -> &'static Documentation {
297 &DOCUMENTATION_COSH
298}
299
300pub fn degrees_order(input: &[ExprProperties]) -> Result<SortProperties> {
302 Ok(input[0].sort_properties)
303}
304
305static DOCUMENTATION_DEGREES: LazyLock<Documentation> = LazyLock::new(|| {
306 Documentation::builder(
307 DOC_SECTION_MATH,
308 "Converts radians to degrees.",
309 "degrees(numeric_expression)",
310 )
311 .with_standard_argument("numeric_expression", Some("Numeric"))
312 .build()
313});
314
315pub fn get_degrees_doc() -> &'static Documentation {
316 &DOCUMENTATION_DEGREES
317}
318
319pub fn exp_order(input: &[ExprProperties]) -> Result<SortProperties> {
321 Ok(input[0].sort_properties)
322}
323
324static DOCUMENTATION_EXP: LazyLock<Documentation> = LazyLock::new(|| {
325 Documentation::builder(
326 DOC_SECTION_MATH,
327 "Returns the base-e exponential of a number.",
328 "exp(numeric_expression)",
329 )
330 .with_standard_argument("numeric_expression", Some("Numeric"))
331 .build()
332});
333
334pub fn get_exp_doc() -> &'static Documentation {
335 &DOCUMENTATION_EXP
336}
337
338pub fn floor_order(input: &[ExprProperties]) -> Result<SortProperties> {
340 Ok(input[0].sort_properties)
341}
342
343static DOCUMENTATION_FLOOR: LazyLock<Documentation> = LazyLock::new(|| {
344 Documentation::builder(
345 DOC_SECTION_MATH,
346 "Returns the nearest integer less than or equal to a number.",
347 "floor(numeric_expression)",
348 )
349 .with_standard_argument("numeric_expression", Some("Numeric"))
350 .build()
351});
352
353pub fn get_floor_doc() -> &'static Documentation {
354 &DOCUMENTATION_FLOOR
355}
356
357pub fn ln_order(input: &[ExprProperties]) -> Result<SortProperties> {
359 let arg = &input[0];
360 let range = &arg.range;
361
362 let zero_point = Interval::make_zero(&range.lower().data_type())?;
363
364 if range.gt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
365 Ok(arg.sort_properties)
366 } else {
367 exec_err!("Input range of LN contains out-of-domain values")
368 }
369}
370
371static DOCUMENTATION_LN: LazyLock<Documentation> = LazyLock::new(|| {
372 Documentation::builder(
373 DOC_SECTION_MATH,
374 "Returns the natural logarithm of a number.",
375 "ln(numeric_expression)",
376 )
377 .with_standard_argument("numeric_expression", Some("Numeric"))
378 .build()
379});
380
381pub fn get_ln_doc() -> &'static Documentation {
382 &DOCUMENTATION_LN
383}
384
385pub fn log2_order(input: &[ExprProperties]) -> Result<SortProperties> {
387 let arg = &input[0];
388 let range = &arg.range;
389
390 let zero_point = Interval::make_zero(&range.lower().data_type())?;
391
392 if range.gt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
393 Ok(arg.sort_properties)
394 } else {
395 exec_err!("Input range of LOG2 contains out-of-domain values")
396 }
397}
398
399static DOCUMENTATION_LOG2: LazyLock<Documentation> = LazyLock::new(|| {
400 Documentation::builder(
401 DOC_SECTION_MATH,
402 "Returns the base-2 logarithm of a number.",
403 "log2(numeric_expression)",
404 )
405 .with_standard_argument("numeric_expression", Some("Numeric"))
406 .build()
407});
408
409pub fn get_log2_doc() -> &'static Documentation {
410 &DOCUMENTATION_LOG2
411}
412
413pub fn log10_order(input: &[ExprProperties]) -> Result<SortProperties> {
415 let arg = &input[0];
416 let range = &arg.range;
417
418 let zero_point = Interval::make_zero(&range.lower().data_type())?;
419
420 if range.gt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
421 Ok(arg.sort_properties)
422 } else {
423 exec_err!("Input range of LOG10 contains out-of-domain values")
424 }
425}
426
427static DOCUMENTATION_LOG10: LazyLock<Documentation> = LazyLock::new(|| {
428 Documentation::builder(
429 DOC_SECTION_MATH,
430 "Returns the base-10 logarithm of a number.",
431 "log10(numeric_expression)",
432 )
433 .with_standard_argument("numeric_expression", Some("Numeric"))
434 .build()
435});
436
437pub fn get_log10_doc() -> &'static Documentation {
438 &DOCUMENTATION_LOG10
439}
440
441pub fn radians_order(input: &[ExprProperties]) -> Result<SortProperties> {
443 Ok(input[0].sort_properties)
444}
445
446static DOCUMENTATION_RADIONS: LazyLock<Documentation> = LazyLock::new(|| {
447 Documentation::builder(
448 DOC_SECTION_MATH,
449 "Converts degrees to radians.",
450 "radians(numeric_expression)",
451 )
452 .with_standard_argument("numeric_expression", Some("Numeric"))
453 .build()
454});
455
456pub fn get_radians_doc() -> &'static Documentation {
457 &DOCUMENTATION_RADIONS
458}
459
460pub fn sin_order(_input: &[ExprProperties]) -> Result<SortProperties> {
464 Ok(SortProperties::Unordered)
465}
466
467static DOCUMENTATION_SIN: LazyLock<Documentation> = LazyLock::new(|| {
468 Documentation::builder(
469 DOC_SECTION_MATH,
470 "Returns the sine of a number.",
471 "sin(numeric_expression)",
472 )
473 .with_standard_argument("numeric_expression", Some("Numeric"))
474 .build()
475});
476
477pub fn get_sin_doc() -> &'static Documentation {
478 &DOCUMENTATION_SIN
479}
480
481pub fn sinh_order(input: &[ExprProperties]) -> Result<SortProperties> {
483 Ok(input[0].sort_properties)
484}
485
486static DOCUMENTATION_SINH: LazyLock<Documentation> = LazyLock::new(|| {
487 Documentation::builder(
488 DOC_SECTION_MATH,
489 "Returns the hyperbolic sine of a number.",
490 "sinh(numeric_expression)",
491 )
492 .with_standard_argument("numeric_expression", Some("Numeric"))
493 .build()
494});
495
496pub fn get_sinh_doc() -> &'static Documentation {
497 &DOCUMENTATION_SINH
498}
499
500pub fn sqrt_order(input: &[ExprProperties]) -> Result<SortProperties> {
502 let arg = &input[0];
503 let range = &arg.range;
504
505 let zero_point = Interval::make_zero(&range.lower().data_type())?;
506
507 if range.gt_eq(&zero_point)? == Interval::CERTAINLY_TRUE {
508 Ok(arg.sort_properties)
509 } else {
510 exec_err!("Input range of SQRT contains out-of-domain values")
511 }
512}
513
514static DOCUMENTATION_SQRT: LazyLock<Documentation> = LazyLock::new(|| {
515 Documentation::builder(
516 DOC_SECTION_MATH,
517 "Returns the square root of a number.",
518 "sqrt(numeric_expression)",
519 )
520 .with_standard_argument("numeric_expression", Some("Numeric"))
521 .build()
522});
523
524pub fn get_sqrt_doc() -> &'static Documentation {
525 &DOCUMENTATION_SQRT
526}
527
528pub fn tan_order(_input: &[ExprProperties]) -> Result<SortProperties> {
532 Ok(SortProperties::Unordered)
533}
534
535static DOCUMENTATION_TAN: LazyLock<Documentation> = LazyLock::new(|| {
536 Documentation::builder(
537 DOC_SECTION_MATH,
538 "Returns the tangent of a number.",
539 "tan(numeric_expression)",
540 )
541 .with_standard_argument("numeric_expression", Some("Numeric"))
542 .build()
543});
544
545pub fn get_tan_doc() -> &'static Documentation {
546 &DOCUMENTATION_TAN
547}
548
549pub fn tanh_order(input: &[ExprProperties]) -> Result<SortProperties> {
551 Ok(input[0].sort_properties)
552}
553
554static DOCUMENTATION_TANH: LazyLock<Documentation> = LazyLock::new(|| {
555 Documentation::builder(
556 DOC_SECTION_MATH,
557 "Returns the hyperbolic tangent of a number.",
558 "tanh(numeric_expression)",
559 )
560 .with_standard_argument("numeric_expression", Some("Numeric"))
561 .build()
562});
563
564pub fn get_tanh_doc() -> &'static Documentation {
565 &DOCUMENTATION_TANH
566}
567
568#[cfg(test)]
569mod tests {
570 use arrow::compute::SortOptions;
571 use datafusion_common::Result;
572
573 use super::*;
574
575 #[derive(Debug)]
576 struct MonotonicityTestCase {
577 name: &'static str,
578 func: fn(&[ExprProperties]) -> Result<SortProperties>,
579 lower: f64,
580 upper: f64,
581 input_sort: SortProperties,
582 expected: Result<SortProperties>,
583 }
584
585 #[test]
586 fn test_monotonicity_table() {
587 fn create_ep(lower: f64, upper: f64, sp: SortProperties) -> ExprProperties {
588 ExprProperties {
589 range: Interval::try_new(
590 ScalarValue::from(lower),
591 ScalarValue::from(upper),
592 )
593 .unwrap(),
594 sort_properties: sp,
595 preserves_lex_ordering: false,
596 }
597 }
598
599 let test_cases = vec![
600 MonotonicityTestCase {
601 name: "acos_order within domain",
602 func: acos_order,
603 lower: -0.5,
604 upper: 0.5,
605 input_sort: SortProperties::Ordered(SortOptions {
606 descending: false,
607 nulls_first: false,
608 }),
609 expected: Ok(SortProperties::Ordered(SortOptions {
610 descending: true,
611 nulls_first: false,
612 })),
613 },
614 MonotonicityTestCase {
615 name: "acos_order out of domain",
616 func: acos_order,
617 lower: -2.0,
618 upper: 1.0,
619 input_sort: SortProperties::Ordered(SortOptions {
620 descending: false,
621 nulls_first: false,
622 }),
623 expected: exec_err!("Input range of ACOS contains out-of-domain values"),
624 },
625 MonotonicityTestCase {
626 name: "acosh_order within domain",
627 func: acosh_order,
628 lower: 2.0,
629 upper: 100.0,
630 input_sort: SortProperties::Ordered(SortOptions {
631 descending: false,
632 nulls_first: true,
633 }),
634 expected: Ok(SortProperties::Ordered(SortOptions {
635 descending: false,
636 nulls_first: true,
637 })),
638 },
639 MonotonicityTestCase {
640 name: "acosh_order out of domain",
641 func: acosh_order,
642 lower: 0.5,
643 upper: 1.0,
644 input_sort: SortProperties::Ordered(SortOptions {
645 descending: true,
646 nulls_first: false,
647 }),
648 expected: exec_err!("Input range of ACOSH contains out-of-domain values"),
649 },
650 MonotonicityTestCase {
651 name: "asin_order within domain",
652 func: asin_order,
653 lower: -0.5,
654 upper: 0.5,
655 input_sort: SortProperties::Ordered(SortOptions {
656 descending: false,
657 nulls_first: false,
658 }),
659 expected: Ok(SortProperties::Ordered(SortOptions {
660 descending: false,
661 nulls_first: false,
662 })),
663 },
664 MonotonicityTestCase {
665 name: "asin_order out of domain",
666 func: asin_order,
667 lower: -2.0,
668 upper: 1.0,
669 input_sort: SortProperties::Ordered(SortOptions {
670 descending: false,
671 nulls_first: false,
672 }),
673 expected: exec_err!("Input range of ASIN contains out-of-domain values"),
674 },
675 MonotonicityTestCase {
676 name: "asinh_order within domain",
677 func: asinh_order,
678 lower: -1.0,
679 upper: 1.0,
680 input_sort: SortProperties::Ordered(SortOptions {
681 descending: false,
682 nulls_first: false,
683 }),
684 expected: Ok(SortProperties::Ordered(SortOptions {
685 descending: false,
686 nulls_first: false,
687 })),
688 },
689 MonotonicityTestCase {
690 name: "asinh_order out of domain",
691 func: asinh_order,
692 lower: -2.0,
693 upper: 1.0,
694 input_sort: SortProperties::Ordered(SortOptions {
695 descending: false,
696 nulls_first: false,
697 }),
698 expected: Ok(SortProperties::Ordered(SortOptions {
699 descending: false,
700 nulls_first: false,
701 })),
702 },
703 MonotonicityTestCase {
704 name: "atan_order within domain",
705 func: atan_order,
706 lower: -1.0,
707 upper: 1.0,
708 input_sort: SortProperties::Ordered(SortOptions {
709 descending: false,
710 nulls_first: false,
711 }),
712 expected: Ok(SortProperties::Ordered(SortOptions {
713 descending: false,
714 nulls_first: false,
715 })),
716 },
717 MonotonicityTestCase {
718 name: "atan_order out of domain",
719 func: atan_order,
720 lower: -2.0,
721 upper: 1.0,
722 input_sort: SortProperties::Ordered(SortOptions {
723 descending: false,
724 nulls_first: false,
725 }),
726 expected: Ok(SortProperties::Ordered(SortOptions {
727 descending: false,
728 nulls_first: false,
729 })),
730 },
731 MonotonicityTestCase {
732 name: "atanh_order within domain",
733 func: atanh_order,
734 lower: -0.6,
735 upper: 0.6,
736 input_sort: SortProperties::Ordered(SortOptions {
737 descending: false,
738 nulls_first: false,
739 }),
740 expected: Ok(SortProperties::Ordered(SortOptions {
741 descending: false,
742 nulls_first: false,
743 })),
744 },
745 MonotonicityTestCase {
746 name: "atanh_order out of domain",
747 func: atanh_order,
748 lower: -2.0,
749 upper: 1.0,
750 input_sort: SortProperties::Ordered(SortOptions {
751 descending: false,
752 nulls_first: false,
753 }),
754 expected: exec_err!("Input range of ATANH contains out-of-domain values"),
755 },
756 MonotonicityTestCase {
757 name: "cbrt_order within domain",
758 func: cbrt_order,
759 lower: -1.0,
760 upper: 1.0,
761 input_sort: SortProperties::Ordered(SortOptions {
762 descending: false,
763 nulls_first: false,
764 }),
765 expected: Ok(SortProperties::Ordered(SortOptions {
766 descending: false,
767 nulls_first: false,
768 })),
769 },
770 MonotonicityTestCase {
771 name: "cbrt_order out of domain",
772 func: cbrt_order,
773 lower: -2.0,
774 upper: 1.0,
775 input_sort: SortProperties::Ordered(SortOptions {
776 descending: false,
777 nulls_first: false,
778 }),
779 expected: Ok(SortProperties::Ordered(SortOptions {
780 descending: false,
781 nulls_first: false,
782 })),
783 },
784 MonotonicityTestCase {
785 name: "ceil_order within domain",
786 func: ceil_order,
787 lower: -1.0,
788 upper: 1.0,
789 input_sort: SortProperties::Ordered(SortOptions {
790 descending: false,
791 nulls_first: false,
792 }),
793 expected: Ok(SortProperties::Ordered(SortOptions {
794 descending: false,
795 nulls_first: false,
796 })),
797 },
798 MonotonicityTestCase {
799 name: "ceil_order out of domain",
800 func: ceil_order,
801 lower: -2.0,
802 upper: 1.0,
803 input_sort: SortProperties::Ordered(SortOptions {
804 descending: false,
805 nulls_first: false,
806 }),
807 expected: Ok(SortProperties::Ordered(SortOptions {
808 descending: false,
809 nulls_first: false,
810 })),
811 },
812 MonotonicityTestCase {
813 name: "cos_order within domain",
814 func: cos_order,
815 lower: 0.0,
816 upper: 2.0 * std::f64::consts::PI,
817 input_sort: SortProperties::Ordered(SortOptions {
818 descending: false,
819 nulls_first: false,
820 }),
821 expected: Ok(SortProperties::Unordered),
822 },
823 MonotonicityTestCase {
824 name: "cos_order out of domain",
825 func: cos_order,
826 lower: -2.0,
827 upper: 1.0,
828 input_sort: SortProperties::Ordered(SortOptions {
829 descending: false,
830 nulls_first: false,
831 }),
832 expected: Ok(SortProperties::Unordered),
833 },
834 MonotonicityTestCase {
835 name: "cosh_order within domain positive",
836 func: cosh_order,
837 lower: 5.0,
838 upper: 100.0,
839 input_sort: SortProperties::Ordered(SortOptions {
840 descending: false,
841 nulls_first: false,
842 }),
843 expected: Ok(SortProperties::Ordered(SortOptions {
844 descending: false,
845 nulls_first: false,
846 })),
847 },
848 MonotonicityTestCase {
849 name: "cosh_order within domain negative",
850 func: cosh_order,
851 lower: -100.0,
852 upper: -5.0,
853 input_sort: SortProperties::Ordered(SortOptions {
854 descending: false,
855 nulls_first: false,
856 }),
857 expected: Ok(SortProperties::Ordered(SortOptions {
858 descending: true,
859 nulls_first: false,
860 })),
861 },
862 MonotonicityTestCase {
863 name: "cosh_order out of domain so unordered",
864 func: cosh_order,
865 lower: -1.0,
866 upper: 1.0,
867 input_sort: SortProperties::Ordered(SortOptions {
868 descending: false,
869 nulls_first: false,
870 }),
871 expected: Ok(SortProperties::Unordered),
872 },
873 MonotonicityTestCase {
874 name: "degrees_order",
875 func: degrees_order,
876 lower: -1.0,
877 upper: 1.0,
878 input_sort: SortProperties::Ordered(SortOptions {
879 descending: true,
880 nulls_first: true,
881 }),
882 expected: Ok(SortProperties::Ordered(SortOptions {
883 descending: true,
884 nulls_first: true,
885 })),
886 },
887 MonotonicityTestCase {
888 name: "exp_order",
889 func: exp_order,
890 lower: -1000.0,
891 upper: 1000.0,
892 input_sort: SortProperties::Ordered(SortOptions {
893 descending: false,
894 nulls_first: false,
895 }),
896 expected: Ok(SortProperties::Ordered(SortOptions {
897 descending: false,
898 nulls_first: false,
899 })),
900 },
901 MonotonicityTestCase {
902 name: "floor_order",
903 func: floor_order,
904 lower: -1.0,
905 upper: 1.0,
906 input_sort: SortProperties::Ordered(SortOptions {
907 descending: true,
908 nulls_first: true,
909 }),
910 expected: Ok(SortProperties::Ordered(SortOptions {
911 descending: true,
912 nulls_first: true,
913 })),
914 },
915 MonotonicityTestCase {
916 name: "ln_order within domain",
917 func: ln_order,
918 lower: 1.0,
919 upper: 2.0,
920 input_sort: SortProperties::Ordered(SortOptions {
921 descending: false,
922 nulls_first: false,
923 }),
924 expected: Ok(SortProperties::Ordered(SortOptions {
925 descending: false,
926 nulls_first: false,
927 })),
928 },
929 MonotonicityTestCase {
930 name: "ln_order out of domain",
931 func: ln_order,
932 lower: -5.0,
933 upper: -4.0,
934 input_sort: SortProperties::Ordered(SortOptions {
935 descending: false,
936 nulls_first: false,
937 }),
938 expected: exec_err!("Input range of LN contains out-of-domain values"),
939 },
940 ];
941
942 for tcase in test_cases {
943 let input = vec![create_ep(tcase.lower, tcase.upper, tcase.input_sort)];
944 let actual = (tcase.func)(&input);
945 match (&actual, &tcase.expected) {
946 (Ok(a), Ok(e)) => assert_eq!(
947 a, e,
948 "Test '{}' failed: got {:?}, expected {:?}",
949 tcase.name, a, e
950 ),
951 (Err(e1), Err(e2)) => {
952 assert_eq!(
953 e1.strip_backtrace().to_string(),
954 e2.strip_backtrace().to_string(),
955 "Test '{}' failed: got {:?}, expected {:?}",
956 tcase.name,
957 e1,
958 e2
959 )
960 } _ => panic!(
962 "Test '{}' failed: got {:?}, expected {:?}",
963 tcase.name, actual, tcase.expected
964 ),
965 }
966 }
967 }
968}