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 {
17 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
18 fn name(&self) -> &'static str {
19 "SIN"
20 }
21 fn min_args(&self) -> usize {
22 1
23 }
24 fn arg_schema(&self) -> &'static [ArgSchema] {
25 &ARG_NUM_LENIENT_ONE[..]
26 }
27 fn eval<'a, 'b, 'c>(
28 &self,
29 args: &'c [ArgumentHandle<'a, 'b>],
30 ctx: &dyn FunctionContext<'b>,
31 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
32 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.sin())))
33 }
34}
35
36#[cfg(test)]
37mod tests_sin {
38 use super::*;
39 use crate::test_workbook::TestWorkbook;
40 use crate::traits::ArgumentHandle;
41 use formualizer_parse::LiteralValue;
42
43 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
44 wb.interpreter()
45 }
46 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
47 formualizer_parse::parser::ASTNode::new(
48 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
49 None,
50 )
51 }
52 fn assert_close(a: f64, b: f64) {
53 assert!((a - b).abs() < 1e-9, "{a} !~= {b}");
54 }
55
56 #[test]
57 fn test_sin_basic() {
58 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinFn));
59 let ctx = interp(&wb);
60 let sin = ctx.context.get_function("", "SIN").unwrap();
61 let a0 = make_num_ast(PI / 2.0);
62 let args = vec![ArgumentHandle::new(&a0, &ctx)];
63 match sin
64 .dispatch(&args, &ctx.function_context(None))
65 .unwrap()
66 .into_literal()
67 {
68 LiteralValue::Number(n) => assert_close(n, 1.0),
69 v => panic!("unexpected {v:?}"),
70 }
71 }
72
73 #[test]
74 fn test_sin_array_literal() {
75 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinFn));
76 let ctx = interp(&wb);
77 let sin = ctx.context.get_function("", "SIN").unwrap();
78 let arr = formualizer_parse::parser::ASTNode::new(
79 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Array(vec![vec![
80 LiteralValue::Number(0.0),
81 LiteralValue::Number(PI / 2.0),
82 ]])),
83 None,
84 );
85 let args = vec![ArgumentHandle::new(&arr, &ctx)];
86
87 match sin
88 .dispatch(&args, &ctx.function_context(None))
89 .unwrap()
90 .into_literal()
91 {
92 formualizer_common::LiteralValue::Array(rows) => {
93 assert_eq!(rows.len(), 1);
94 assert_eq!(rows[0].len(), 2);
95 match (&rows[0][0], &rows[0][1]) {
96 (
97 formualizer_common::LiteralValue::Number(a),
98 formualizer_common::LiteralValue::Number(b),
99 ) => {
100 assert_close(*a, 0.0);
101 assert_close(*b, 1.0);
102 }
103 other => panic!("unexpected {other:?}"),
104 }
105 }
106 other => panic!("expected array, got {other:?}"),
107 }
108 }
109}
110
111#[derive(Debug)]
112pub struct CosFn;
113impl Function for CosFn {
114 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
115 fn name(&self) -> &'static str {
116 "COS"
117 }
118 fn min_args(&self) -> usize {
119 1
120 }
121 fn arg_schema(&self) -> &'static [ArgSchema] {
122 &ARG_NUM_LENIENT_ONE[..]
123 }
124 fn eval<'a, 'b, 'c>(
125 &self,
126 args: &'c [ArgumentHandle<'a, 'b>],
127 ctx: &dyn FunctionContext<'b>,
128 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
129 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.cos())))
130 }
131}
132
133#[cfg(test)]
134mod tests_cos {
135 use super::*;
136 use crate::test_workbook::TestWorkbook;
137 use crate::traits::ArgumentHandle;
138 use formualizer_parse::LiteralValue;
139 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
140 wb.interpreter()
141 }
142 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
143 formualizer_parse::parser::ASTNode::new(
144 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
145 None,
146 )
147 }
148 fn assert_close(a: f64, b: f64) {
149 assert!((a - b).abs() < 1e-9);
150 }
151 #[test]
152 fn test_cos_basic() {
153 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CosFn));
154 let ctx = interp(&wb);
155 let cos = ctx.context.get_function("", "COS").unwrap();
156 let a0 = make_num_ast(0.0);
157 let args = vec![ArgumentHandle::new(&a0, &ctx)];
158 match cos
159 .dispatch(&args, &ctx.function_context(None))
160 .unwrap()
161 .into_literal()
162 {
163 LiteralValue::Number(n) => assert_close(n, 1.0),
164 v => panic!("unexpected {v:?}"),
165 }
166 }
167}
168
169#[derive(Debug)]
170pub struct TanFn;
171impl Function for TanFn {
172 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
173 fn name(&self) -> &'static str {
174 "TAN"
175 }
176 fn min_args(&self) -> usize {
177 1
178 }
179 fn arg_schema(&self) -> &'static [ArgSchema] {
180 &ARG_NUM_LENIENT_ONE[..]
181 }
182 fn eval<'a, 'b, 'c>(
183 &self,
184 args: &'c [ArgumentHandle<'a, 'b>],
185 ctx: &dyn FunctionContext<'b>,
186 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
187 unary_numeric_elementwise(args, ctx, |x| Ok(LiteralValue::Number(x.tan())))
188 }
189}
190
191#[cfg(test)]
192mod tests_tan {
193 use super::*;
194 use crate::test_workbook::TestWorkbook;
195 use crate::traits::ArgumentHandle;
196 use formualizer_parse::LiteralValue;
197 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
198 wb.interpreter()
199 }
200 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
201 formualizer_parse::parser::ASTNode::new(
202 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
203 None,
204 )
205 }
206 fn assert_close(a: f64, b: f64) {
207 assert!((a - b).abs() < 1e-9);
208 }
209 #[test]
210 fn test_tan_basic() {
211 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TanFn));
212 let ctx = interp(&wb);
213 let tan = ctx.context.get_function("", "TAN").unwrap();
214 let a0 = make_num_ast(PI / 4.0);
215 let args = vec![ArgumentHandle::new(&a0, &ctx)];
216 match tan
217 .dispatch(&args, &ctx.function_context(None))
218 .unwrap()
219 .into_literal()
220 {
221 LiteralValue::Number(n) => assert_close(n, 1.0),
222 v => panic!("unexpected {v:?}"),
223 }
224 }
225}
226
227#[derive(Debug)]
228pub struct AsinFn;
229impl Function for AsinFn {
230 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
231 fn name(&self) -> &'static str {
232 "ASIN"
233 }
234 fn min_args(&self) -> usize {
235 1
236 }
237 fn arg_schema(&self) -> &'static [ArgSchema] {
238 &ARG_NUM_LENIENT_ONE[..]
239 }
240 fn eval<'a, 'b, 'c>(
241 &self,
242 args: &'c [ArgumentHandle<'a, 'b>],
243 _ctx: &dyn FunctionContext<'b>,
244 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
245 let x = unary_numeric_arg(args)?;
246 if !(-1.0..=1.0).contains(&x) {
247 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
248 ExcelError::new_num(),
249 )));
250 }
251 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
252 x.asin(),
253 )))
254 }
255}
256
257#[cfg(test)]
258mod tests_asin {
259 use super::*;
260 use crate::test_workbook::TestWorkbook;
261 use crate::traits::ArgumentHandle;
262 use formualizer_parse::LiteralValue;
263 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
264 wb.interpreter()
265 }
266 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
267 formualizer_parse::parser::ASTNode::new(
268 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
269 None,
270 )
271 }
272 fn assert_close(a: f64, b: f64) {
273 assert!((a - b).abs() < 1e-9);
274 }
275 #[test]
276 fn test_asin_basic_and_domain() {
277 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AsinFn));
278 let ctx = interp(&wb);
279 let asin = ctx.context.get_function("", "ASIN").unwrap();
280 let a0 = make_num_ast(0.5);
282 let args = vec![ArgumentHandle::new(&a0, &ctx)];
283 match asin
284 .dispatch(&args, &ctx.function_context(None))
285 .unwrap()
286 .into_literal()
287 {
288 LiteralValue::Number(n) => assert_close(n, (0.5f64).asin()),
289 v => panic!("unexpected {v:?}"),
290 }
291 let a1 = make_num_ast(2.0);
293 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
294 match asin
295 .dispatch(&args2, &ctx.function_context(None))
296 .unwrap()
297 .into_literal()
298 {
299 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
300 v => panic!("expected error, got {v:?}"),
301 }
302 }
303}
304
305#[derive(Debug)]
306pub struct AcosFn;
307impl Function for AcosFn {
308 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
309 fn name(&self) -> &'static str {
310 "ACOS"
311 }
312 fn min_args(&self) -> usize {
313 1
314 }
315 fn arg_schema(&self) -> &'static [ArgSchema] {
316 &ARG_NUM_LENIENT_ONE[..]
317 }
318 fn eval<'a, 'b, 'c>(
319 &self,
320 args: &'c [ArgumentHandle<'a, 'b>],
321 _ctx: &dyn FunctionContext<'b>,
322 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
323 let x = unary_numeric_arg(args)?;
324 if !(-1.0..=1.0).contains(&x) {
325 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
326 ExcelError::new_num(),
327 )));
328 }
329 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
330 x.acos(),
331 )))
332 }
333}
334
335#[cfg(test)]
336mod tests_acos {
337 use super::*;
338 use crate::test_workbook::TestWorkbook;
339 use crate::traits::ArgumentHandle;
340 use formualizer_parse::LiteralValue;
341 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
342 wb.interpreter()
343 }
344 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
345 formualizer_parse::parser::ASTNode::new(
346 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
347 None,
348 )
349 }
350 fn assert_close(a: f64, b: f64) {
351 assert!((a - b).abs() < 1e-9);
352 }
353 #[test]
354 fn test_acos_basic_and_domain() {
355 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcosFn));
356 let ctx = interp(&wb);
357 let acos = ctx.context.get_function("", "ACOS").unwrap();
358 let a0 = make_num_ast(0.5);
359 let args = vec![ArgumentHandle::new(&a0, &ctx)];
360 match acos
361 .dispatch(&args, &ctx.function_context(None))
362 .unwrap()
363 .into_literal()
364 {
365 LiteralValue::Number(n) => assert_close(n, (0.5f64).acos()),
366 v => panic!("unexpected {v:?}"),
367 }
368 let a1 = make_num_ast(-2.0);
369 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
370 match acos
371 .dispatch(&args2, &ctx.function_context(None))
372 .unwrap()
373 .into_literal()
374 {
375 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
376 v => panic!("expected error, got {v:?}"),
377 }
378 }
379}
380
381#[derive(Debug)]
382pub struct AtanFn;
383impl Function for AtanFn {
384 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
385 fn name(&self) -> &'static str {
386 "ATAN"
387 }
388 fn min_args(&self) -> usize {
389 1
390 }
391 fn arg_schema(&self) -> &'static [ArgSchema] {
392 &ARG_NUM_LENIENT_ONE[..]
393 }
394 fn eval<'a, 'b, 'c>(
395 &self,
396 args: &'c [ArgumentHandle<'a, 'b>],
397 _ctx: &dyn FunctionContext<'b>,
398 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
399 let x = unary_numeric_arg(args)?;
400 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
401 x.atan(),
402 )))
403 }
404}
405
406#[cfg(test)]
407mod tests_atan {
408 use super::*;
409 use crate::test_workbook::TestWorkbook;
410 use crate::traits::ArgumentHandle;
411 use formualizer_parse::LiteralValue;
412 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
413 wb.interpreter()
414 }
415 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
416 formualizer_parse::parser::ASTNode::new(
417 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
418 None,
419 )
420 }
421 fn assert_close(a: f64, b: f64) {
422 assert!((a - b).abs() < 1e-9);
423 }
424 #[test]
425 fn test_atan_basic() {
426 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AtanFn));
427 let ctx = interp(&wb);
428 let atan = ctx.context.get_function("", "ATAN").unwrap();
429 let a0 = make_num_ast(1.0);
430 let args = vec![ArgumentHandle::new(&a0, &ctx)];
431 match atan
432 .dispatch(&args, &ctx.function_context(None))
433 .unwrap()
434 .into_literal()
435 {
436 LiteralValue::Number(n) => assert_close(n, (1.0f64).atan()),
437 v => panic!("unexpected {v:?}"),
438 }
439 }
440}
441
442#[derive(Debug)]
443pub struct Atan2Fn;
444impl Function for Atan2Fn {
445 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
446 fn name(&self) -> &'static str {
447 "ATAN2"
448 }
449 fn min_args(&self) -> usize {
450 2
451 }
452 fn arg_schema(&self) -> &'static [ArgSchema] {
453 &ARG_NUM_LENIENT_TWO[..]
454 }
455 fn eval<'a, 'b, 'c>(
456 &self,
457 args: &'c [ArgumentHandle<'a, 'b>],
458 ctx: &dyn FunctionContext<'b>,
459 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
460 binary_numeric_elementwise(args, ctx, |x, y| {
462 if x == 0.0 && y == 0.0 {
463 Ok(LiteralValue::Error(ExcelError::from_error_string(
464 "#DIV/0!",
465 )))
466 } else {
467 Ok(LiteralValue::Number(y.atan2(x)))
468 }
469 })
470 }
471}
472
473#[cfg(test)]
474mod tests_atan2 {
475 use super::*;
476 use crate::test_workbook::TestWorkbook;
477 use crate::traits::ArgumentHandle;
478 use formualizer_parse::LiteralValue;
479 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
480 wb.interpreter()
481 }
482 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
483 formualizer_parse::parser::ASTNode::new(
484 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
485 None,
486 )
487 }
488 fn assert_close(a: f64, b: f64) {
489 assert!((a - b).abs() < 1e-9);
490 }
491 #[test]
492 fn test_atan2_basic_and_zero_zero() {
493 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(Atan2Fn));
494 let ctx = interp(&wb);
495 let atan2 = ctx.context.get_function("", "ATAN2").unwrap();
496 let a0 = make_num_ast(1.0);
498 let a1 = make_num_ast(1.0);
499 let args = vec![
500 ArgumentHandle::new(&a0, &ctx),
501 ArgumentHandle::new(&a1, &ctx),
502 ];
503 match atan2
504 .dispatch(&args, &ctx.function_context(None))
505 .unwrap()
506 .into_literal()
507 {
508 LiteralValue::Number(n) => assert_close(n, PI / 4.0),
509 v => panic!("unexpected {v:?}"),
510 }
511 let b0 = make_num_ast(0.0);
513 let b1 = make_num_ast(0.0);
514 let args2 = vec![
515 ArgumentHandle::new(&b0, &ctx),
516 ArgumentHandle::new(&b1, &ctx),
517 ];
518 match atan2
519 .dispatch(&args2, &ctx.function_context(None))
520 .unwrap()
521 .into_literal()
522 {
523 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
524 v => panic!("expected error, got {v:?}"),
525 }
526 }
527
528 #[test]
529 fn test_atan2_broadcast_scalar_over_array() {
530 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(Atan2Fn));
531 let ctx = interp(&wb);
532 let atan2 = ctx.context.get_function("", "ATAN2").unwrap();
533
534 let x = make_num_ast(1.0);
536 let y = formualizer_parse::parser::ASTNode::new(
537 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Array(vec![vec![
538 LiteralValue::Number(0.0),
539 LiteralValue::Number(1.0),
540 ]])),
541 None,
542 );
543 let args = vec![ArgumentHandle::new(&x, &ctx), ArgumentHandle::new(&y, &ctx)];
544
545 match atan2
546 .dispatch(&args, &ctx.function_context(None))
547 .unwrap()
548 .into_literal()
549 {
550 formualizer_common::LiteralValue::Array(rows) => {
551 assert_eq!(rows.len(), 1);
552 assert_eq!(rows[0].len(), 2);
553 match (&rows[0][0], &rows[0][1]) {
554 (
555 formualizer_common::LiteralValue::Number(a),
556 formualizer_common::LiteralValue::Number(b),
557 ) => {
558 assert_close(*a, 0.0);
559 assert_close(*b, PI / 4.0);
560 }
561 other => panic!("unexpected {other:?}"),
562 }
563 }
564 other => panic!("expected array, got {other:?}"),
565 }
566 }
567}
568
569#[derive(Debug)]
570pub struct SecFn;
571impl Function for SecFn {
572 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
573 fn name(&self) -> &'static str {
574 "SEC"
575 }
576 fn min_args(&self) -> usize {
577 1
578 }
579 fn arg_schema(&self) -> &'static [ArgSchema] {
580 &ARG_NUM_LENIENT_ONE[..]
581 }
582 fn eval<'a, 'b, 'c>(
583 &self,
584 args: &'c [ArgumentHandle<'a, 'b>],
585 _ctx: &dyn FunctionContext<'b>,
586 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
587 let x = unary_numeric_arg(args)?;
588 let c = x.cos();
589 if c.abs() < EPSILON_NEAR_ZERO {
590 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
591 ExcelError::from_error_string("#DIV/0!"),
592 )));
593 }
594 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
595 1.0 / c,
596 )))
597 }
598}
599
600#[cfg(test)]
601mod tests_sec {
602 use super::*;
603 use crate::test_workbook::TestWorkbook;
604 use crate::traits::ArgumentHandle;
605 use formualizer_parse::LiteralValue;
606 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
607 wb.interpreter()
608 }
609 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
610 formualizer_parse::parser::ASTNode::new(
611 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
612 None,
613 )
614 }
615 fn assert_close(a: f64, b: f64) {
616 assert!((a - b).abs() < 1e-9);
617 }
618 #[test]
619 fn test_sec_basic_and_div0() {
620 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SecFn));
621 let ctx = interp(&wb);
622 let sec = ctx.context.get_function("", "SEC").unwrap();
623 let a0 = make_num_ast(0.0);
624 let args = vec![ArgumentHandle::new(&a0, &ctx)];
625 match sec
626 .dispatch(&args, &ctx.function_context(None))
627 .unwrap()
628 .into_literal()
629 {
630 LiteralValue::Number(n) => assert_close(n, 1.0),
631 v => panic!("unexpected {v:?}"),
632 }
633 let a1 = make_num_ast(PI / 2.0);
634 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
635 match sec
636 .dispatch(&args2, &ctx.function_context(None))
637 .unwrap()
638 .into_literal()
639 {
640 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
641 LiteralValue::Number(n) => assert!(n.abs() > 1e12), v => panic!("unexpected {v:?}"),
643 }
644 }
645}
646
647#[derive(Debug)]
648pub struct CscFn;
649impl Function for CscFn {
650 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
651 fn name(&self) -> &'static str {
652 "CSC"
653 }
654 fn min_args(&self) -> usize {
655 1
656 }
657 fn arg_schema(&self) -> &'static [ArgSchema] {
658 &ARG_NUM_LENIENT_ONE[..]
659 }
660 fn eval<'a, 'b, 'c>(
661 &self,
662 args: &'c [ArgumentHandle<'a, 'b>],
663 _ctx: &dyn FunctionContext<'b>,
664 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
665 let x = unary_numeric_arg(args)?;
666 let s = x.sin();
667 if s.abs() < EPSILON_NEAR_ZERO {
668 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
669 ExcelError::from_error_string("#DIV/0!"),
670 )));
671 }
672 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
673 1.0 / s,
674 )))
675 }
676}
677
678#[cfg(test)]
679mod tests_csc {
680 use super::*;
681 use crate::test_workbook::TestWorkbook;
682 use crate::traits::ArgumentHandle;
683 use formualizer_parse::LiteralValue;
684 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
685 wb.interpreter()
686 }
687 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
688 formualizer_parse::parser::ASTNode::new(
689 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
690 None,
691 )
692 }
693 fn assert_close(a: f64, b: f64) {
694 assert!((a - b).abs() < 1e-9);
695 }
696 #[test]
697 fn test_csc_basic_and_div0() {
698 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CscFn));
699 let ctx = interp(&wb);
700 let csc = ctx.context.get_function("", "CSC").unwrap();
701 let a0 = make_num_ast(PI / 2.0);
702 let args = vec![ArgumentHandle::new(&a0, &ctx)];
703 match csc
704 .dispatch(&args, &ctx.function_context(None))
705 .unwrap()
706 .into_literal()
707 {
708 LiteralValue::Number(n) => assert_close(n, 1.0),
709 v => panic!("unexpected {v:?}"),
710 }
711 let a1 = make_num_ast(0.0);
712 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
713 match csc
714 .dispatch(&args2, &ctx.function_context(None))
715 .unwrap()
716 .into_literal()
717 {
718 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
719 v => panic!("expected error, got {v:?}"),
720 }
721 }
722}
723
724#[derive(Debug)]
725pub struct CotFn;
726impl Function for CotFn {
727 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
728 fn name(&self) -> &'static str {
729 "COT"
730 }
731 fn min_args(&self) -> usize {
732 1
733 }
734 fn arg_schema(&self) -> &'static [ArgSchema] {
735 &ARG_NUM_LENIENT_ONE[..]
736 }
737 fn eval<'a, 'b, 'c>(
738 &self,
739 args: &'c [ArgumentHandle<'a, 'b>],
740 _ctx: &dyn FunctionContext<'b>,
741 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
742 let x = unary_numeric_arg(args)?;
743 let t = x.tan();
744 if t.abs() < EPSILON_NEAR_ZERO {
745 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
746 ExcelError::from_error_string("#DIV/0!"),
747 )));
748 }
749 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
750 1.0 / t,
751 )))
752 }
753}
754
755#[cfg(test)]
756mod tests_cot {
757 use super::*;
758 use crate::test_workbook::TestWorkbook;
759 use crate::traits::ArgumentHandle;
760 use formualizer_parse::LiteralValue;
761 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
762 wb.interpreter()
763 }
764 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
765 formualizer_parse::parser::ASTNode::new(
766 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
767 None,
768 )
769 }
770 fn assert_close(a: f64, b: f64) {
771 assert!((a - b).abs() < 1e-9);
772 }
773 #[test]
774 fn test_cot_basic_and_div0() {
775 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CotFn));
776 let ctx = interp(&wb);
777 let cot = ctx.context.get_function("", "COT").unwrap();
778 let a0 = make_num_ast(PI / 4.0);
779 let args = vec![ArgumentHandle::new(&a0, &ctx)];
780 match cot
781 .dispatch(&args, &ctx.function_context(None))
782 .unwrap()
783 .into_literal()
784 {
785 LiteralValue::Number(n) => assert_close(n, 1.0),
786 v => panic!("unexpected {v:?}"),
787 }
788 let a1 = make_num_ast(0.0);
789 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
790 match cot
791 .dispatch(&args2, &ctx.function_context(None))
792 .unwrap()
793 .into_literal()
794 {
795 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
796 v => panic!("expected error, got {v:?}"),
797 }
798 }
799}
800
801#[derive(Debug)]
802pub struct AcotFn;
803impl Function for AcotFn {
804 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
805 fn name(&self) -> &'static str {
806 "ACOT"
807 }
808 fn min_args(&self) -> usize {
809 1
810 }
811 fn arg_schema(&self) -> &'static [ArgSchema] {
812 &ARG_NUM_LENIENT_ONE[..]
813 }
814 fn eval<'a, 'b, 'c>(
815 &self,
816 args: &'c [ArgumentHandle<'a, 'b>],
817 _ctx: &dyn FunctionContext<'b>,
818 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
819 let x = unary_numeric_arg(args)?;
820 let result = if x == 0.0 {
821 PI / 2.0
822 } else if x > 0.0 {
823 (1.0 / x).atan()
824 } else {
825 (1.0 / x).atan() + PI
826 };
827 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
828 result,
829 )))
830 }
831}
832
833#[cfg(test)]
834mod tests_acot {
835 use super::*;
836 use crate::test_workbook::TestWorkbook;
837 use crate::traits::ArgumentHandle;
838 use formualizer_parse::LiteralValue;
839 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
840 wb.interpreter()
841 }
842 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
843 formualizer_parse::parser::ASTNode::new(
844 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
845 None,
846 )
847 }
848 fn assert_close(a: f64, b: f64) {
849 assert!((a - b).abs() < 1e-9);
850 }
851 #[test]
852 fn test_acot_basic() {
853 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcotFn));
854 let ctx = interp(&wb);
855 let acot = ctx.context.get_function("", "ACOT").unwrap();
856 let a0 = make_num_ast(2.0);
857 let args = vec![ArgumentHandle::new(&a0, &ctx)];
858 match acot
859 .dispatch(&args, &ctx.function_context(None))
860 .unwrap()
861 .into_literal()
862 {
863 LiteralValue::Number(n) => assert_close(n, 0.4636476090008061),
864 v => panic!("unexpected {v:?}"),
865 }
866 }
867}
868
869#[derive(Debug)]
872pub struct SinhFn;
873impl Function for SinhFn {
874 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
875 fn name(&self) -> &'static str {
876 "SINH"
877 }
878 fn min_args(&self) -> usize {
879 1
880 }
881 fn arg_schema(&self) -> &'static [ArgSchema] {
882 &ARG_NUM_LENIENT_ONE[..]
883 }
884 fn eval<'a, 'b, 'c>(
885 &self,
886 args: &'c [ArgumentHandle<'a, 'b>],
887 _ctx: &dyn FunctionContext<'b>,
888 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
889 let x = unary_numeric_arg(args)?;
890 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
891 x.sinh(),
892 )))
893 }
894}
895
896#[cfg(test)]
897mod tests_sinh {
898 use super::*;
899 use crate::test_workbook::TestWorkbook;
900 use crate::traits::ArgumentHandle;
901 use formualizer_parse::LiteralValue;
902 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
903 wb.interpreter()
904 }
905 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
906 formualizer_parse::parser::ASTNode::new(
907 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
908 None,
909 )
910 }
911 fn assert_close(a: f64, b: f64) {
912 assert!((a - b).abs() < 1e-9);
913 }
914 #[test]
915 fn test_sinh_basic() {
916 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SinhFn));
917 let ctx = interp(&wb);
918 let f = ctx.context.get_function("", "SINH").unwrap();
919 let a0 = make_num_ast(1.0);
920 let args = vec![ArgumentHandle::new(&a0, &ctx)];
921 let fctx = ctx.function_context(None);
922 match f.dispatch(&args, &fctx).unwrap().into_literal() {
923 LiteralValue::Number(n) => assert_close(n, (1.0f64).sinh()),
924 v => panic!("unexpected {v:?}"),
925 }
926 }
927}
928
929#[derive(Debug)]
930pub struct CoshFn;
931impl Function for CoshFn {
932 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
933 fn name(&self) -> &'static str {
934 "COSH"
935 }
936 fn min_args(&self) -> usize {
937 1
938 }
939 fn arg_schema(&self) -> &'static [ArgSchema] {
940 &ARG_NUM_LENIENT_ONE[..]
941 }
942 fn eval<'a, 'b, 'c>(
943 &self,
944 args: &'c [ArgumentHandle<'a, 'b>],
945 _ctx: &dyn FunctionContext<'b>,
946 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
947 let x = unary_numeric_arg(args)?;
948 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
949 x.cosh(),
950 )))
951 }
952}
953
954#[cfg(test)]
955mod tests_cosh {
956 use super::*;
957 use crate::test_workbook::TestWorkbook;
958 use crate::traits::ArgumentHandle;
959 use formualizer_parse::LiteralValue;
960 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
961 wb.interpreter()
962 }
963 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
964 formualizer_parse::parser::ASTNode::new(
965 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
966 None,
967 )
968 }
969 fn assert_close(a: f64, b: f64) {
970 assert!((a - b).abs() < 1e-9);
971 }
972 #[test]
973 fn test_cosh_basic() {
974 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CoshFn));
975 let ctx = interp(&wb);
976 let f = ctx.context.get_function("", "COSH").unwrap();
977 let a0 = make_num_ast(1.0);
978 let args = vec![ArgumentHandle::new(&a0, &ctx)];
979 match f
980 .dispatch(&args, &ctx.function_context(None))
981 .unwrap()
982 .into_literal()
983 {
984 LiteralValue::Number(n) => assert_close(n, (1.0f64).cosh()),
985 v => panic!("unexpected {v:?}"),
986 }
987 }
988}
989
990#[derive(Debug)]
991pub struct TanhFn;
992impl Function for TanhFn {
993 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
994 fn name(&self) -> &'static str {
995 "TANH"
996 }
997 fn min_args(&self) -> usize {
998 1
999 }
1000 fn arg_schema(&self) -> &'static [ArgSchema] {
1001 &ARG_NUM_LENIENT_ONE[..]
1002 }
1003 fn eval<'a, 'b, 'c>(
1004 &self,
1005 args: &'c [ArgumentHandle<'a, 'b>],
1006 _ctx: &dyn FunctionContext<'b>,
1007 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1008 let x = unary_numeric_arg(args)?;
1009 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1010 x.tanh(),
1011 )))
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests_tanh {
1017 use super::*;
1018 use crate::test_workbook::TestWorkbook;
1019 use crate::traits::ArgumentHandle;
1020 use formualizer_parse::LiteralValue;
1021 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1022 wb.interpreter()
1023 }
1024 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1025 formualizer_parse::parser::ASTNode::new(
1026 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1027 None,
1028 )
1029 }
1030 fn assert_close(a: f64, b: f64) {
1031 assert!((a - b).abs() < 1e-9);
1032 }
1033 #[test]
1034 fn test_tanh_basic() {
1035 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TanhFn));
1036 let ctx = interp(&wb);
1037 let f = ctx.context.get_function("", "TANH").unwrap();
1038 let a0 = make_num_ast(0.5);
1039 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1040 match f
1041 .dispatch(&args, &ctx.function_context(None))
1042 .unwrap()
1043 .into_literal()
1044 {
1045 LiteralValue::Number(n) => assert_close(n, (0.5f64).tanh()),
1046 v => panic!("unexpected {v:?}"),
1047 }
1048 }
1049}
1050
1051#[derive(Debug)]
1052pub struct AsinhFn;
1053impl Function for AsinhFn {
1054 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1055 fn name(&self) -> &'static str {
1056 "ASINH"
1057 }
1058 fn min_args(&self) -> usize {
1059 1
1060 }
1061 fn arg_schema(&self) -> &'static [ArgSchema] {
1062 &ARG_NUM_LENIENT_ONE[..]
1063 }
1064 fn eval<'a, 'b, 'c>(
1065 &self,
1066 args: &'c [ArgumentHandle<'a, 'b>],
1067 _ctx: &dyn FunctionContext<'b>,
1068 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1069 let x = unary_numeric_arg(args)?;
1070 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1071 x.asinh(),
1072 )))
1073 }
1074}
1075
1076#[cfg(test)]
1077mod tests_asinh {
1078 use super::*;
1079 use crate::test_workbook::TestWorkbook;
1080 use crate::traits::ArgumentHandle;
1081 use formualizer_parse::LiteralValue;
1082 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1083 wb.interpreter()
1084 }
1085 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1086 formualizer_parse::parser::ASTNode::new(
1087 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1088 None,
1089 )
1090 }
1091 fn assert_close(a: f64, b: f64) {
1092 assert!((a - b).abs() < 1e-9);
1093 }
1094 #[test]
1095 fn test_asinh_basic() {
1096 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AsinhFn));
1097 let ctx = interp(&wb);
1098 let f = ctx.context.get_function("", "ASINH").unwrap();
1099 let a0 = make_num_ast(1.5);
1100 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1101 match f
1102 .dispatch(&args, &ctx.function_context(None))
1103 .unwrap()
1104 .into_literal()
1105 {
1106 LiteralValue::Number(n) => assert_close(n, (1.5f64).asinh()),
1107 v => panic!("unexpected {v:?}"),
1108 }
1109 }
1110}
1111
1112#[derive(Debug)]
1113pub struct AcoshFn;
1114impl Function for AcoshFn {
1115 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1116 fn name(&self) -> &'static str {
1117 "ACOSH"
1118 }
1119 fn min_args(&self) -> usize {
1120 1
1121 }
1122 fn arg_schema(&self) -> &'static [ArgSchema] {
1123 &ARG_ANY_ONE[..]
1124 }
1125 fn eval<'a, 'b, 'c>(
1126 &self,
1127 args: &'c [ArgumentHandle<'a, 'b>],
1128 _ctx: &dyn FunctionContext<'b>,
1129 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1130 let x = unary_numeric_arg(args)?;
1131 if x < 1.0 {
1132 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1133 ExcelError::new_num(),
1134 )));
1135 }
1136 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1137 x.acosh(),
1138 )))
1139 }
1140}
1141
1142#[cfg(test)]
1143mod tests_acosh {
1144 use super::*;
1145 use crate::test_workbook::TestWorkbook;
1146 use crate::traits::ArgumentHandle;
1147 use formualizer_parse::LiteralValue;
1148 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1149 wb.interpreter()
1150 }
1151 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1152 formualizer_parse::parser::ASTNode::new(
1153 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1154 None,
1155 )
1156 }
1157 #[test]
1158 fn test_acosh_basic_and_domain() {
1159 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AcoshFn));
1160 let ctx = interp(&wb);
1161 let f = ctx.context.get_function("", "ACOSH").unwrap();
1162 let a0 = make_num_ast(1.0);
1163 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1164 assert_eq!(
1165 f.dispatch(&args, &ctx.function_context(None))
1166 .unwrap()
1167 .into_literal(),
1168 LiteralValue::Number(0.0)
1169 );
1170 let a1 = make_num_ast(0.5);
1171 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1172 match f
1173 .dispatch(&args2, &ctx.function_context(None))
1174 .unwrap()
1175 .into_literal()
1176 {
1177 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
1178 v => panic!("expected error, got {v:?}"),
1179 }
1180 }
1181}
1182
1183#[derive(Debug)]
1184pub struct AtanhFn;
1185impl Function for AtanhFn {
1186 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1187 fn name(&self) -> &'static str {
1188 "ATANH"
1189 }
1190 fn min_args(&self) -> usize {
1191 1
1192 }
1193 fn arg_schema(&self) -> &'static [ArgSchema] {
1194 &ARG_ANY_ONE[..]
1195 }
1196 fn eval<'a, 'b, 'c>(
1197 &self,
1198 args: &'c [ArgumentHandle<'a, 'b>],
1199 _ctx: &dyn FunctionContext<'b>,
1200 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1201 let x = unary_numeric_arg(args)?;
1202 if x <= -1.0 || x >= 1.0 {
1203 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1204 ExcelError::new_num(),
1205 )));
1206 }
1207 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1208 x.atanh(),
1209 )))
1210 }
1211}
1212
1213#[cfg(test)]
1214mod tests_atanh {
1215 use super::*;
1216 use crate::test_workbook::TestWorkbook;
1217 use crate::traits::ArgumentHandle;
1218 use formualizer_parse::LiteralValue;
1219 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1220 wb.interpreter()
1221 }
1222 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1223 formualizer_parse::parser::ASTNode::new(
1224 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1225 None,
1226 )
1227 }
1228 fn assert_close(a: f64, b: f64) {
1229 assert!((a - b).abs() < 1e-9);
1230 }
1231 #[test]
1232 fn test_atanh_basic_and_domain() {
1233 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AtanhFn));
1234 let ctx = interp(&wb);
1235 let f = ctx.context.get_function("", "ATANH").unwrap();
1236 let a0 = make_num_ast(0.5);
1237 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1238 match f
1239 .dispatch(&args, &ctx.function_context(None))
1240 .unwrap()
1241 .into_literal()
1242 {
1243 LiteralValue::Number(n) => assert_close(n, (0.5f64).atanh()),
1244 v => panic!("unexpected {v:?}"),
1245 }
1246 let a1 = make_num_ast(1.0);
1247 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1248 match f
1249 .dispatch(&args2, &ctx.function_context(None))
1250 .unwrap()
1251 .into_literal()
1252 {
1253 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
1254 v => panic!("expected error, got {v:?}"),
1255 }
1256 }
1257}
1258
1259#[derive(Debug)]
1260pub struct SechFn;
1261impl Function for SechFn {
1262 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1263 fn name(&self) -> &'static str {
1264 "SECH"
1265 }
1266 fn min_args(&self) -> usize {
1267 1
1268 }
1269 fn arg_schema(&self) -> &'static [ArgSchema] {
1270 &ARG_ANY_ONE[..]
1271 }
1272 fn eval<'a, 'b, 'c>(
1273 &self,
1274 args: &'c [ArgumentHandle<'a, 'b>],
1275 _ctx: &dyn FunctionContext<'b>,
1276 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1277 let x = unary_numeric_arg(args)?;
1278 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1279 1.0 / x.cosh(),
1280 )))
1281 }
1282}
1283
1284#[cfg(test)]
1285mod tests_sech {
1286 use super::*;
1287 use crate::test_workbook::TestWorkbook;
1288 use crate::traits::ArgumentHandle;
1289 use formualizer_parse::LiteralValue;
1290 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1291 wb.interpreter()
1292 }
1293 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1294 formualizer_parse::parser::ASTNode::new(
1295 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1296 None,
1297 )
1298 }
1299 fn assert_close(a: f64, b: f64) {
1300 assert!((a - b).abs() < 1e-9);
1301 }
1302 #[test]
1303 fn test_sech_basic() {
1304 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SechFn));
1305 let ctx = interp(&wb);
1306 let f = ctx.context.get_function("", "SECH").unwrap();
1307 let a0 = make_num_ast(0.0);
1308 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1309 match f
1310 .dispatch(&args, &ctx.function_context(None))
1311 .unwrap()
1312 .into_literal()
1313 {
1314 LiteralValue::Number(n) => assert_close(n, 1.0),
1315 v => panic!("unexpected {v:?}"),
1316 }
1317 }
1318}
1319
1320#[derive(Debug)]
1321pub struct CschFn;
1322impl Function for CschFn {
1323 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1324 fn name(&self) -> &'static str {
1325 "CSCH"
1326 }
1327 fn min_args(&self) -> usize {
1328 1
1329 }
1330 fn arg_schema(&self) -> &'static [ArgSchema] {
1331 &ARG_NUM_LENIENT_ONE[..]
1332 }
1333 fn eval<'a, 'b, 'c>(
1334 &self,
1335 args: &'c [ArgumentHandle<'a, 'b>],
1336 _ctx: &dyn FunctionContext<'b>,
1337 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1338 let x = unary_numeric_arg(args)?;
1339 let s = x.sinh(); if s.abs() < EPSILON_NEAR_ZERO {
1341 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1342 ExcelError::from_error_string("#DIV/0!"),
1343 )));
1344 }
1345 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1346 1.0 / s,
1347 )))
1348 }
1349}
1350
1351#[cfg(test)]
1352mod tests_csch {
1353 use super::*;
1354 use crate::test_workbook::TestWorkbook;
1355 use crate::traits::ArgumentHandle;
1356 use formualizer_parse::LiteralValue;
1357 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1358 wb.interpreter()
1359 }
1360 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1361 formualizer_parse::parser::ASTNode::new(
1362 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1363 None,
1364 )
1365 }
1366 fn assert_close(a: f64, b: f64) {
1367 assert!((a - b).abs() < 1e-9);
1368 }
1369 #[test]
1370 fn test_csch_basic_and_div0() {
1371 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CschFn));
1372 let ctx = interp(&wb);
1373 let csch = ctx.context.get_function("", "CSCH").unwrap();
1374 let a0 = make_num_ast(1.0);
1376 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1377 match csch
1378 .dispatch(&args, &ctx.function_context(None))
1379 .unwrap()
1380 .into_literal()
1381 {
1382 LiteralValue::Number(n) => assert_close(n, 0.8509181282393216),
1383 v => panic!("unexpected {v:?}"),
1384 }
1385 let a1 = make_num_ast(0.0);
1387 let args2 = vec![ArgumentHandle::new(&a1, &ctx)];
1388 match csch
1389 .dispatch(&args2, &ctx.function_context(None))
1390 .unwrap()
1391 .into_literal()
1392 {
1393 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1394 v => panic!("expected error, got {v:?}"),
1395 }
1396 }
1397}
1398
1399#[derive(Debug)]
1400pub struct CothFn;
1401impl Function for CothFn {
1402 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1403 fn name(&self) -> &'static str {
1404 "COTH"
1405 }
1406 fn min_args(&self) -> usize {
1407 1
1408 }
1409 fn arg_schema(&self) -> &'static [ArgSchema] {
1410 &ARG_NUM_LENIENT_ONE[..]
1411 }
1412 fn eval<'a, 'b, 'c>(
1413 &self,
1414 args: &'c [ArgumentHandle<'a, 'b>],
1415 _ctx: &dyn FunctionContext<'b>,
1416 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1417 let x = unary_numeric_arg(args)?;
1418 let s = x.sinh();
1419 if s.abs() < EPSILON_NEAR_ZERO {
1420 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1421 ExcelError::from_error_string("#DIV/0!"),
1422 )));
1423 }
1424 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1425 x.cosh() / s,
1426 )))
1427 }
1428}
1429
1430#[cfg(test)]
1431mod tests_coth {
1432 use super::*;
1433 use crate::test_workbook::TestWorkbook;
1434 use crate::traits::ArgumentHandle;
1435 use formualizer_parse::LiteralValue;
1436 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1437 wb.interpreter()
1438 }
1439 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1440 formualizer_parse::parser::ASTNode::new(
1441 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1442 None,
1443 )
1444 }
1445 #[test]
1446 fn test_coth_div0() {
1447 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(CothFn));
1448 let ctx = interp(&wb);
1449 let f = ctx.context.get_function("", "COTH").unwrap();
1450 let a0 = make_num_ast(0.0);
1451 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1452 match f
1453 .dispatch(&args, &ctx.function_context(None))
1454 .unwrap()
1455 .into_literal()
1456 {
1457 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1458 v => panic!("expected error, got {v:?}"),
1459 }
1460 }
1461}
1462
1463#[derive(Debug)]
1466pub struct RadiansFn;
1467impl Function for RadiansFn {
1468 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1469 fn name(&self) -> &'static str {
1470 "RADIANS"
1471 }
1472 fn min_args(&self) -> usize {
1473 1
1474 }
1475 fn arg_schema(&self) -> &'static [ArgSchema] {
1476 &ARG_ANY_ONE[..]
1477 }
1478 fn eval<'a, 'b, 'c>(
1479 &self,
1480 args: &'c [ArgumentHandle<'a, 'b>],
1481 _ctx: &dyn FunctionContext<'b>,
1482 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1483 let deg = unary_numeric_arg(args)?;
1484 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1485 deg * PI / 180.0,
1486 )))
1487 }
1488}
1489
1490#[cfg(test)]
1491mod tests_radians {
1492 use super::*;
1493 use crate::test_workbook::TestWorkbook;
1494 use crate::traits::ArgumentHandle;
1495 use formualizer_parse::LiteralValue;
1496 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1497 wb.interpreter()
1498 }
1499 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1500 formualizer_parse::parser::ASTNode::new(
1501 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1502 None,
1503 )
1504 }
1505 fn assert_close(a: f64, b: f64) {
1506 assert!((a - b).abs() < 1e-9);
1507 }
1508 #[test]
1509 fn test_radians_basic() {
1510 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RadiansFn));
1511 let ctx = interp(&wb);
1512 let f = ctx.context.get_function("", "RADIANS").unwrap();
1513 let a0 = make_num_ast(180.0);
1514 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1515 match f
1516 .dispatch(&args, &ctx.function_context(None))
1517 .unwrap()
1518 .into_literal()
1519 {
1520 LiteralValue::Number(n) => assert_close(n, PI),
1521 v => panic!("unexpected {v:?}"),
1522 }
1523 }
1524}
1525
1526#[derive(Debug)]
1527pub struct DegreesFn;
1528impl Function for DegreesFn {
1529 func_caps!(PURE, ELEMENTWISE, NUMERIC_ONLY);
1530 fn name(&self) -> &'static str {
1531 "DEGREES"
1532 }
1533 fn min_args(&self) -> usize {
1534 1
1535 }
1536 fn arg_schema(&self) -> &'static [ArgSchema] {
1537 &ARG_ANY_ONE[..]
1538 }
1539 fn eval<'a, 'b, 'c>(
1540 &self,
1541 args: &'c [ArgumentHandle<'a, 'b>],
1542 _ctx: &dyn FunctionContext<'b>,
1543 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1544 let rad = unary_numeric_arg(args)?;
1545 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1546 rad * 180.0 / PI,
1547 )))
1548 }
1549}
1550
1551#[cfg(test)]
1552mod tests_degrees {
1553 use super::*;
1554 use crate::test_workbook::TestWorkbook;
1555 use crate::traits::ArgumentHandle;
1556 use formualizer_parse::LiteralValue;
1557 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1558 wb.interpreter()
1559 }
1560 fn make_num_ast(n: f64) -> formualizer_parse::parser::ASTNode {
1561 formualizer_parse::parser::ASTNode::new(
1562 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Number(n)),
1563 None,
1564 )
1565 }
1566 fn assert_close(a: f64, b: f64) {
1567 assert!((a - b).abs() < 1e-9);
1568 }
1569 #[test]
1570 fn test_degrees_basic() {
1571 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(DegreesFn));
1572 let ctx = interp(&wb);
1573 let f = ctx.context.get_function("", "DEGREES").unwrap();
1574 let a0 = make_num_ast(PI);
1575 let args = vec![ArgumentHandle::new(&a0, &ctx)];
1576 match f
1577 .dispatch(&args, &ctx.function_context(None))
1578 .unwrap()
1579 .into_literal()
1580 {
1581 LiteralValue::Number(n) => assert_close(n, 180.0),
1582 v => panic!("unexpected {v:?}"),
1583 }
1584 }
1585}
1586
1587#[derive(Debug)]
1588pub struct PiFn;
1589impl Function for PiFn {
1590 func_caps!(PURE);
1591 fn name(&self) -> &'static str {
1592 "PI"
1593 }
1594 fn min_args(&self) -> usize {
1595 0
1596 }
1597 fn eval<'a, 'b, 'c>(
1598 &self,
1599 _args: &'c [ArgumentHandle<'a, 'b>],
1600 _ctx: &dyn FunctionContext<'b>,
1601 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1602 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(PI)))
1603 }
1604}
1605
1606#[cfg(test)]
1607mod tests_pi {
1608 use super::*;
1609 use crate::test_workbook::TestWorkbook;
1610 use formualizer_parse::LiteralValue;
1611 #[test]
1612 fn test_pi_basic() {
1613 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PiFn));
1614 let ctx = wb.interpreter();
1615 let f = ctx.context.get_function("", "PI").unwrap();
1616 assert_eq!(
1617 f.eval(&[], &ctx.function_context(None))
1618 .unwrap()
1619 .into_literal(),
1620 LiteralValue::Number(PI)
1621 );
1622 }
1623}
1624
1625pub fn register_builtins() {
1626 crate::function_registry::register_function(std::sync::Arc::new(SinFn));
1628 crate::function_registry::register_function(std::sync::Arc::new(CosFn));
1629 crate::function_registry::register_function(std::sync::Arc::new(TanFn));
1630 crate::function_registry::register_function(std::sync::Arc::new(AsinFn));
1632 crate::function_registry::register_function(std::sync::Arc::new(AcosFn));
1633 crate::function_registry::register_function(std::sync::Arc::new(AtanFn));
1634 crate::function_registry::register_function(std::sync::Arc::new(Atan2Fn));
1635 crate::function_registry::register_function(std::sync::Arc::new(SecFn));
1636 crate::function_registry::register_function(std::sync::Arc::new(CscFn));
1637 crate::function_registry::register_function(std::sync::Arc::new(CotFn));
1638 crate::function_registry::register_function(std::sync::Arc::new(AcotFn));
1639
1640 crate::function_registry::register_function(std::sync::Arc::new(SinhFn));
1642 crate::function_registry::register_function(std::sync::Arc::new(CoshFn));
1643 crate::function_registry::register_function(std::sync::Arc::new(TanhFn));
1644 crate::function_registry::register_function(std::sync::Arc::new(AsinhFn));
1645 crate::function_registry::register_function(std::sync::Arc::new(AcoshFn));
1646 crate::function_registry::register_function(std::sync::Arc::new(AtanhFn));
1647 crate::function_registry::register_function(std::sync::Arc::new(SechFn));
1648 crate::function_registry::register_function(std::sync::Arc::new(CschFn));
1649 crate::function_registry::register_function(std::sync::Arc::new(CothFn));
1650
1651 crate::function_registry::register_function(std::sync::Arc::new(RadiansFn));
1653 crate::function_registry::register_function(std::sync::Arc::new(DegreesFn));
1654 crate::function_registry::register_function(std::sync::Arc::new(PiFn));
1655}