1use super::super::utils::{
2 ARG_ANY_ONE, ARG_NUM_LENIENT_ONE, ARG_NUM_LENIENT_TWO, EPSILON_NEAR_ZERO,
3 binary_numeric_elementwise, unary_numeric_arg, unary_numeric_elementwise,
4};
5use crate::args::ArgSchema;
6use crate::function::Function;
7use crate::traits::{ArgumentHandle, FunctionContext};
8use formualizer_common::{ExcelError, LiteralValue};
9use formualizer_macros::func_caps;
10use std::f64::consts::PI;
11
12#[derive(Debug)]
15pub struct SinFn;
16impl Function for SinFn {
57 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
58 fn name(&self) -> &'static str {
59 "SIN"
60 }
61 fn min_args(&self) -> usize {
62 1
63 }
64 fn arg_schema(&self) -> &'static [ArgSchema] {
65 &ARG_NUM_LENIENT_ONE[..]
66 }
67 fn eval<'a, 'b, 'c>(
68 &self,
69 args: &'c [ArgumentHandle<'a, 'b>],
70 ctx: &dyn FunctionContext<'b>,
71 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
72 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.sin())))
73 }
74}
75
76#[cfg(test)]
77mod tests_sin {
78 use super::*;
79 use crate::test_workbook::TestWorkbook;
80 use crate::traits::ArgumentHandle;
81 use formualizer_parse::LiteralValue;
82
83 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
84 wb.interpreter()
85 }
86 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
87 formualizer_parse::parser::ASTNode::new(
88 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
89 None,
90 )
91 }
92 fn assert_close(a: f64, b: f64) {
93 assert!((a - b).abs() < 1e-9, "{a} !~= {b}");
94 }
95
96 #[test]
97 fn test_sin_basic() {
98 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinFn));
99 let ctx = interp(&wb);
100 let sin = ctx.context.get_function("", "SIN").unwrap();
101 let a0 = make_num_ast(PI / 2.0);
102 let args = vec![ArgumentHandle::new(&a0, &ctx)];
103 match sin
104 .dispatch(&args, &ctx.function_context(None))
105 .unwrap()
106 .into_literal()
107 {
108 LiteralValue::Number(n) => assert_close(n, 1.0),
109 v => panic!("unexpected {v:?}"),
110 }
111 }
112
113 #[test]
114 fn test_sin_array_literal() {
115 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinFn));
116 let ctx = interp(&wb);
117 let sin = ctx.context.get_function("", "SIN").unwrap();
118 let arr = formualizer_parse::parser::ASTNode::new(
119 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Array(vec![vec![
120 LiteralValue::Number(0.0),
121 LiteralValue::Number(PI / 2.0),
122 ]])),
123 None,
124 );
125 let args = vec![ArgumentHandle::new(&arr, &ctx)];
126
127 match sin
128 .dispatch(&args, &ctx.function_context(None))
129 .unwrap()
130 .into_literal()
131 {
132 formualizer_common::LiteralValue::Array(rows) => {
133 assert_eq!(rows.len(), 1);
134 assert_eq!(rows[0].len(), 2);
135 match (&rows[0][0], &rows[0][1]) {
136 (
137 formualizer_common::LiteralValue::Number(a),
138 formualizer_common::LiteralValue::Number(b),
139 ) => {
140 assert_close(*a, 0.0);
141 assert_close(*b, 1.0);
142 }
143 other => panic!("unexpected {other:?}"),
144 }
145 }
146 other => panic!("expected array, got {other:?}"),
147 }
148 }
149}
150
151#[derive(Debug)]
152pub struct CosFn;
153impl Function for CosFn {
192 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
193 fn name(&self) -> &'static str {
194 "COS"
195 }
196 fn min_args(&self) -> usize {
197 1
198 }
199 fn arg_schema(&self) -> &'static [ArgSchema] {
200 &ARG_NUM_LENIENT_ONE[..]
201 }
202 fn eval<'a, 'b, 'c>(
203 &self,
204 args: &'c [ArgumentHandle<'a, 'b>],
205 ctx: &dyn FunctionContext<'b>,
206 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
207 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.cos())))
208 }
209}
210
211#[cfg(test)]
212mod tests_cos {
213 use super::*;
214 use crate::test_workbook::TestWorkbook;
215 use crate::traits::ArgumentHandle;
216 use formualizer_parse::LiteralValue;
217 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
218 wb.interpreter()
219 }
220 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
221 formualizer_parse::parser::ASTNode::new(
222 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
223 None,
224 )
225 }
226 fn assert_close(a: f64, b: f64) {
227 assert!((a - b).abs() < 1e-9);
228 }
229 #[test]
230 fn test_cos_basic() {
231 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CosFn));
232 let ctx = interp(&wb);
233 let cos = ctx.context.get_function("", "COS").unwrap();
234 let a0 = make_num_ast(0.0);
235 let args = vec![ArgumentHandle::new(&a0, &ctx)];
236 match cos
237 .dispatch(&args, &ctx.function_context(None))
238 .unwrap()
239 .into_literal()
240 {
241 LiteralValue::Number(n) => assert_close(n, 1.0),
242 v => panic!("unexpected {v:?}"),
243 }
244 }
245}
246
247#[derive(Debug)]
248pub struct TanFn;
249impl Function for TanFn {
288 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
289 fn name(&self) -> &'static str {
290 "TAN"
291 }
292 fn min_args(&self) -> usize {
293 1
294 }
295 fn arg_schema(&self) -> &'static [ArgSchema] {
296 &ARG_NUM_LENIENT_ONE[..]
297 }
298 fn eval<'a, 'b, 'c>(
299 &self,
300 args: &'c [ArgumentHandle<'a, 'b>],
301 ctx: &dyn FunctionContext<'b>,
302 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
303 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.tan())))
304 }
305}
306
307#[cfg(test)]
308mod tests_tan {
309 use super::*;
310 use crate::test_workbook::TestWorkbook;
311 use crate::traits::ArgumentHandle;
312 use formualizer_parse::LiteralValue;
313 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
314 wb.interpreter()
315 }
316 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
317 formualizer_parse::parser::ASTNode::new(
318 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
319 None,
320 )
321 }
322 fn assert_close(a: f64, b: f64) {
323 assert!((a - b).abs() < 1e-9);
324 }
325 #[test]
326 fn test_tan_basic() {
327 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TanFn));
328 let ctx = interp(&wb);
329 let tan = ctx.context.get_function("", "TAN").unwrap();
330 let a0 = make_num_ast(PI / 4.0);
331 let args = vec![ArgumentHandle::new(&a0, &ctx)];
332 match tan
333 .dispatch(&args, &ctx.function_context(None))
334 .unwrap()
335 .into_literal()
336 {
337 LiteralValue::Number(n) => assert_close(n, 1.0),
338 v => panic!("unexpected {v:?}"),
339 }
340 }
341}
342
343#[derive(Debug)]
344pub struct AsinFn;
345impl Function for AsinFn {
387 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
388 fn name(&self) -> &'static str {
389 "ASIN"
390 }
391 fn min_args(&self) -> usize {
392 1
393 }
394 fn arg_schema(&self) -> &'static [ArgSchema] {
395 &ARG_NUM_LENIENT_ONE[..]
396 }
397 fn eval<'a, 'b, 'c>(
398 &self,
399 args: &'c [ArgumentHandle<'a, 'b>],
400 _ctx: &dyn FunctionContext<'b>,
401 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
402 let x = unary_numeric_arg(args)?;
403 if !(-1.0..=1.0).contains(&x) {
404 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
405 ExcelError::new_num(),
406 )));
407 }
408 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
409 x.asin(),
410 )))
411 }
412}
413
414#[cfg(test)]
415mod tests_asin {
416 use super::*;
417 use crate::test_workbook::TestWorkbook;
418 use crate::traits::ArgumentHandle;
419 use formualizer_parse::LiteralValue;
420 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
421 wb.interpreter()
422 }
423 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
424 formualizer_parse::parser::ASTNode::new(
425 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
426 None,
427 )
428 }
429 fn assert_close(a: f64, b: f64) {
430 assert!((a - b).abs() < 1e-9);
431 }
432 #[test]
433 fn test_asin_basic_and_domain() {
434 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AsinFn));
435 let ctx = interp(&wb);
436 let asin = ctx.context.get_function("", "ASIN").unwrap();
437 let a0 = make_num_ast(0.5);
439 let args = vec![ArgumentHandle::new(&a0, &ctx)];
440 match asin
441 .dispatch(&args, &ctx.function_context(None))
442 .unwrap()
443 .into_literal()
444 {
445 LiteralValue::Number(n) => assert_close(n, (0.5f64).asin()),
446 v => panic!("unexpected {v:?}"),
447 }
448 let a1 = make_num_ast(2.0);
450 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
451 match asin
452 .dispatch(&args2, &ctx.function_context(None))
453 .unwrap()
454 .into_literal()
455 {
456 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
457 v => panic!("expected error, got {v:?}"),
458 }
459 }
460}
461
462#[derive(Debug)]
463pub struct AcosFn;
464impl Function for AcosFn {
506 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
507 fn name(&self) -> &'static str {
508 "ACOS"
509 }
510 fn min_args(&self) -> usize {
511 1
512 }
513 fn arg_schema(&self) -> &'static [ArgSchema] {
514 &ARG_NUM_LENIENT_ONE[..]
515 }
516 fn eval<'a, 'b, 'c>(
517 &self,
518 args: &'c [ArgumentHandle<'a, 'b>],
519 _ctx: &dyn FunctionContext<'b>,
520 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
521 let x = unary_numeric_arg(args)?;
522 if !(-1.0..=1.0).contains(&x) {
523 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
524 ExcelError::new_num(),
525 )));
526 }
527 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
528 x.acos(),
529 )))
530 }
531}
532
533#[cfg(test)]
534mod tests_acos {
535 use super::*;
536 use crate::test_workbook::TestWorkbook;
537 use crate::traits::ArgumentHandle;
538 use formualizer_parse::LiteralValue;
539 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
540 wb.interpreter()
541 }
542 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
543 formualizer_parse::parser::ASTNode::new(
544 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
545 None,
546 )
547 }
548 fn assert_close(a: f64, b: f64) {
549 assert!((a - b).abs() < 1e-9);
550 }
551 #[test]
552 fn test_acos_basic_and_domain() {
553 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcosFn));
554 let ctx = interp(&wb);
555 let acos = ctx.context.get_function("", "ACOS").unwrap();
556 let a0 = make_num_ast(0.5);
557 let args = vec![ArgumentHandle::new(&a0, &ctx)];
558 match acos
559 .dispatch(&args, &ctx.function_context(None))
560 .unwrap()
561 .into_literal()
562 {
563 LiteralValue::Number(n) => assert_close(n, (0.5f64).acos()),
564 v => panic!("unexpected {v:?}"),
565 }
566 let a1 = make_num_ast(-2.0);
567 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
568 match acos
569 .dispatch(&args2, &ctx.function_context(None))
570 .unwrap()
571 .into_literal()
572 {
573 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
574 v => panic!("expected error, got {v:?}"),
575 }
576 }
577}
578
579#[derive(Debug)]
580pub struct AtanFn;
581impl Function for AtanFn {
623 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
624 fn name(&self) -> &'static str {
625 "ATAN"
626 }
627 fn min_args(&self) -> usize {
628 1
629 }
630 fn arg_schema(&self) -> &'static [ArgSchema] {
631 &ARG_NUM_LENIENT_ONE[..]
632 }
633 fn eval<'a, 'b, 'c>(
634 &self,
635 args: &'c [ArgumentHandle<'a, 'b>],
636 _ctx: &dyn FunctionContext<'b>,
637 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
638 let x = unary_numeric_arg(args)?;
639 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
640 x.atan(),
641 )))
642 }
643}
644
645#[cfg(test)]
646mod tests_atan {
647 use super::*;
648 use crate::test_workbook::TestWorkbook;
649 use crate::traits::ArgumentHandle;
650 use formualizer_parse::LiteralValue;
651 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
652 wb.interpreter()
653 }
654 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
655 formualizer_parse::parser::ASTNode::new(
656 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
657 None,
658 )
659 }
660 fn assert_close(a: f64, b: f64) {
661 assert!((a - b).abs() < 1e-9);
662 }
663 #[test]
664 fn test_atan_basic() {
665 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AtanFn));
666 let ctx = interp(&wb);
667 let atan = ctx.context.get_function("", "ATAN").unwrap();
668 let a0 = make_num_ast(1.0);
669 let args = vec![ArgumentHandle::new(&a0, &ctx)];
670 match atan
671 .dispatch(&args, &ctx.function_context(None))
672 .unwrap()
673 .into_literal()
674 {
675 LiteralValue::Number(n) => assert_close(n, (1.0f64).atan()),
676 v => panic!("unexpected {v:?}"),
677 }
678 }
679}
680
681#[derive(Debug)]
682pub struct Atan2Fn;
683impl Function for Atan2Fn {
722 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
723 fn name(&self) -> &'static str {
724 "ATAN2"
725 }
726 fn min_args(&self) -> usize {
727 2
728 }
729 fn arg_schema(&self) -> &'static [ArgSchema] {
730 &ARG_NUM_LENIENT_TWO[..]
731 }
732 fn eval<'a, 'b, 'c>(
733 &self,
734 args: &'c [ArgumentHandle<'a, 'b>],
735 ctx: &dyn FunctionContext<'b>,
736 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
737 binary_numeric_elementwise(args, ctx, |x, y| {
739 if x == 0.0 && y == 0.0 {
740 Ok(LiteralValue::Error(ExcelError::from_error_string(
741 "#DIV/0!",
742 )))
743 } else {
744 Ok(LiteralValue::Number(y.atan2(x)))
745 }
746 })
747 }
748}
749
750#[cfg(test)]
751mod tests_atan2 {
752 use super::*;
753 use crate::test_workbook::TestWorkbook;
754 use crate::traits::ArgumentHandle;
755 use formualizer_parse::LiteralValue;
756 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
757 wb.interpreter()
758 }
759 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
760 formualizer_parse::parser::ASTNode::new(
761 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
762 None,
763 )
764 }
765 fn assert_close(a: f64, b: f64) {
766 assert!((a - b).abs() < 1e-9);
767 }
768 #[test]
769 fn test_atan2_basic_and_zero_zero() {
770 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(Atan2Fn));
771 let ctx = interp(&wb);
772 let atan2 = ctx.context.get_function("", "ATAN2").unwrap();
773 let a0 = make_num_ast(1.0);
775 let a1 = make_num_ast(1.0);
776 let args = vec![
777 ArgumentHandle::new(&a0, &ctx),
778 ArgumentHandle::new(&a1, &ctx),
779 ];
780 match atan2
781 .dispatch(&args, &ctx.function_context(None))
782 .unwrap()
783 .into_literal()
784 {
785 LiteralValue::Number(n) => assert_close(n, PI / 4.0),
786 v => panic!("unexpected {v:?}"),
787 }
788 let b0 = make_num_ast(0.0);
790 let b1 = make_num_ast(0.0);
791 let args2 = vec![
792 ArgumentHandle::new(&b0, &ctx),
793 ArgumentHandle::new(&b1, &ctx),
794 ];
795 match atan2
796 .dispatch(&args2, &ctx.function_context(None))
797 .unwrap()
798 .into_literal()
799 {
800 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
801 v => panic!("expected error, got {v:?}"),
802 }
803 }
804
805 #[test]
806 fn test_atan2_broadcast_scalar_over_array() {
807 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(Atan2Fn));
808 let ctx = interp(&wb);
809 let atan2 = ctx.context.get_function("", "ATAN2").unwrap();
810
811 let x = make_num_ast(1.0);
813 let y = formualizer_parse::parser::ASTNode::new(
814 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Array(vec![vec![
815 LiteralValue::Number(0.0),
816 LiteralValue::Number(1.0),
817 ]])),
818 None,
819 );
820 let args = vec![ArgumentHandle::new(&x, &ctx), ArgumentHandle::new(&y, &ctx)];
821
822 match atan2
823 .dispatch(&args, &ctx.function_context(None))
824 .unwrap()
825 .into_literal()
826 {
827 formualizer_common::LiteralValue::Array(rows) => {
828 assert_eq!(rows.len(), 1);
829 assert_eq!(rows[0].len(), 2);
830 match (&rows[0][0], &rows[0][1]) {
831 (
832 formualizer_common::LiteralValue::Number(a),
833 formualizer_common::LiteralValue::Number(b),
834 ) => {
835 assert_close(*a, 0.0);
836 assert_close(*b, PI / 4.0);
837 }
838 other => panic!("unexpected {other:?}"),
839 }
840 }
841 other => panic!("expected array, got {other:?}"),
842 }
843 }
844}
845
846#[derive(Debug)]
847pub struct SecFn;
848impl Function for SecFn {
890 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
891 fn name(&self) -> &'static str {
892 "SEC"
893 }
894 fn min_args(&self) -> usize {
895 1
896 }
897 fn arg_schema(&self) -> &'static [ArgSchema] {
898 &ARG_NUM_LENIENT_ONE[..]
899 }
900 fn eval<'a, 'b, 'c>(
901 &self,
902 args: &'c [ArgumentHandle<'a, 'b>],
903 _ctx: &dyn FunctionContext<'b>,
904 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
905 let x = unary_numeric_arg(args)?;
906 let c = x.cos();
907 if c.abs() < EPSILON_NEAR_ZERO {
908 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
909 ExcelError::from_error_string("#DIV/0!"),
910 )));
911 }
912 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
913 1.0 / c,
914 )))
915 }
916}
917
918#[cfg(test)]
919mod tests_sec {
920 use super::*;
921 use crate::test_workbook::TestWorkbook;
922 use crate::traits::ArgumentHandle;
923 use formualizer_parse::LiteralValue;
924 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
925 wb.interpreter()
926 }
927 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
928 formualizer_parse::parser::ASTNode::new(
929 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
930 None,
931 )
932 }
933 fn assert_close(a: f64, b: f64) {
934 assert!((a - b).abs() < 1e-9);
935 }
936 #[test]
937 fn test_sec_basic_and_div0() {
938 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SecFn));
939 let ctx = interp(&wb);
940 let sec = ctx.context.get_function("", "SEC").unwrap();
941 let a0 = make_num_ast(0.0);
942 let args = vec![ArgumentHandle::new(&a0, &ctx)];
943 match sec
944 .dispatch(&args, &ctx.function_context(None))
945 .unwrap()
946 .into_literal()
947 {
948 LiteralValue::Number(n) => assert_close(n, 1.0),
949 v => panic!("unexpected {v:?}"),
950 }
951 let a1 = make_num_ast(PI / 2.0);
952 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
953 match sec
954 .dispatch(&args2, &ctx.function_context(None))
955 .unwrap()
956 .into_literal()
957 {
958 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
959 LiteralValue::Number(n) => assert!(n.abs() > 1e12), v => panic!("unexpected {v:?}"),
961 }
962 }
963}
964
965#[derive(Debug)]
966pub struct CscFn;
967impl Function for CscFn {
1009 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1010 fn name(&self) -> &'static str {
1011 "CSC"
1012 }
1013 fn min_args(&self) -> usize {
1014 1
1015 }
1016 fn arg_schema(&self) -> &'static [ArgSchema] {
1017 &ARG_NUM_LENIENT_ONE[..]
1018 }
1019 fn eval<'a, 'b, 'c>(
1020 &self,
1021 args: &'c [ArgumentHandle<'a, 'b>],
1022 _ctx: &dyn FunctionContext<'b>,
1023 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1024 let x = unary_numeric_arg(args)?;
1025 let s = x.sin();
1026 if s.abs() < EPSILON_NEAR_ZERO {
1027 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1028 ExcelError::from_error_string("#DIV/0!"),
1029 )));
1030 }
1031 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1032 1.0 / s,
1033 )))
1034 }
1035}
1036
1037#[cfg(test)]
1038mod tests_csc {
1039 use super::*;
1040 use crate::test_workbook::TestWorkbook;
1041 use crate::traits::ArgumentHandle;
1042 use formualizer_parse::LiteralValue;
1043 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1044 wb.interpreter()
1045 }
1046 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1047 formualizer_parse::parser::ASTNode::new(
1048 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1049 None,
1050 )
1051 }
1052 fn assert_close(a: f64, b: f64) {
1053 assert!((a - b).abs() < 1e-9);
1054 }
1055 #[test]
1056 fn test_csc_basic_and_div0() {
1057 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CscFn));
1058 let ctx = interp(&wb);
1059 let csc = ctx.context.get_function("", "CSC").unwrap();
1060 let a0 = make_num_ast(PI / 2.0);
1061 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1062 match csc
1063 .dispatch(&args, &ctx.function_context(None))
1064 .unwrap()
1065 .into_literal()
1066 {
1067 LiteralValue::Number(n) => assert_close(n, 1.0),
1068 v => panic!("unexpected {v:?}"),
1069 }
1070 let a1 = make_num_ast(0.0);
1071 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1072 match csc
1073 .dispatch(&args2, &ctx.function_context(None))
1074 .unwrap()
1075 .into_literal()
1076 {
1077 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1078 v => panic!("expected error, got {v:?}"),
1079 }
1080 }
1081}
1082
1083#[derive(Debug)]
1084pub struct CotFn;
1085impl Function for CotFn {
1127 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1128 fn name(&self) -> &'static str {
1129 "COT"
1130 }
1131 fn min_args(&self) -> usize {
1132 1
1133 }
1134 fn arg_schema(&self) -> &'static [ArgSchema] {
1135 &ARG_NUM_LENIENT_ONE[..]
1136 }
1137 fn eval<'a, 'b, 'c>(
1138 &self,
1139 args: &'c [ArgumentHandle<'a, 'b>],
1140 _ctx: &dyn FunctionContext<'b>,
1141 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1142 let x = unary_numeric_arg(args)?;
1143 let t = x.tan();
1144 if t.abs() < EPSILON_NEAR_ZERO {
1145 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1146 ExcelError::from_error_string("#DIV/0!"),
1147 )));
1148 }
1149 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1150 1.0 / t,
1151 )))
1152 }
1153}
1154
1155#[cfg(test)]
1156mod tests_cot {
1157 use super::*;
1158 use crate::test_workbook::TestWorkbook;
1159 use crate::traits::ArgumentHandle;
1160 use formualizer_parse::LiteralValue;
1161 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1162 wb.interpreter()
1163 }
1164 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1165 formualizer_parse::parser::ASTNode::new(
1166 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1167 None,
1168 )
1169 }
1170 fn assert_close(a: f64, b: f64) {
1171 assert!((a - b).abs() < 1e-9);
1172 }
1173 #[test]
1174 fn test_cot_basic_and_div0() {
1175 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CotFn));
1176 let ctx = interp(&wb);
1177 let cot = ctx.context.get_function("", "COT").unwrap();
1178 let a0 = make_num_ast(PI / 4.0);
1179 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1180 match cot
1181 .dispatch(&args, &ctx.function_context(None))
1182 .unwrap()
1183 .into_literal()
1184 {
1185 LiteralValue::Number(n) => assert_close(n, 1.0),
1186 v => panic!("unexpected {v:?}"),
1187 }
1188 let a1 = make_num_ast(0.0);
1189 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1190 match cot
1191 .dispatch(&args2, &ctx.function_context(None))
1192 .unwrap()
1193 .into_literal()
1194 {
1195 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1196 v => panic!("expected error, got {v:?}"),
1197 }
1198 }
1199}
1200
1201#[derive(Debug)]
1202pub struct AcotFn;
1203impl Function for AcotFn {
1245 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1246 fn name(&self) -> &'static str {
1247 "ACOT"
1248 }
1249 fn min_args(&self) -> usize {
1250 1
1251 }
1252 fn arg_schema(&self) -> &'static [ArgSchema] {
1253 &ARG_NUM_LENIENT_ONE[..]
1254 }
1255 fn eval<'a, 'b, 'c>(
1256 &self,
1257 args: &'c [ArgumentHandle<'a, 'b>],
1258 _ctx: &dyn FunctionContext<'b>,
1259 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1260 let x = unary_numeric_arg(args)?;
1261 let result = if x == 0.0 {
1262 PI / 2.0
1263 } else if x > 0.0 {
1264 (1.0 / x).atan()
1265 } else {
1266 (1.0 / x).atan() + PI
1267 };
1268 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1269 result,
1270 )))
1271 }
1272}
1273
1274#[cfg(test)]
1275mod tests_acot {
1276 use super::*;
1277 use crate::test_workbook::TestWorkbook;
1278 use crate::traits::ArgumentHandle;
1279 use formualizer_parse::LiteralValue;
1280 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1281 wb.interpreter()
1282 }
1283 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1284 formualizer_parse::parser::ASTNode::new(
1285 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1286 None,
1287 )
1288 }
1289 fn assert_close(a: f64, b: f64) {
1290 assert!((a - b).abs() < 1e-9);
1291 }
1292 #[test]
1293 fn test_acot_basic() {
1294 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcotFn));
1295 let ctx = interp(&wb);
1296 let acot = ctx.context.get_function("", "ACOT").unwrap();
1297 let a0 = make_num_ast(2.0);
1298 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1299 match acot
1300 .dispatch(&args, &ctx.function_context(None))
1301 .unwrap()
1302 .into_literal()
1303 {
1304 LiteralValue::Number(n) => assert_close(n, 0.4636476090008061),
1305 v => panic!("unexpected {v:?}"),
1306 }
1307 }
1308}
1309
1310#[derive(Debug)]
1313pub struct SinhFn;
1314impl Function for SinhFn {
1356 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1357 fn name(&self) -> &'static str {
1358 "SINH"
1359 }
1360 fn min_args(&self) -> usize {
1361 1
1362 }
1363 fn arg_schema(&self) -> &'static [ArgSchema] {
1364 &ARG_NUM_LENIENT_ONE[..]
1365 }
1366 fn eval<'a, 'b, 'c>(
1367 &self,
1368 args: &'c [ArgumentHandle<'a, 'b>],
1369 _ctx: &dyn FunctionContext<'b>,
1370 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1371 let x = unary_numeric_arg(args)?;
1372 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1373 x.sinh(),
1374 )))
1375 }
1376}
1377
1378#[cfg(test)]
1379mod tests_sinh {
1380 use super::*;
1381 use crate::test_workbook::TestWorkbook;
1382 use crate::traits::ArgumentHandle;
1383 use formualizer_parse::LiteralValue;
1384 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1385 wb.interpreter()
1386 }
1387 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1388 formualizer_parse::parser::ASTNode::new(
1389 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1390 None,
1391 )
1392 }
1393 fn assert_close(a: f64, b: f64) {
1394 assert!((a - b).abs() < 1e-9);
1395 }
1396 #[test]
1397 fn test_sinh_basic() {
1398 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinhFn));
1399 let ctx = interp(&wb);
1400 let f = ctx.context.get_function("", "SINH").unwrap();
1401 let a0 = make_num_ast(1.0);
1402 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1403 let fctx = ctx.function_context(None);
1404 match f.dispatch(&args, &fctx).unwrap().into_literal() {
1405 LiteralValue::Number(n) => assert_close(n, (1.0f64).sinh()),
1406 v => panic!("unexpected {v:?}"),
1407 }
1408 }
1409}
1410
1411#[derive(Debug)]
1412pub struct CoshFn;
1413impl Function for CoshFn {
1455 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1456 fn name(&self) -> &'static str {
1457 "COSH"
1458 }
1459 fn min_args(&self) -> usize {
1460 1
1461 }
1462 fn arg_schema(&self) -> &'static [ArgSchema] {
1463 &ARG_NUM_LENIENT_ONE[..]
1464 }
1465 fn eval<'a, 'b, 'c>(
1466 &self,
1467 args: &'c [ArgumentHandle<'a, 'b>],
1468 _ctx: &dyn FunctionContext<'b>,
1469 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1470 let x = unary_numeric_arg(args)?;
1471 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1472 x.cosh(),
1473 )))
1474 }
1475}
1476
1477#[cfg(test)]
1478mod tests_cosh {
1479 use super::*;
1480 use crate::test_workbook::TestWorkbook;
1481 use crate::traits::ArgumentHandle;
1482 use formualizer_parse::LiteralValue;
1483 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1484 wb.interpreter()
1485 }
1486 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1487 formualizer_parse::parser::ASTNode::new(
1488 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1489 None,
1490 )
1491 }
1492 fn assert_close(a: f64, b: f64) {
1493 assert!((a - b).abs() < 1e-9);
1494 }
1495 #[test]
1496 fn test_cosh_basic() {
1497 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CoshFn));
1498 let ctx = interp(&wb);
1499 let f = ctx.context.get_function("", "COSH").unwrap();
1500 let a0 = make_num_ast(1.0);
1501 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1502 match f
1503 .dispatch(&args, &ctx.function_context(None))
1504 .unwrap()
1505 .into_literal()
1506 {
1507 LiteralValue::Number(n) => assert_close(n, (1.0f64).cosh()),
1508 v => panic!("unexpected {v:?}"),
1509 }
1510 }
1511}
1512
1513#[derive(Debug)]
1514pub struct TanhFn;
1515impl Function for TanhFn {
1557 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1558 fn name(&self) -> &'static str {
1559 "TANH"
1560 }
1561 fn min_args(&self) -> usize {
1562 1
1563 }
1564 fn arg_schema(&self) -> &'static [ArgSchema] {
1565 &ARG_NUM_LENIENT_ONE[..]
1566 }
1567 fn eval<'a, 'b, 'c>(
1568 &self,
1569 args: &'c [ArgumentHandle<'a, 'b>],
1570 _ctx: &dyn FunctionContext<'b>,
1571 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1572 let x = unary_numeric_arg(args)?;
1573 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1574 x.tanh(),
1575 )))
1576 }
1577}
1578
1579#[cfg(test)]
1580mod tests_tanh {
1581 use super::*;
1582 use crate::test_workbook::TestWorkbook;
1583 use crate::traits::ArgumentHandle;
1584 use formualizer_parse::LiteralValue;
1585 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1586 wb.interpreter()
1587 }
1588 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1589 formualizer_parse::parser::ASTNode::new(
1590 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1591 None,
1592 )
1593 }
1594 fn assert_close(a: f64, b: f64) {
1595 assert!((a - b).abs() < 1e-9);
1596 }
1597 #[test]
1598 fn test_tanh_basic() {
1599 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TanhFn));
1600 let ctx = interp(&wb);
1601 let f = ctx.context.get_function("", "TANH").unwrap();
1602 let a0 = make_num_ast(0.5);
1603 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1604 match f
1605 .dispatch(&args, &ctx.function_context(None))
1606 .unwrap()
1607 .into_literal()
1608 {
1609 LiteralValue::Number(n) => assert_close(n, (0.5f64).tanh()),
1610 v => panic!("unexpected {v:?}"),
1611 }
1612 }
1613}
1614
1615#[derive(Debug)]
1616pub struct AsinhFn;
1617impl Function for AsinhFn {
1659 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1660 fn name(&self) -> &'static str {
1661 "ASINH"
1662 }
1663 fn min_args(&self) -> usize {
1664 1
1665 }
1666 fn arg_schema(&self) -> &'static [ArgSchema] {
1667 &ARG_NUM_LENIENT_ONE[..]
1668 }
1669 fn eval<'a, 'b, 'c>(
1670 &self,
1671 args: &'c [ArgumentHandle<'a, 'b>],
1672 _ctx: &dyn FunctionContext<'b>,
1673 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1674 let x = unary_numeric_arg(args)?;
1675 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1676 x.asinh(),
1677 )))
1678 }
1679}
1680
1681#[cfg(test)]
1682mod tests_asinh {
1683 use super::*;
1684 use crate::test_workbook::TestWorkbook;
1685 use crate::traits::ArgumentHandle;
1686 use formualizer_parse::LiteralValue;
1687 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1688 wb.interpreter()
1689 }
1690 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1691 formualizer_parse::parser::ASTNode::new(
1692 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1693 None,
1694 )
1695 }
1696 fn assert_close(a: f64, b: f64) {
1697 assert!((a - b).abs() < 1e-9);
1698 }
1699 #[test]
1700 fn test_asinh_basic() {
1701 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AsinhFn));
1702 let ctx = interp(&wb);
1703 let f = ctx.context.get_function("", "ASINH").unwrap();
1704 let a0 = make_num_ast(1.5);
1705 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1706 match f
1707 .dispatch(&args, &ctx.function_context(None))
1708 .unwrap()
1709 .into_literal()
1710 {
1711 LiteralValue::Number(n) => assert_close(n, (1.5f64).asinh()),
1712 v => panic!("unexpected {v:?}"),
1713 }
1714 }
1715}
1716
1717#[derive(Debug)]
1718pub struct AcoshFn;
1719impl Function for AcoshFn {
1761 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1762 fn name(&self) -> &'static str {
1763 "ACOSH"
1764 }
1765 fn min_args(&self) -> usize {
1766 1
1767 }
1768 fn arg_schema(&self) -> &'static [ArgSchema] {
1769 &ARG_ANY_ONE[..]
1770 }
1771 fn eval<'a, 'b, 'c>(
1772 &self,
1773 args: &'c [ArgumentHandle<'a, 'b>],
1774 _ctx: &dyn FunctionContext<'b>,
1775 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1776 let x = unary_numeric_arg(args)?;
1777 if x < 1.0 {
1778 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1779 ExcelError::new_num(),
1780 )));
1781 }
1782 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1783 x.acosh(),
1784 )))
1785 }
1786}
1787
1788#[cfg(test)]
1789mod tests_acosh {
1790 use super::*;
1791 use crate::test_workbook::TestWorkbook;
1792 use crate::traits::ArgumentHandle;
1793 use formualizer_parse::LiteralValue;
1794 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1795 wb.interpreter()
1796 }
1797 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1798 formualizer_parse::parser::ASTNode::new(
1799 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1800 None,
1801 )
1802 }
1803 #[test]
1804 fn test_acosh_basic_and_domain() {
1805 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcoshFn));
1806 let ctx = interp(&wb);
1807 let f = ctx.context.get_function("", "ACOSH").unwrap();
1808 let a0 = make_num_ast(1.0);
1809 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1810 assert_eq!(
1811 f.dispatch(&args, &ctx.function_context(None))
1812 .unwrap()
1813 .into_literal(),
1814 LiteralValue::Number(0.0)
1815 );
1816 let a1 = make_num_ast(0.5);
1817 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1818 match f
1819 .dispatch(&args2, &ctx.function_context(None))
1820 .unwrap()
1821 .into_literal()
1822 {
1823 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
1824 v => panic!("expected error, got {v:?}"),
1825 }
1826 }
1827}
1828
1829#[derive(Debug)]
1830pub struct AtanhFn;
1831impl Function for AtanhFn {
1873 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1874 fn name(&self) -> &'static str {
1875 "ATANH"
1876 }
1877 fn min_args(&self) -> usize {
1878 1
1879 }
1880 fn arg_schema(&self) -> &'static [ArgSchema] {
1881 &ARG_ANY_ONE[..]
1882 }
1883 fn eval<'a, 'b, 'c>(
1884 &self,
1885 args: &'c [ArgumentHandle<'a, 'b>],
1886 _ctx: &dyn FunctionContext<'b>,
1887 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1888 let x = unary_numeric_arg(args)?;
1889 if x <= -1.0 || x >= 1.0 {
1890 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1891 ExcelError::new_num(),
1892 )));
1893 }
1894 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1895 x.atanh(),
1896 )))
1897 }
1898}
1899
1900#[cfg(test)]
1901mod tests_atanh {
1902 use super::*;
1903 use crate::test_workbook::TestWorkbook;
1904 use crate::traits::ArgumentHandle;
1905 use formualizer_parse::LiteralValue;
1906 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1907 wb.interpreter()
1908 }
1909 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1910 formualizer_parse::parser::ASTNode::new(
1911 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1912 None,
1913 )
1914 }
1915 fn assert_close(a: f64, b: f64) {
1916 assert!((a - b).abs() < 1e-9);
1917 }
1918 #[test]
1919 fn test_atanh_basic_and_domain() {
1920 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AtanhFn));
1921 let ctx = interp(&wb);
1922 let f = ctx.context.get_function("", "ATANH").unwrap();
1923 let a0 = make_num_ast(0.5);
1924 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1925 match f
1926 .dispatch(&args, &ctx.function_context(None))
1927 .unwrap()
1928 .into_literal()
1929 {
1930 LiteralValue::Number(n) => assert_close(n, (0.5f64).atanh()),
1931 v => panic!("unexpected {v:?}"),
1932 }
1933 let a1 = make_num_ast(1.0);
1934 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1935 match f
1936 .dispatch(&args2, &ctx.function_context(None))
1937 .unwrap()
1938 .into_literal()
1939 {
1940 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
1941 v => panic!("expected error, got {v:?}"),
1942 }
1943 }
1944}
1945
1946#[derive(Debug)]
1947pub struct SechFn;
1948impl Function for SechFn {
1990 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1991 fn name(&self) -> &'static str {
1992 "SECH"
1993 }
1994 fn min_args(&self) -> usize {
1995 1
1996 }
1997 fn arg_schema(&self) -> &'static [ArgSchema] {
1998 &ARG_ANY_ONE[..]
1999 }
2000 fn eval<'a, 'b, 'c>(
2001 &self,
2002 args: &'c [ArgumentHandle<'a, 'b>],
2003 _ctx: &dyn FunctionContext<'b>,
2004 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2005 let x = unary_numeric_arg(args)?;
2006 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2007 1.0 / x.cosh(),
2008 )))
2009 }
2010}
2011
2012#[cfg(test)]
2013mod tests_sech {
2014 use super::*;
2015 use crate::test_workbook::TestWorkbook;
2016 use crate::traits::ArgumentHandle;
2017 use formualizer_parse::LiteralValue;
2018 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2019 wb.interpreter()
2020 }
2021 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
2022 formualizer_parse::parser::ASTNode::new(
2023 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
2024 None,
2025 )
2026 }
2027 fn assert_close(a: f64, b: f64) {
2028 assert!((a - b).abs() < 1e-9);
2029 }
2030 #[test]
2031 fn test_sech_basic() {
2032 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SechFn));
2033 let ctx = interp(&wb);
2034 let f = ctx.context.get_function("", "SECH").unwrap();
2035 let a0 = make_num_ast(0.0);
2036 let args = vec![ArgumentHandle::new(&a0, &ctx)];
2037 match f
2038 .dispatch(&args, &ctx.function_context(None))
2039 .unwrap()
2040 .into_literal()
2041 {
2042 LiteralValue::Number(n) => assert_close(n, 1.0),
2043 v => panic!("unexpected {v:?}"),
2044 }
2045 }
2046}
2047
2048#[derive(Debug)]
2049pub struct CschFn;
2050impl Function for CschFn {
2092 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
2093 fn name(&self) -> &'static str {
2094 "CSCH"
2095 }
2096 fn min_args(&self) -> usize {
2097 1
2098 }
2099 fn arg_schema(&self) -> &'static [ArgSchema] {
2100 &ARG_NUM_LENIENT_ONE[..]
2101 }
2102 fn eval<'a, 'b, 'c>(
2103 &self,
2104 args: &'c [ArgumentHandle<'a, 'b>],
2105 _ctx: &dyn FunctionContext<'b>,
2106 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2107 let x = unary_numeric_arg(args)?;
2108 let s = x.sinh(); if s.abs() < EPSILON_NEAR_ZERO {
2110 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2111 ExcelError::from_error_string("#DIV/0!"),
2112 )));
2113 }
2114 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2115 1.0 / s,
2116 )))
2117 }
2118}
2119
2120#[cfg(test)]
2121mod tests_csch {
2122 use super::*;
2123 use crate::test_workbook::TestWorkbook;
2124 use crate::traits::ArgumentHandle;
2125 use formualizer_parse::LiteralValue;
2126 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2127 wb.interpreter()
2128 }
2129 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
2130 formualizer_parse::parser::ASTNode::new(
2131 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
2132 None,
2133 )
2134 }
2135 fn assert_close(a: f64, b: f64) {
2136 assert!((a - b).abs() < 1e-9);
2137 }
2138 #[test]
2139 fn test_csch_basic_and_div0() {
2140 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CschFn));
2141 let ctx = interp(&wb);
2142 let csch = ctx.context.get_function("", "CSCH").unwrap();
2143 let a0 = make_num_ast(1.0);
2145 let args = vec![ArgumentHandle::new(&a0, &ctx)];
2146 match csch
2147 .dispatch(&args, &ctx.function_context(None))
2148 .unwrap()
2149 .into_literal()
2150 {
2151 LiteralValue::Number(n) => assert_close(n, 0.8509181282393216),
2152 v => panic!("unexpected {v:?}"),
2153 }
2154 let a1 = make_num_ast(0.0);
2156 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
2157 match csch
2158 .dispatch(&args2, &ctx.function_context(None))
2159 .unwrap()
2160 .into_literal()
2161 {
2162 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
2163 v => panic!("expected error, got {v:?}"),
2164 }
2165 }
2166}
2167
2168#[derive(Debug)]
2169pub struct CothFn;
2170impl Function for CothFn {
2212 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
2213 fn name(&self) -> &'static str {
2214 "COTH"
2215 }
2216 fn min_args(&self) -> usize {
2217 1
2218 }
2219 fn arg_schema(&self) -> &'static [ArgSchema] {
2220 &ARG_NUM_LENIENT_ONE[..]
2221 }
2222 fn eval<'a, 'b, 'c>(
2223 &self,
2224 args: &'c [ArgumentHandle<'a, 'b>],
2225 _ctx: &dyn FunctionContext<'b>,
2226 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2227 let x = unary_numeric_arg(args)?;
2228 let s = x.sinh();
2229 if s.abs() < EPSILON_NEAR_ZERO {
2230 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2231 ExcelError::from_error_string("#DIV/0!"),
2232 )));
2233 }
2234 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2235 x.cosh() / s,
2236 )))
2237 }
2238}
2239
2240#[cfg(test)]
2241mod tests_coth {
2242 use super::*;
2243 use crate::test_workbook::TestWorkbook;
2244 use crate::traits::ArgumentHandle;
2245 use formualizer_parse::LiteralValue;
2246 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2247 wb.interpreter()
2248 }
2249 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
2250 formualizer_parse::parser::ASTNode::new(
2251 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
2252 None,
2253 )
2254 }
2255 #[test]
2256 fn test_coth_div0() {
2257 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CothFn));
2258 let ctx = interp(&wb);
2259 let f = ctx.context.get_function("", "COTH").unwrap();
2260 let a0 = make_num_ast(0.0);
2261 let args = vec![ArgumentHandle::new(&a0, &ctx)];
2262 match f
2263 .dispatch(&args, &ctx.function_context(None))
2264 .unwrap()
2265 .into_literal()
2266 {
2267 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
2268 v => panic!("expected error, got {v:?}"),
2269 }
2270 }
2271}
2272
2273#[derive(Debug)]
2276pub struct RadiansFn;
2277impl Function for RadiansFn {
2316 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
2317 fn name(&self) -> &'static str {
2318 "RADIANS"
2319 }
2320 fn min_args(&self) -> usize {
2321 1
2322 }
2323 fn arg_schema(&self) -> &'static [ArgSchema] {
2324 &ARG_ANY_ONE[..]
2325 }
2326 fn eval<'a, 'b, 'c>(
2327 &self,
2328 args: &'c [ArgumentHandle<'a, 'b>],
2329 _ctx: &dyn FunctionContext<'b>,
2330 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2331 let deg = unary_numeric_arg(args)?;
2332 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2333 deg * PI / 180.0,
2334 )))
2335 }
2336}
2337
2338#[cfg(test)]
2339mod tests_radians {
2340 use super::*;
2341 use crate::test_workbook::TestWorkbook;
2342 use crate::traits::ArgumentHandle;
2343 use formualizer_parse::LiteralValue;
2344 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2345 wb.interpreter()
2346 }
2347 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
2348 formualizer_parse::parser::ASTNode::new(
2349 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
2350 None,
2351 )
2352 }
2353 fn assert_close(a: f64, b: f64) {
2354 assert!((a - b).abs() < 1e-9);
2355 }
2356 #[test]
2357 fn test_radians_basic() {
2358 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RadiansFn));
2359 let ctx = interp(&wb);
2360 let f = ctx.context.get_function("", "RADIANS").unwrap();
2361 let a0 = make_num_ast(180.0);
2362 let args = vec![ArgumentHandle::new(&a0, &ctx)];
2363 match f
2364 .dispatch(&args, &ctx.function_context(None))
2365 .unwrap()
2366 .into_literal()
2367 {
2368 LiteralValue::Number(n) => assert_close(n, PI),
2369 v => panic!("unexpected {v:?}"),
2370 }
2371 }
2372}
2373
2374#[derive(Debug)]
2375pub struct DegreesFn;
2376impl Function for DegreesFn {
2415 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
2416 fn name(&self) -> &'static str {
2417 "DEGREES"
2418 }
2419 fn min_args(&self) -> usize {
2420 1
2421 }
2422 fn arg_schema(&self) -> &'static [ArgSchema] {
2423 &ARG_ANY_ONE[..]
2424 }
2425 fn eval<'a, 'b, 'c>(
2426 &self,
2427 args: &'c [ArgumentHandle<'a, 'b>],
2428 _ctx: &dyn FunctionContext<'b>,
2429 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2430 let rad = unary_numeric_arg(args)?;
2431 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2432 rad * 180.0 / PI,
2433 )))
2434 }
2435}
2436
2437#[cfg(test)]
2438mod tests_degrees {
2439 use super::*;
2440 use crate::test_workbook::TestWorkbook;
2441 use crate::traits::ArgumentHandle;
2442 use formualizer_parse::LiteralValue;
2443 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2444 wb.interpreter()
2445 }
2446 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
2447 formualizer_parse::parser::ASTNode::new(
2448 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
2449 None,
2450 )
2451 }
2452 fn assert_close(a: f64, b: f64) {
2453 assert!((a - b).abs() < 1e-9);
2454 }
2455 #[test]
2456 fn test_degrees_basic() {
2457 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(DegreesFn));
2458 let ctx = interp(&wb);
2459 let f = ctx.context.get_function("", "DEGREES").unwrap();
2460 let a0 = make_num_ast(PI);
2461 let args = vec![ArgumentHandle::new(&a0, &ctx)];
2462 match f
2463 .dispatch(&args, &ctx.function_context(None))
2464 .unwrap()
2465 .into_literal()
2466 {
2467 LiteralValue::Number(n) => assert_close(n, 180.0),
2468 v => panic!("unexpected {v:?}"),
2469 }
2470 }
2471}
2472
2473#[derive(Debug)]
2474pub struct PiFn;
2475impl Function for PiFn {
2514 func_caps!(PURE);
2515 fn name(&self) -> &'static str {
2516 "PI"
2517 }
2518 fn min_args(&self) -> usize {
2519 0
2520 }
2521 fn eval<'a, 'b, 'c>(
2522 &self,
2523 _args: &'c [ArgumentHandle<'a, 'b>],
2524 _ctx: &dyn FunctionContext<'b>,
2525 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2526 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(PI)))
2527 }
2528}
2529
2530#[cfg(test)]
2531mod tests_pi {
2532 use super::*;
2533 use crate::test_workbook::TestWorkbook;
2534 use formualizer_parse::LiteralValue;
2535 #[test]
2536 fn test_pi_basic() {
2537 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PiFn));
2538 let ctx = wb.interpreter();
2539 let f = ctx.context.get_function("", "PI").unwrap();
2540 assert_eq!(
2541 f.eval(&[], &ctx.function_context(None))
2542 .unwrap()
2543 .into_literal(),
2544 LiteralValue::Number(PI)
2545 );
2546 }
2547}
2548
2549pub fn register_builtins() {
2550 crate::function_registry::register_function(std::sync::Arc::new(SinFn));
2552 crate::function_registry::register_function(std::sync::Arc::new(CosFn));
2553 crate::function_registry::register_function(std::sync::Arc::new(TanFn));
2554 crate::function_registry::register_function(std::sync::Arc::new(AsinFn));
2556 crate::function_registry::register_function(std::sync::Arc::new(AcosFn));
2557 crate::function_registry::register_function(std::sync::Arc::new(AtanFn));
2558 crate::function_registry::register_function(std::sync::Arc::new(Atan2Fn));
2559 crate::function_registry::register_function(std::sync::Arc::new(SecFn));
2560 crate::function_registry::register_function(std::sync::Arc::new(CscFn));
2561 crate::function_registry::register_function(std::sync::Arc::new(CotFn));
2562 crate::function_registry::register_function(std::sync::Arc::new(AcotFn));
2563
2564 crate::function_registry::register_function(std::sync::Arc::new(SinhFn));
2566 crate::function_registry::register_function(std::sync::Arc::new(CoshFn));
2567 crate::function_registry::register_function(std::sync::Arc::new(TanhFn));
2568 crate::function_registry::register_function(std::sync::Arc::new(AsinhFn));
2569 crate::function_registry::register_function(std::sync::Arc::new(AcoshFn));
2570 crate::function_registry::register_function(std::sync::Arc::new(AtanhFn));
2571 crate::function_registry::register_function(std::sync::Arc::new(SechFn));
2572 crate::function_registry::register_function(std::sync::Arc::new(CschFn));
2573 crate::function_registry::register_function(std::sync::Arc::new(CothFn));
2574
2575 crate::function_registry::register_function(std::sync::Arc::new(RadiansFn));
2577 crate::function_registry::register_function(std::sync::Arc::new(DegreesFn));
2578 crate::function_registry::register_function(std::sync::Arc::new(PiFn));
2579}