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