1use super::super::utils::{
2 ARG_NUM_LENIENT_ONE, ARG_NUM_LENIENT_TWO, ARG_RANGE_NUM_LENIENT_ONE, coerce_num,
3};
4use crate::args::ArgSchema;
5use crate::function::Function;
6use crate::traits::{ArgumentHandle, FunctionContext};
7use formualizer_common::{ExcelError, LiteralValue};
8use formualizer_macros::func_caps;
9
10#[derive(Debug)]
11pub struct AbsFn;
12impl Function for AbsFn {
13 func_caps!(PURE);
14 fn name(&self) -> &'static str {
15 "ABS"
16 }
17 fn min_args(&self) -> usize {
18 1
19 }
20 fn arg_schema(&self) -> &'static [ArgSchema] {
21 &ARG_NUM_LENIENT_ONE[..]
22 }
23 fn eval<'a, 'b, 'c>(
24 &self,
25 args: &'c [ArgumentHandle<'a, 'b>],
26 _: &dyn FunctionContext<'b>,
27 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
28 let v = args[0].value()?.into_literal();
29 match v {
30 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
31 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
32 coerce_num(&other)?.abs(),
33 ))),
34 }
35 }
36}
37
38#[derive(Debug)]
39pub struct SignFn;
40impl Function for SignFn {
41 func_caps!(PURE);
42 fn name(&self) -> &'static str {
43 "SIGN"
44 }
45 fn min_args(&self) -> usize {
46 1
47 }
48 fn arg_schema(&self) -> &'static [ArgSchema] {
49 &ARG_NUM_LENIENT_ONE[..]
50 }
51 fn eval<'a, 'b, 'c>(
52 &self,
53 args: &'c [ArgumentHandle<'a, 'b>],
54 _: &dyn FunctionContext<'b>,
55 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
56 let v = args[0].value()?.into_literal();
57 match v {
58 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
59 other => {
60 let n = coerce_num(&other)?;
61 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
62 if n > 0.0 {
63 1.0
64 } else if n < 0.0 {
65 -1.0
66 } else {
67 0.0
68 },
69 )))
70 }
71 }
72 }
73}
74
75#[derive(Debug)]
76pub struct IntFn; impl Function for IntFn {
78 func_caps!(PURE);
79 fn name(&self) -> &'static str {
80 "INT"
81 }
82 fn min_args(&self) -> usize {
83 1
84 }
85 fn arg_schema(&self) -> &'static [ArgSchema] {
86 &ARG_NUM_LENIENT_ONE[..]
87 }
88 fn eval<'a, 'b, 'c>(
89 &self,
90 args: &'c [ArgumentHandle<'a, 'b>],
91 _: &dyn FunctionContext<'b>,
92 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
93 let v = args[0].value()?.into_literal();
94 match v {
95 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
96 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
97 coerce_num(&other)?.floor(),
98 ))),
99 }
100 }
101}
102
103#[derive(Debug)]
104pub struct TruncFn; impl Function for TruncFn {
106 func_caps!(PURE);
107 fn name(&self) -> &'static str {
108 "TRUNC"
109 }
110 fn min_args(&self) -> usize {
111 1
112 }
113 fn variadic(&self) -> bool {
114 true
115 }
116 fn arg_schema(&self) -> &'static [ArgSchema] {
117 &ARG_NUM_LENIENT_TWO[..]
118 }
119 fn eval<'a, 'b, 'c>(
120 &self,
121 args: &'c [ArgumentHandle<'a, 'b>],
122 _: &dyn FunctionContext<'b>,
123 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
124 if args.is_empty() || args.len() > 2 {
125 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
126 ExcelError::new_value(),
127 )));
128 }
129 let mut n = match args[0].value()?.into_literal() {
130 LiteralValue::Error(e) => {
131 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
132 }
133 other => coerce_num(&other)?,
134 };
135 let digits: i32 = if args.len() == 2 {
136 match args[1].value()?.into_literal() {
137 LiteralValue::Error(e) => {
138 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
139 }
140 other => coerce_num(&other)? as i32,
141 }
142 } else {
143 0
144 };
145 if digits >= 0 {
146 let f = 10f64.powi(digits);
147 n = (n * f).trunc() / f;
148 } else {
149 let f = 10f64.powi(-digits);
150 n = (n / f).trunc() * f;
151 }
152 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(n)))
153 }
154}
155
156#[derive(Debug)]
157pub struct RoundFn; impl Function for RoundFn {
159 func_caps!(PURE);
160 fn name(&self) -> &'static str {
161 "ROUND"
162 }
163 fn min_args(&self) -> usize {
164 2
165 }
166 fn arg_schema(&self) -> &'static [ArgSchema] {
167 &ARG_NUM_LENIENT_TWO[..]
168 }
169 fn eval<'a, 'b, 'c>(
170 &self,
171 args: &'c [ArgumentHandle<'a, 'b>],
172 _: &dyn FunctionContext<'b>,
173 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
174 let n = match args[0].value()?.into_literal() {
175 LiteralValue::Error(e) => {
176 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
177 }
178 other => coerce_num(&other)?,
179 };
180 let digits = match args[1].value()?.into_literal() {
181 LiteralValue::Error(e) => {
182 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
183 }
184 other => coerce_num(&other)? as i32,
185 };
186 let f = 10f64.powi(digits.abs());
187 let out = if digits >= 0 {
188 (n * f).round() / f
189 } else {
190 (n / f).round() * f
191 };
192 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
193 }
194}
195
196#[derive(Debug)]
197pub struct RoundDownFn; impl Function for RoundDownFn {
199 func_caps!(PURE);
200 fn name(&self) -> &'static str {
201 "ROUNDDOWN"
202 }
203 fn min_args(&self) -> usize {
204 2
205 }
206 fn arg_schema(&self) -> &'static [ArgSchema] {
207 &ARG_NUM_LENIENT_TWO[..]
208 }
209 fn eval<'a, 'b, 'c>(
210 &self,
211 args: &'c [ArgumentHandle<'a, 'b>],
212 _: &dyn FunctionContext<'b>,
213 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
214 let n = match args[0].value()?.into_literal() {
215 LiteralValue::Error(e) => {
216 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
217 }
218 other => coerce_num(&other)?,
219 };
220 let digits = match args[1].value()?.into_literal() {
221 LiteralValue::Error(e) => {
222 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
223 }
224 other => coerce_num(&other)? as i32,
225 };
226 let f = 10f64.powi(digits.abs());
227 let out = if digits >= 0 {
228 (n * f).trunc() / f
229 } else {
230 (n / f).trunc() * f
231 };
232 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
233 }
234}
235
236#[derive(Debug)]
237pub struct RoundUpFn; impl Function for RoundUpFn {
239 func_caps!(PURE);
240 fn name(&self) -> &'static str {
241 "ROUNDUP"
242 }
243 fn min_args(&self) -> usize {
244 2
245 }
246 fn arg_schema(&self) -> &'static [ArgSchema] {
247 &ARG_NUM_LENIENT_TWO[..]
248 }
249 fn eval<'a, 'b, 'c>(
250 &self,
251 args: &'c [ArgumentHandle<'a, 'b>],
252 _: &dyn FunctionContext<'b>,
253 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
254 let n = match args[0].value()?.into_literal() {
255 LiteralValue::Error(e) => {
256 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
257 }
258 other => coerce_num(&other)?,
259 };
260 let digits = match args[1].value()?.into_literal() {
261 LiteralValue::Error(e) => {
262 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
263 }
264 other => coerce_num(&other)? as i32,
265 };
266 let f = 10f64.powi(digits.abs());
267 let mut scaled = if digits >= 0 { n * f } else { n / f };
268 if scaled > 0.0 {
269 scaled = scaled.ceil();
270 } else {
271 scaled = scaled.floor();
272 }
273 let out = if digits >= 0 { scaled / f } else { scaled * f };
274 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
275 }
276}
277
278#[derive(Debug)]
279pub struct ModFn; impl Function for ModFn {
281 func_caps!(PURE);
282 fn name(&self) -> &'static str {
283 "MOD"
284 }
285 fn min_args(&self) -> usize {
286 2
287 }
288 fn arg_schema(&self) -> &'static [ArgSchema] {
289 &ARG_NUM_LENIENT_TWO[..]
290 }
291 fn eval<'a, 'b, 'c>(
292 &self,
293 args: &'c [ArgumentHandle<'a, 'b>],
294 _: &dyn FunctionContext<'b>,
295 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
296 let x = match args[0].value()?.into_literal() {
297 LiteralValue::Error(e) => {
298 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
299 }
300 other => coerce_num(&other)?,
301 };
302 let y = match args[1].value()?.into_literal() {
303 LiteralValue::Error(e) => {
304 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
305 }
306 other => coerce_num(&other)?,
307 };
308 if y == 0.0 {
309 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
310 ExcelError::from_error_string("#DIV/0!"),
311 )));
312 }
313 let m = x % y;
314 let mut r = if m == 0.0 {
315 0.0
316 } else if (y > 0.0 && m < 0.0) || (y < 0.0 && m > 0.0) {
317 m + y
318 } else {
319 m
320 };
321 if r == -0.0 {
322 r = 0.0;
323 }
324 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(r)))
325 }
326}
327
328#[derive(Debug)]
331pub struct CeilingFn; impl Function for CeilingFn {
333 func_caps!(PURE);
334 fn name(&self) -> &'static str {
335 "CEILING"
336 }
337 fn min_args(&self) -> usize {
338 1
339 }
340 fn variadic(&self) -> bool {
341 true
342 }
343 fn arg_schema(&self) -> &'static [ArgSchema] {
344 &ARG_NUM_LENIENT_TWO[..]
345 }
346 fn eval<'a, 'b, 'c>(
347 &self,
348 args: &'c [ArgumentHandle<'a, 'b>],
349 _: &dyn FunctionContext<'b>,
350 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
351 if args.is_empty() || args.len() > 2 {
352 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
353 ExcelError::new_value(),
354 )));
355 }
356 let n = match args[0].value()?.into_literal() {
357 LiteralValue::Error(e) => {
358 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
359 }
360 other => coerce_num(&other)?,
361 };
362 let mut sig = if args.len() == 2 {
363 match args[1].value()?.into_literal() {
364 LiteralValue::Error(e) => {
365 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
366 }
367 other => coerce_num(&other)?,
368 }
369 } else {
370 1.0
371 };
372 if sig == 0.0 {
373 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
374 ExcelError::from_error_string("#DIV/0!"),
375 )));
376 }
377 if sig < 0.0 {
378 sig = sig.abs(); }
380 let k = (n / sig).ceil();
381 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
382 k * sig,
383 )))
384 }
385}
386
387#[derive(Debug)]
388pub struct CeilingMathFn; impl Function for CeilingMathFn {
390 func_caps!(PURE);
391 fn name(&self) -> &'static str {
392 "CEILING.MATH"
393 }
394 fn min_args(&self) -> usize {
395 1
396 }
397 fn variadic(&self) -> bool {
398 true
399 }
400 fn arg_schema(&self) -> &'static [ArgSchema] {
401 &ARG_NUM_LENIENT_TWO[..]
402 } fn eval<'a, 'b, 'c>(
404 &self,
405 args: &'c [ArgumentHandle<'a, 'b>],
406 _: &dyn FunctionContext<'b>,
407 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
408 if args.is_empty() || args.len() > 3 {
409 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
410 ExcelError::new_value(),
411 )));
412 }
413 let n = match args[0].value()?.into_literal() {
414 LiteralValue::Error(e) => {
415 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
416 }
417 other => coerce_num(&other)?,
418 };
419 let sig = if args.len() >= 2 {
420 match args[1].value()?.into_literal() {
421 LiteralValue::Error(e) => {
422 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
423 }
424 other => {
425 let v = coerce_num(&other)?;
426 if v == 0.0 { 1.0 } else { v.abs() }
427 }
428 }
429 } else {
430 1.0
431 };
432 let mode_nonzero = if args.len() == 3 {
433 match args[2].value()?.into_literal() {
434 LiteralValue::Error(e) => {
435 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
436 }
437 other => coerce_num(&other)? != 0.0,
438 }
439 } else {
440 false
441 };
442 let result = if n >= 0.0 {
443 (n / sig).ceil() * sig
444 } else if mode_nonzero {
445 (n / sig).floor() * sig } else {
447 (n / sig).ceil() * sig };
449 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
450 result,
451 )))
452 }
453}
454
455#[derive(Debug)]
456pub struct FloorFn; impl Function for FloorFn {
458 func_caps!(PURE);
459 fn name(&self) -> &'static str {
460 "FLOOR"
461 }
462 fn min_args(&self) -> usize {
463 1
464 }
465 fn variadic(&self) -> bool {
466 true
467 }
468 fn arg_schema(&self) -> &'static [ArgSchema] {
469 &ARG_NUM_LENIENT_TWO[..]
470 }
471 fn eval<'a, 'b, 'c>(
472 &self,
473 args: &'c [ArgumentHandle<'a, 'b>],
474 _: &dyn FunctionContext<'b>,
475 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
476 if args.is_empty() || args.len() > 2 {
477 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
478 ExcelError::new_value(),
479 )));
480 }
481 let n = match args[0].value()?.into_literal() {
482 LiteralValue::Error(e) => {
483 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
484 }
485 other => coerce_num(&other)?,
486 };
487 let mut sig = if args.len() == 2 {
488 match args[1].value()?.into_literal() {
489 LiteralValue::Error(e) => {
490 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
491 }
492 other => coerce_num(&other)?,
493 }
494 } else {
495 1.0
496 };
497 if sig == 0.0 {
498 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
499 ExcelError::from_error_string("#DIV/0!"),
500 )));
501 }
502 if sig < 0.0 {
503 sig = sig.abs();
504 }
505 let k = (n / sig).floor();
506 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
507 k * sig,
508 )))
509 }
510}
511
512#[derive(Debug)]
513pub struct FloorMathFn; impl Function for FloorMathFn {
515 func_caps!(PURE);
516 fn name(&self) -> &'static str {
517 "FLOOR.MATH"
518 }
519 fn min_args(&self) -> usize {
520 1
521 }
522 fn variadic(&self) -> bool {
523 true
524 }
525 fn arg_schema(&self) -> &'static [ArgSchema] {
526 &ARG_NUM_LENIENT_TWO[..]
527 }
528 fn eval<'a, 'b, 'c>(
529 &self,
530 args: &'c [ArgumentHandle<'a, 'b>],
531 _: &dyn FunctionContext<'b>,
532 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
533 if args.is_empty() || args.len() > 3 {
534 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
535 ExcelError::new_value(),
536 )));
537 }
538 let n = match args[0].value()?.into_literal() {
539 LiteralValue::Error(e) => {
540 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
541 }
542 other => coerce_num(&other)?,
543 };
544 let sig = if args.len() >= 2 {
545 match args[1].value()?.into_literal() {
546 LiteralValue::Error(e) => {
547 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
548 }
549 other => {
550 let v = coerce_num(&other)?;
551 if v == 0.0 { 1.0 } else { v.abs() }
552 }
553 }
554 } else {
555 1.0
556 };
557 let mode_nonzero = if args.len() == 3 {
558 match args[2].value()?.into_literal() {
559 LiteralValue::Error(e) => {
560 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
561 }
562 other => coerce_num(&other)? != 0.0,
563 }
564 } else {
565 false
566 };
567 let result = if n >= 0.0 {
568 (n / sig).floor() * sig
569 } else if mode_nonzero {
570 (n / sig).ceil() * sig
571 } else {
572 (n / sig).floor() * sig
573 };
574 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
575 result,
576 )))
577 }
578}
579
580#[derive(Debug)]
581pub struct SqrtFn; impl Function for SqrtFn {
583 func_caps!(PURE);
584 fn name(&self) -> &'static str {
585 "SQRT"
586 }
587 fn min_args(&self) -> usize {
588 1
589 }
590 fn arg_schema(&self) -> &'static [ArgSchema] {
591 &ARG_NUM_LENIENT_ONE[..]
592 }
593 fn eval<'a, 'b, 'c>(
594 &self,
595 args: &'c [ArgumentHandle<'a, 'b>],
596 _: &dyn FunctionContext<'b>,
597 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
598 let n = match args[0].value()?.into_literal() {
599 LiteralValue::Error(e) => {
600 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
601 }
602 other => coerce_num(&other)?,
603 };
604 if n < 0.0 {
605 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
606 ExcelError::new_num(),
607 )));
608 }
609 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
610 n.sqrt(),
611 )))
612 }
613}
614
615#[derive(Debug)]
616pub struct PowerFn; impl Function for PowerFn {
618 func_caps!(PURE);
619 fn name(&self) -> &'static str {
620 "POWER"
621 }
622 fn min_args(&self) -> usize {
623 2
624 }
625 fn arg_schema(&self) -> &'static [ArgSchema] {
626 &ARG_NUM_LENIENT_TWO[..]
627 }
628 fn eval<'a, 'b, 'c>(
629 &self,
630 args: &'c [ArgumentHandle<'a, 'b>],
631 _: &dyn FunctionContext<'b>,
632 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
633 let base = match args[0].value()?.into_literal() {
634 LiteralValue::Error(e) => {
635 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
636 }
637 other => coerce_num(&other)?,
638 };
639 let expv = match args[1].value()?.into_literal() {
640 LiteralValue::Error(e) => {
641 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
642 }
643 other => coerce_num(&other)?,
644 };
645 if base < 0.0 && (expv.fract().abs() > 1e-12) {
646 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
647 ExcelError::new_num(),
648 )));
649 }
650 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
651 base.powf(expv),
652 )))
653 }
654}
655
656#[derive(Debug)]
657pub struct ExpFn; impl Function for ExpFn {
659 func_caps!(PURE);
660 fn name(&self) -> &'static str {
661 "EXP"
662 }
663 fn min_args(&self) -> usize {
664 1
665 }
666 fn arg_schema(&self) -> &'static [ArgSchema] {
667 &ARG_NUM_LENIENT_ONE[..]
668 }
669 fn eval<'a, 'b, 'c>(
670 &self,
671 args: &'c [ArgumentHandle<'a, 'b>],
672 _: &dyn FunctionContext<'b>,
673 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
674 let n = match args[0].value()?.into_literal() {
675 LiteralValue::Error(e) => {
676 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
677 }
678 other => coerce_num(&other)?,
679 };
680 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
681 n.exp(),
682 )))
683 }
684}
685
686#[derive(Debug)]
687pub struct LnFn; impl Function for LnFn {
689 func_caps!(PURE);
690 fn name(&self) -> &'static str {
691 "LN"
692 }
693 fn min_args(&self) -> usize {
694 1
695 }
696 fn arg_schema(&self) -> &'static [ArgSchema] {
697 &ARG_NUM_LENIENT_ONE[..]
698 }
699 fn eval<'a, 'b, 'c>(
700 &self,
701 args: &'c [ArgumentHandle<'a, 'b>],
702 _: &dyn FunctionContext<'b>,
703 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
704 let n = match args[0].value()?.into_literal() {
705 LiteralValue::Error(e) => {
706 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
707 }
708 other => coerce_num(&other)?,
709 };
710 if n <= 0.0 {
711 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
712 ExcelError::new_num(),
713 )));
714 }
715 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
716 n.ln(),
717 )))
718 }
719}
720
721#[derive(Debug)]
722pub struct LogFn; impl Function for LogFn {
724 func_caps!(PURE);
725 fn name(&self) -> &'static str {
726 "LOG"
727 }
728 fn min_args(&self) -> usize {
729 1
730 }
731 fn variadic(&self) -> bool {
732 true
733 }
734 fn arg_schema(&self) -> &'static [ArgSchema] {
735 &ARG_NUM_LENIENT_TWO[..]
736 }
737 fn eval<'a, 'b, 'c>(
738 &self,
739 args: &'c [ArgumentHandle<'a, 'b>],
740 _: &dyn FunctionContext<'b>,
741 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
742 if args.is_empty() || args.len() > 2 {
743 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
744 ExcelError::new_value(),
745 )));
746 }
747 let n = match args[0].value()?.into_literal() {
748 LiteralValue::Error(e) => {
749 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
750 }
751 other => coerce_num(&other)?,
752 };
753 let base = if args.len() == 2 {
754 match args[1].value()?.into_literal() {
755 LiteralValue::Error(e) => {
756 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
757 }
758 other => coerce_num(&other)?,
759 }
760 } else {
761 10.0
762 };
763 if n <= 0.0 || base <= 0.0 || (base - 1.0).abs() < 1e-12 {
764 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
765 ExcelError::new_num(),
766 )));
767 }
768 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
769 n.log(base),
770 )))
771 }
772}
773
774#[derive(Debug)]
775pub struct Log10Fn; impl Function for Log10Fn {
777 func_caps!(PURE);
778 fn name(&self) -> &'static str {
779 "LOG10"
780 }
781 fn min_args(&self) -> usize {
782 1
783 }
784 fn arg_schema(&self) -> &'static [ArgSchema] {
785 &ARG_NUM_LENIENT_ONE[..]
786 }
787 fn eval<'a, 'b, 'c>(
788 &self,
789 args: &'c [ArgumentHandle<'a, 'b>],
790 _: &dyn FunctionContext<'b>,
791 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
792 let n = match args[0].value()?.into_literal() {
793 LiteralValue::Error(e) => {
794 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
795 }
796 other => coerce_num(&other)?,
797 };
798 if n <= 0.0 {
799 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
800 ExcelError::new_num(),
801 )));
802 }
803 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
804 n.log10(),
805 )))
806 }
807}
808
809fn factorial_checked(n: i64) -> Option<f64> {
810 if !(0..=170).contains(&n) {
811 return None;
812 }
813 let mut out = 1.0;
814 for i in 2..=n {
815 out *= i as f64;
816 }
817 Some(out)
818}
819
820#[derive(Debug)]
821pub struct QuotientFn;
822impl Function for QuotientFn {
823 func_caps!(PURE);
824 fn name(&self) -> &'static str {
825 "QUOTIENT"
826 }
827 fn min_args(&self) -> usize {
828 2
829 }
830 fn arg_schema(&self) -> &'static [ArgSchema] {
831 &ARG_NUM_LENIENT_TWO[..]
832 }
833 fn eval<'a, 'b, 'c>(
834 &self,
835 args: &'c [ArgumentHandle<'a, 'b>],
836 _: &dyn FunctionContext<'b>,
837 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
838 let n = match args[0].value()?.into_literal() {
839 LiteralValue::Error(e) => {
840 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
841 }
842 other => coerce_num(&other)?,
843 };
844 let d = match args[1].value()?.into_literal() {
845 LiteralValue::Error(e) => {
846 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
847 }
848 other => coerce_num(&other)?,
849 };
850 if d == 0.0 {
851 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
852 ExcelError::new_div(),
853 )));
854 }
855 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
856 (n / d).trunc(),
857 )))
858 }
859}
860
861#[derive(Debug)]
862pub struct EvenFn;
863impl Function for EvenFn {
864 func_caps!(PURE);
865 fn name(&self) -> &'static str {
866 "EVEN"
867 }
868 fn min_args(&self) -> usize {
869 1
870 }
871 fn arg_schema(&self) -> &'static [ArgSchema] {
872 &ARG_NUM_LENIENT_ONE[..]
873 }
874 fn eval<'a, 'b, 'c>(
875 &self,
876 args: &'c [ArgumentHandle<'a, 'b>],
877 _: &dyn FunctionContext<'b>,
878 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
879 let number = match args[0].value()?.into_literal() {
880 LiteralValue::Error(e) => {
881 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
882 }
883 other => coerce_num(&other)?,
884 };
885 if number == 0.0 {
886 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
887 }
888
889 let sign = number.signum();
890 let mut v = number.abs().ceil() as i64;
891 if v % 2 != 0 {
892 v += 1;
893 }
894 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
895 sign * v as f64,
896 )))
897 }
898}
899
900#[derive(Debug)]
901pub struct OddFn;
902impl Function for OddFn {
903 func_caps!(PURE);
904 fn name(&self) -> &'static str {
905 "ODD"
906 }
907 fn min_args(&self) -> usize {
908 1
909 }
910 fn arg_schema(&self) -> &'static [ArgSchema] {
911 &ARG_NUM_LENIENT_ONE[..]
912 }
913 fn eval<'a, 'b, 'c>(
914 &self,
915 args: &'c [ArgumentHandle<'a, 'b>],
916 _: &dyn FunctionContext<'b>,
917 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
918 let number = match args[0].value()?.into_literal() {
919 LiteralValue::Error(e) => {
920 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
921 }
922 other => coerce_num(&other)?,
923 };
924
925 let sign = if number < 0.0 { -1.0 } else { 1.0 };
926 let mut v = number.abs().ceil() as i64;
927 if v % 2 == 0 {
928 v += 1;
929 }
930 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
931 sign * v as f64,
932 )))
933 }
934}
935
936#[derive(Debug)]
937pub struct SqrtPiFn;
938impl Function for SqrtPiFn {
939 func_caps!(PURE);
940 fn name(&self) -> &'static str {
941 "SQRTPI"
942 }
943 fn min_args(&self) -> usize {
944 1
945 }
946 fn arg_schema(&self) -> &'static [ArgSchema] {
947 &ARG_NUM_LENIENT_ONE[..]
948 }
949 fn eval<'a, 'b, 'c>(
950 &self,
951 args: &'c [ArgumentHandle<'a, 'b>],
952 _: &dyn FunctionContext<'b>,
953 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
954 let n = match args[0].value()?.into_literal() {
955 LiteralValue::Error(e) => {
956 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
957 }
958 other => coerce_num(&other)?,
959 };
960 if n < 0.0 {
961 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
962 ExcelError::new_num(),
963 )));
964 }
965 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
966 (n * std::f64::consts::PI).sqrt(),
967 )))
968 }
969}
970
971#[derive(Debug)]
972pub struct MultinomialFn;
973impl Function for MultinomialFn {
974 func_caps!(PURE);
975 fn name(&self) -> &'static str {
976 "MULTINOMIAL"
977 }
978 fn min_args(&self) -> usize {
979 1
980 }
981 fn variadic(&self) -> bool {
982 true
983 }
984 fn arg_schema(&self) -> &'static [ArgSchema] {
985 &ARG_NUM_LENIENT_ONE[..]
986 }
987 fn eval<'a, 'b, 'c>(
988 &self,
989 args: &'c [ArgumentHandle<'a, 'b>],
990 _ctx: &dyn FunctionContext<'b>,
991 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
992 let mut values: Vec<i64> = Vec::new();
993 for arg in args {
994 for value in arg.lazy_values_owned()? {
995 let n = match value {
996 LiteralValue::Error(e) => {
997 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
998 }
999 other => coerce_num(&other)?.trunc() as i64,
1000 };
1001 if n < 0 {
1002 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1003 ExcelError::new_num(),
1004 )));
1005 }
1006 values.push(n);
1007 }
1008 }
1009
1010 let sum: i64 = values.iter().sum();
1011 let num = match factorial_checked(sum) {
1012 Some(v) => v,
1013 None => {
1014 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1015 ExcelError::new_num(),
1016 )));
1017 }
1018 };
1019
1020 let mut den = 1.0;
1021 for n in values {
1022 let fact = match factorial_checked(n) {
1023 Some(v) => v,
1024 None => {
1025 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1026 ExcelError::new_num(),
1027 )));
1028 }
1029 };
1030 den *= fact;
1031 }
1032
1033 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1034 (num / den).round(),
1035 )))
1036 }
1037}
1038
1039#[derive(Debug)]
1040pub struct SeriesSumFn;
1041impl Function for SeriesSumFn {
1042 func_caps!(PURE);
1043 fn name(&self) -> &'static str {
1044 "SERIESSUM"
1045 }
1046 fn min_args(&self) -> usize {
1047 4
1048 }
1049 fn arg_schema(&self) -> &'static [ArgSchema] {
1050 use std::sync::LazyLock;
1051 static SCHEMA: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| {
1052 vec![
1053 ArgSchema::number_lenient_scalar(),
1054 ArgSchema::number_lenient_scalar(),
1055 ArgSchema::number_lenient_scalar(),
1056 ArgSchema::any(),
1057 ]
1058 });
1059 &SCHEMA[..]
1060 }
1061 fn eval<'a, 'b, 'c>(
1062 &self,
1063 args: &'c [ArgumentHandle<'a, 'b>],
1064 _ctx: &dyn FunctionContext<'b>,
1065 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1066 let x = match args[0].value()?.into_literal() {
1067 LiteralValue::Error(e) => {
1068 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1069 }
1070 other => coerce_num(&other)?,
1071 };
1072 let n = match args[1].value()?.into_literal() {
1073 LiteralValue::Error(e) => {
1074 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1075 }
1076 other => coerce_num(&other)?,
1077 };
1078 let m = match args[2].value()?.into_literal() {
1079 LiteralValue::Error(e) => {
1080 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1081 }
1082 other => coerce_num(&other)?,
1083 };
1084
1085 let mut coeffs: Vec<f64> = Vec::new();
1086 if let Ok(view) = args[3].range_view() {
1087 view.for_each_cell(&mut |cell| {
1088 match cell {
1089 LiteralValue::Error(e) => return Err(e.clone()),
1090 other => coeffs.push(coerce_num(other)?),
1091 }
1092 Ok(())
1093 })?;
1094 } else {
1095 match args[3].value()?.into_literal() {
1096 LiteralValue::Array(rows) => {
1097 for row in rows {
1098 for cell in row {
1099 match cell {
1100 LiteralValue::Error(e) => {
1101 return Ok(crate::traits::CalcValue::Scalar(
1102 LiteralValue::Error(e),
1103 ));
1104 }
1105 other => coeffs.push(coerce_num(&other)?),
1106 }
1107 }
1108 }
1109 }
1110 LiteralValue::Error(e) => {
1111 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1112 }
1113 other => coeffs.push(coerce_num(&other)?),
1114 }
1115 }
1116
1117 let mut sum = 0.0;
1118 for (i, c) in coeffs.into_iter().enumerate() {
1119 sum += c * x.powf(n + (i as f64) * m);
1120 }
1121
1122 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(sum)))
1123 }
1124}
1125
1126#[derive(Debug)]
1127pub struct SumsqFn;
1128impl Function for SumsqFn {
1129 func_caps!(PURE, REDUCTION, NUMERIC_ONLY);
1130 fn name(&self) -> &'static str {
1131 "SUMSQ"
1132 }
1133 fn min_args(&self) -> usize {
1134 1
1135 }
1136 fn variadic(&self) -> bool {
1137 true
1138 }
1139 fn arg_schema(&self) -> &'static [ArgSchema] {
1140 &ARG_RANGE_NUM_LENIENT_ONE[..]
1141 }
1142 fn eval<'a, 'b, 'c>(
1143 &self,
1144 args: &'c [ArgumentHandle<'a, 'b>],
1145 _ctx: &dyn FunctionContext<'b>,
1146 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1147 let mut total = 0.0;
1148 for arg in args {
1149 if let Ok(view) = arg.range_view() {
1150 view.for_each_cell(&mut |cell| {
1151 match cell {
1152 LiteralValue::Error(e) => return Err(e.clone()),
1153 LiteralValue::Number(n) => total += n * n,
1154 LiteralValue::Int(i) => {
1155 let n = *i as f64;
1156 total += n * n;
1157 }
1158 LiteralValue::Date(d) => {
1159 let n = crate::builtins::datetime::date_to_serial(d);
1160 total += n * n;
1161 }
1162 LiteralValue::DateTime(dt) => {
1163 let n = crate::builtins::datetime::datetime_to_serial(dt);
1164 total += n * n;
1165 }
1166 LiteralValue::Time(t) => {
1167 let n = crate::builtins::datetime::time_to_fraction(t);
1168 total += n * n;
1169 }
1170 LiteralValue::Duration(d) => {
1171 let n = d.num_seconds() as f64 / 86_400.0;
1172 total += n * n;
1173 }
1174 _ => {}
1175 }
1176 Ok(())
1177 })?;
1178 } else {
1179 let v = arg.value()?.into_literal();
1180 match v {
1181 LiteralValue::Error(e) => {
1182 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1183 }
1184 other => {
1185 let n = coerce_num(&other)?;
1186 total += n * n;
1187 }
1188 }
1189 }
1190 }
1191 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1192 total,
1193 )))
1194 }
1195}
1196
1197#[derive(Debug)]
1198pub struct MroundFn;
1199impl Function for MroundFn {
1200 func_caps!(PURE);
1201 fn name(&self) -> &'static str {
1202 "MROUND"
1203 }
1204 fn min_args(&self) -> usize {
1205 2
1206 }
1207 fn arg_schema(&self) -> &'static [ArgSchema] {
1208 &ARG_NUM_LENIENT_TWO[..]
1209 }
1210 fn eval<'a, 'b, 'c>(
1211 &self,
1212 args: &'c [ArgumentHandle<'a, 'b>],
1213 _ctx: &dyn FunctionContext<'b>,
1214 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1215 let number = match args[0].value()?.into_literal() {
1216 LiteralValue::Error(e) => {
1217 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1218 }
1219 other => coerce_num(&other)?,
1220 };
1221 let multiple = match args[1].value()?.into_literal() {
1222 LiteralValue::Error(e) => {
1223 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1224 }
1225 other => coerce_num(&other)?,
1226 };
1227
1228 if multiple == 0.0 {
1229 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
1230 }
1231 if number != 0.0 && number.signum() != multiple.signum() {
1232 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1233 ExcelError::new_num(),
1234 )));
1235 }
1236
1237 let m = multiple.abs();
1238 let scaled = number.abs() / m;
1239 let rounded = (scaled + 0.5 + 1e-12).floor();
1240 let out = rounded * m * number.signum();
1241 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
1242 }
1243}
1244
1245fn roman_classic(mut n: u32) -> String {
1246 let table = [
1247 (1000, "M"),
1248 (900, "CM"),
1249 (500, "D"),
1250 (400, "CD"),
1251 (100, "C"),
1252 (90, "XC"),
1253 (50, "L"),
1254 (40, "XL"),
1255 (10, "X"),
1256 (9, "IX"),
1257 (5, "V"),
1258 (4, "IV"),
1259 (1, "I"),
1260 ];
1261
1262 let mut out = String::new();
1263 for (value, glyph) in table {
1264 while n >= value {
1265 n -= value;
1266 out.push_str(glyph);
1267 }
1268 }
1269 out
1270}
1271
1272fn roman_apply_form(classic: String, form: i64) -> String {
1273 match form {
1274 0 => classic,
1275 1 => classic
1276 .replace("CM", "LM")
1277 .replace("CD", "LD")
1278 .replace("XC", "VL")
1279 .replace("XL", "VL")
1280 .replace("IX", "IV"),
1281 2 => roman_apply_form(classic, 1)
1282 .replace("LD", "XD")
1283 .replace("LM", "XM")
1284 .replace("VLIV", "IX"),
1285 3 => roman_apply_form(classic, 2)
1286 .replace("XD", "VD")
1287 .replace("XM", "VM")
1288 .replace("IX", "IV"),
1289 4 => roman_apply_form(classic, 3)
1290 .replace("VDIV", "ID")
1291 .replace("VMIV", "IM"),
1292 _ => classic,
1293 }
1294}
1295
1296#[derive(Debug)]
1297pub struct RomanFn;
1298impl Function for RomanFn {
1299 func_caps!(PURE);
1300 fn name(&self) -> &'static str {
1301 "ROMAN"
1302 }
1303 fn min_args(&self) -> usize {
1304 1
1305 }
1306 fn variadic(&self) -> bool {
1307 true
1308 }
1309 fn arg_schema(&self) -> &'static [ArgSchema] {
1310 &ARG_NUM_LENIENT_TWO[..]
1311 }
1312 fn eval<'a, 'b, 'c>(
1313 &self,
1314 args: &'c [ArgumentHandle<'a, 'b>],
1315 _ctx: &dyn FunctionContext<'b>,
1316 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1317 if args.len() > 2 {
1318 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1319 ExcelError::new_value(),
1320 )));
1321 }
1322
1323 let number = match args[0].value()?.into_literal() {
1324 LiteralValue::Error(e) => {
1325 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1326 }
1327 other => coerce_num(&other)?.trunc() as i64,
1328 };
1329
1330 if !(0..=3999).contains(&number) {
1331 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1332 ExcelError::new_value(),
1333 )));
1334 }
1335 if number == 0 {
1336 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
1337 "".to_string(),
1338 )));
1339 }
1340
1341 let form = if args.len() >= 2 {
1342 match args[1].value()?.into_literal() {
1343 LiteralValue::Boolean(b) => {
1344 if b {
1345 0
1346 } else {
1347 4
1348 }
1349 }
1350 LiteralValue::Number(n) => n.trunc() as i64,
1351 LiteralValue::Int(i) => i,
1352 LiteralValue::Empty => 0,
1353 LiteralValue::Error(e) => {
1354 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1355 }
1356 _ => {
1357 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1358 ExcelError::new_value(),
1359 )));
1360 }
1361 }
1362 } else {
1363 0
1364 };
1365
1366 if !(0..=4).contains(&form) {
1367 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1368 ExcelError::new_value(),
1369 )));
1370 }
1371
1372 let classic = roman_classic(number as u32);
1373 let text = roman_apply_form(classic, form);
1374 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(text)))
1375 }
1376}
1377
1378fn roman_digit_value(ch: char) -> Option<i64> {
1379 match ch {
1380 'I' => Some(1),
1381 'V' => Some(5),
1382 'X' => Some(10),
1383 'L' => Some(50),
1384 'C' => Some(100),
1385 'D' => Some(500),
1386 'M' => Some(1000),
1387 _ => None,
1388 }
1389}
1390
1391#[derive(Debug)]
1392pub struct ArabicFn;
1393impl Function for ArabicFn {
1394 func_caps!(PURE);
1395 fn name(&self) -> &'static str {
1396 "ARABIC"
1397 }
1398 fn min_args(&self) -> usize {
1399 1
1400 }
1401 fn arg_schema(&self) -> &'static [ArgSchema] {
1402 use std::sync::LazyLock;
1403 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
1404 &ONE[..]
1405 }
1406 fn eval<'a, 'b, 'c>(
1407 &self,
1408 args: &'c [ArgumentHandle<'a, 'b>],
1409 _ctx: &dyn FunctionContext<'b>,
1410 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1411 let raw = match args[0].value()?.into_literal() {
1412 LiteralValue::Text(s) => s,
1413 LiteralValue::Empty => String::new(),
1414 LiteralValue::Error(e) => {
1415 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1416 }
1417 _ => {
1418 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1419 ExcelError::new_value(),
1420 )));
1421 }
1422 };
1423
1424 let mut text = raw.trim().to_uppercase();
1425 if text.len() > 255 {
1426 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1427 ExcelError::new_value(),
1428 )));
1429 }
1430 if text.is_empty() {
1431 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
1432 }
1433
1434 let sign = if text.starts_with('-') {
1435 text.remove(0);
1436 -1.0
1437 } else {
1438 1.0
1439 };
1440
1441 if text.is_empty() {
1442 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1443 ExcelError::new_value(),
1444 )));
1445 }
1446
1447 let mut total = 0i64;
1448 let mut prev = 0i64;
1449 for ch in text.chars().rev() {
1450 let v = match roman_digit_value(ch) {
1451 Some(v) => v,
1452 None => {
1453 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1454 ExcelError::new_value(),
1455 )));
1456 }
1457 };
1458 if v < prev {
1459 total -= v;
1460 } else {
1461 total += v;
1462 prev = v;
1463 }
1464 }
1465
1466 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1467 sign * total as f64,
1468 )))
1469 }
1470}
1471
1472pub fn register_builtins() {
1473 use std::sync::Arc;
1474 crate::function_registry::register_function(Arc::new(AbsFn));
1475 crate::function_registry::register_function(Arc::new(SignFn));
1476 crate::function_registry::register_function(Arc::new(IntFn));
1477 crate::function_registry::register_function(Arc::new(TruncFn));
1478 crate::function_registry::register_function(Arc::new(RoundFn));
1479 crate::function_registry::register_function(Arc::new(RoundDownFn));
1480 crate::function_registry::register_function(Arc::new(RoundUpFn));
1481 crate::function_registry::register_function(Arc::new(ModFn));
1482 crate::function_registry::register_function(Arc::new(CeilingFn));
1483 crate::function_registry::register_function(Arc::new(CeilingMathFn));
1484 crate::function_registry::register_function(Arc::new(FloorFn));
1485 crate::function_registry::register_function(Arc::new(FloorMathFn));
1486 crate::function_registry::register_function(Arc::new(SqrtFn));
1487 crate::function_registry::register_function(Arc::new(PowerFn));
1488 crate::function_registry::register_function(Arc::new(ExpFn));
1489 crate::function_registry::register_function(Arc::new(LnFn));
1490 crate::function_registry::register_function(Arc::new(LogFn));
1491 crate::function_registry::register_function(Arc::new(Log10Fn));
1492 crate::function_registry::register_function(Arc::new(QuotientFn));
1493 crate::function_registry::register_function(Arc::new(EvenFn));
1494 crate::function_registry::register_function(Arc::new(OddFn));
1495 crate::function_registry::register_function(Arc::new(SqrtPiFn));
1496 crate::function_registry::register_function(Arc::new(MultinomialFn));
1497 crate::function_registry::register_function(Arc::new(SeriesSumFn));
1498 crate::function_registry::register_function(Arc::new(SumsqFn));
1499 crate::function_registry::register_function(Arc::new(MroundFn));
1500 crate::function_registry::register_function(Arc::new(RomanFn));
1501 crate::function_registry::register_function(Arc::new(ArabicFn));
1502}
1503
1504#[cfg(test)]
1505mod tests_numeric {
1506 use super::*;
1507 use crate::test_workbook::TestWorkbook;
1508 use crate::traits::ArgumentHandle;
1509 use formualizer_common::LiteralValue;
1510 use formualizer_parse::parser::{ASTNode, ASTNodeType};
1511
1512 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
1513 wb.interpreter()
1514 }
1515 fn lit(v: LiteralValue) -> ASTNode {
1516 ASTNode::new(ASTNodeType::Literal(v), None)
1517 }
1518
1519 #[test]
1521 fn abs_basic() {
1522 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
1523 let ctx = interp(&wb);
1524 let n = lit(LiteralValue::Number(-5.5));
1525 let f = ctx.context.get_function("", "ABS").unwrap();
1526 assert_eq!(
1527 f.dispatch(
1528 &[ArgumentHandle::new(&n, &ctx)],
1529 &ctx.function_context(None)
1530 )
1531 .unwrap()
1532 .into_literal(),
1533 LiteralValue::Number(5.5)
1534 );
1535 }
1536 #[test]
1537 fn abs_error_passthrough() {
1538 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
1539 let ctx = interp(&wb);
1540 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
1541 "#VALUE!",
1542 )));
1543 let f = ctx.context.get_function("", "ABS").unwrap();
1544 match f
1545 .dispatch(
1546 &[ArgumentHandle::new(&e, &ctx)],
1547 &ctx.function_context(None),
1548 )
1549 .unwrap()
1550 .into_literal()
1551 {
1552 LiteralValue::Error(er) => assert_eq!(er, "#VALUE!"),
1553 _ => panic!(),
1554 }
1555 }
1556
1557 #[test]
1559 fn sign_neg_zero_pos() {
1560 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
1561 let ctx = interp(&wb);
1562 let f = ctx.context.get_function("", "SIGN").unwrap();
1563 let neg = lit(LiteralValue::Number(-3.2));
1564 let zero = lit(LiteralValue::Int(0));
1565 let pos = lit(LiteralValue::Int(9));
1566 assert_eq!(
1567 f.dispatch(
1568 &[ArgumentHandle::new(&neg, &ctx)],
1569 &ctx.function_context(None)
1570 )
1571 .unwrap()
1572 .into_literal(),
1573 LiteralValue::Number(-1.0)
1574 );
1575 assert_eq!(
1576 f.dispatch(
1577 &[ArgumentHandle::new(&zero, &ctx)],
1578 &ctx.function_context(None)
1579 )
1580 .unwrap()
1581 .into_literal(),
1582 LiteralValue::Number(0.0)
1583 );
1584 assert_eq!(
1585 f.dispatch(
1586 &[ArgumentHandle::new(&pos, &ctx)],
1587 &ctx.function_context(None)
1588 )
1589 .unwrap()
1590 .into_literal(),
1591 LiteralValue::Number(1.0)
1592 );
1593 }
1594 #[test]
1595 fn sign_error_passthrough() {
1596 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
1597 let ctx = interp(&wb);
1598 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
1599 "#DIV/0!",
1600 )));
1601 let f = ctx.context.get_function("", "SIGN").unwrap();
1602 match f
1603 .dispatch(
1604 &[ArgumentHandle::new(&e, &ctx)],
1605 &ctx.function_context(None),
1606 )
1607 .unwrap()
1608 .into_literal()
1609 {
1610 LiteralValue::Error(er) => assert_eq!(er, "#DIV/0!"),
1611 _ => panic!(),
1612 }
1613 }
1614
1615 #[test]
1617 fn int_floor_negative() {
1618 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
1619 let ctx = interp(&wb);
1620 let f = ctx.context.get_function("", "INT").unwrap();
1621 let n = lit(LiteralValue::Number(-3.2));
1622 assert_eq!(
1623 f.dispatch(
1624 &[ArgumentHandle::new(&n, &ctx)],
1625 &ctx.function_context(None)
1626 )
1627 .unwrap()
1628 .into_literal(),
1629 LiteralValue::Number(-4.0)
1630 );
1631 }
1632 #[test]
1633 fn int_floor_positive() {
1634 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
1635 let ctx = interp(&wb);
1636 let f = ctx.context.get_function("", "INT").unwrap();
1637 let n = lit(LiteralValue::Number(3.7));
1638 assert_eq!(
1639 f.dispatch(
1640 &[ArgumentHandle::new(&n, &ctx)],
1641 &ctx.function_context(None)
1642 )
1643 .unwrap()
1644 .into_literal(),
1645 LiteralValue::Number(3.0)
1646 );
1647 }
1648
1649 #[test]
1651 fn trunc_digits_positive_and_negative() {
1652 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
1653 let ctx = interp(&wb);
1654 let f = ctx.context.get_function("", "TRUNC").unwrap();
1655 let n = lit(LiteralValue::Number(12.3456));
1656 let d2 = lit(LiteralValue::Int(2));
1657 let dneg1 = lit(LiteralValue::Int(-1));
1658 assert_eq!(
1659 f.dispatch(
1660 &[
1661 ArgumentHandle::new(&n, &ctx),
1662 ArgumentHandle::new(&d2, &ctx)
1663 ],
1664 &ctx.function_context(None)
1665 )
1666 .unwrap()
1667 .into_literal(),
1668 LiteralValue::Number(12.34)
1669 );
1670 assert_eq!(
1671 f.dispatch(
1672 &[
1673 ArgumentHandle::new(&n, &ctx),
1674 ArgumentHandle::new(&dneg1, &ctx)
1675 ],
1676 &ctx.function_context(None)
1677 )
1678 .unwrap()
1679 .into_literal(),
1680 LiteralValue::Number(10.0)
1681 );
1682 }
1683 #[test]
1684 fn trunc_default_zero_digits() {
1685 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
1686 let ctx = interp(&wb);
1687 let f = ctx.context.get_function("", "TRUNC").unwrap();
1688 let n = lit(LiteralValue::Number(-12.999));
1689 assert_eq!(
1690 f.dispatch(
1691 &[ArgumentHandle::new(&n, &ctx)],
1692 &ctx.function_context(None)
1693 )
1694 .unwrap()
1695 .into_literal(),
1696 LiteralValue::Number(-12.0)
1697 );
1698 }
1699
1700 #[test]
1702 fn round_half_away_positive_and_negative() {
1703 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
1704 let ctx = interp(&wb);
1705 let f = ctx.context.get_function("", "ROUND").unwrap();
1706 let p = lit(LiteralValue::Number(2.5));
1707 let n = lit(LiteralValue::Number(-2.5));
1708 let d0 = lit(LiteralValue::Int(0));
1709 assert_eq!(
1710 f.dispatch(
1711 &[
1712 ArgumentHandle::new(&p, &ctx),
1713 ArgumentHandle::new(&d0, &ctx)
1714 ],
1715 &ctx.function_context(None)
1716 )
1717 .unwrap()
1718 .into_literal(),
1719 LiteralValue::Number(3.0)
1720 );
1721 assert_eq!(
1722 f.dispatch(
1723 &[
1724 ArgumentHandle::new(&n, &ctx),
1725 ArgumentHandle::new(&d0, &ctx)
1726 ],
1727 &ctx.function_context(None)
1728 )
1729 .unwrap()
1730 .into_literal(),
1731 LiteralValue::Number(-3.0)
1732 );
1733 }
1734 #[test]
1735 fn round_digits_positive() {
1736 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
1737 let ctx = interp(&wb);
1738 let f = ctx.context.get_function("", "ROUND").unwrap();
1739 let n = lit(LiteralValue::Number(1.2345));
1740 let d = lit(LiteralValue::Int(3));
1741 assert_eq!(
1742 f.dispatch(
1743 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1744 &ctx.function_context(None)
1745 )
1746 .unwrap()
1747 .into_literal(),
1748 LiteralValue::Number(1.235)
1749 );
1750 }
1751
1752 #[test]
1754 fn rounddown_truncates() {
1755 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
1756 let ctx = interp(&wb);
1757 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
1758 let n = lit(LiteralValue::Number(1.299));
1759 let d = lit(LiteralValue::Int(2));
1760 assert_eq!(
1761 f.dispatch(
1762 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1763 &ctx.function_context(None)
1764 )
1765 .unwrap()
1766 .into_literal(),
1767 LiteralValue::Number(1.29)
1768 );
1769 }
1770 #[test]
1771 fn rounddown_negative_number() {
1772 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
1773 let ctx = interp(&wb);
1774 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
1775 let n = lit(LiteralValue::Number(-1.299));
1776 let d = lit(LiteralValue::Int(2));
1777 assert_eq!(
1778 f.dispatch(
1779 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1780 &ctx.function_context(None)
1781 )
1782 .unwrap()
1783 .into_literal(),
1784 LiteralValue::Number(-1.29)
1785 );
1786 }
1787
1788 #[test]
1790 fn roundup_away_from_zero() {
1791 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
1792 let ctx = interp(&wb);
1793 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
1794 let n = lit(LiteralValue::Number(1.001));
1795 let d = lit(LiteralValue::Int(2));
1796 assert_eq!(
1797 f.dispatch(
1798 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1799 &ctx.function_context(None)
1800 )
1801 .unwrap()
1802 .into_literal(),
1803 LiteralValue::Number(1.01)
1804 );
1805 }
1806 #[test]
1807 fn roundup_negative() {
1808 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
1809 let ctx = interp(&wb);
1810 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
1811 let n = lit(LiteralValue::Number(-1.001));
1812 let d = lit(LiteralValue::Int(2));
1813 assert_eq!(
1814 f.dispatch(
1815 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1816 &ctx.function_context(None)
1817 )
1818 .unwrap()
1819 .into_literal(),
1820 LiteralValue::Number(-1.01)
1821 );
1822 }
1823
1824 #[test]
1826 fn mod_positive_negative_cases() {
1827 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
1828 let ctx = interp(&wb);
1829 let f = ctx.context.get_function("", "MOD").unwrap();
1830 let a = lit(LiteralValue::Int(-3));
1831 let b = lit(LiteralValue::Int(2));
1832 let out = f
1833 .dispatch(
1834 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
1835 &ctx.function_context(None),
1836 )
1837 .unwrap();
1838 assert_eq!(out, LiteralValue::Number(1.0));
1839 let a2 = lit(LiteralValue::Int(3));
1840 let b2 = lit(LiteralValue::Int(-2));
1841 let out2 = f
1842 .dispatch(
1843 &[
1844 ArgumentHandle::new(&a2, &ctx),
1845 ArgumentHandle::new(&b2, &ctx),
1846 ],
1847 &ctx.function_context(None),
1848 )
1849 .unwrap();
1850 assert_eq!(out2, LiteralValue::Number(-1.0));
1851 }
1852 #[test]
1853 fn mod_div_by_zero_error() {
1854 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
1855 let ctx = interp(&wb);
1856 let f = ctx.context.get_function("", "MOD").unwrap();
1857 let a = lit(LiteralValue::Int(5));
1858 let zero = lit(LiteralValue::Int(0));
1859 match f
1860 .dispatch(
1861 &[
1862 ArgumentHandle::new(&a, &ctx),
1863 ArgumentHandle::new(&zero, &ctx),
1864 ],
1865 &ctx.function_context(None),
1866 )
1867 .unwrap()
1868 .into_literal()
1869 {
1870 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1871 _ => panic!(),
1872 }
1873 }
1874
1875 #[test]
1877 fn sqrt_basic_and_domain() {
1878 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SqrtFn));
1879 let ctx = interp(&wb);
1880 let f = ctx.context.get_function("", "SQRT").unwrap();
1881 let n = lit(LiteralValue::Number(9.0));
1882 let out = f
1883 .dispatch(
1884 &[ArgumentHandle::new(&n, &ctx)],
1885 &ctx.function_context(None),
1886 )
1887 .unwrap();
1888 assert_eq!(out, LiteralValue::Number(3.0));
1889 let neg = lit(LiteralValue::Number(-1.0));
1890 let out2 = f
1891 .dispatch(
1892 &[ArgumentHandle::new(&neg, &ctx)],
1893 &ctx.function_context(None),
1894 )
1895 .unwrap();
1896 assert!(matches!(out2.into_literal(), LiteralValue::Error(_)));
1897 }
1898
1899 #[test]
1900 fn power_fractional_negative_domain() {
1901 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PowerFn));
1902 let ctx = interp(&wb);
1903 let f = ctx.context.get_function("", "POWER").unwrap();
1904 let a = lit(LiteralValue::Number(-4.0));
1905 let half = lit(LiteralValue::Number(0.5));
1906 let out = f
1907 .dispatch(
1908 &[
1909 ArgumentHandle::new(&a, &ctx),
1910 ArgumentHandle::new(&half, &ctx),
1911 ],
1912 &ctx.function_context(None),
1913 )
1914 .unwrap();
1915 assert!(matches!(out.into_literal(), LiteralValue::Error(_))); }
1917
1918 #[test]
1919 fn log_variants() {
1920 let wb = TestWorkbook::new()
1921 .with_function(std::sync::Arc::new(LogFn))
1922 .with_function(std::sync::Arc::new(Log10Fn))
1923 .with_function(std::sync::Arc::new(LnFn));
1924 let ctx = interp(&wb);
1925 let logf = ctx.context.get_function("", "LOG").unwrap();
1926 let log10f = ctx.context.get_function("", "LOG10").unwrap();
1927 let lnf = ctx.context.get_function("", "LN").unwrap();
1928 let n = lit(LiteralValue::Number(100.0));
1929 let base = lit(LiteralValue::Number(10.0));
1930 assert_eq!(
1931 logf.dispatch(
1932 &[
1933 ArgumentHandle::new(&n, &ctx),
1934 ArgumentHandle::new(&base, &ctx)
1935 ],
1936 &ctx.function_context(None)
1937 )
1938 .unwrap()
1939 .into_literal(),
1940 LiteralValue::Number(2.0)
1941 );
1942 assert_eq!(
1943 log10f
1944 .dispatch(
1945 &[ArgumentHandle::new(&n, &ctx)],
1946 &ctx.function_context(None)
1947 )
1948 .unwrap()
1949 .into_literal(),
1950 LiteralValue::Number(2.0)
1951 );
1952 assert_eq!(
1953 lnf.dispatch(
1954 &[ArgumentHandle::new(&n, &ctx)],
1955 &ctx.function_context(None)
1956 )
1957 .unwrap()
1958 .into_literal(),
1959 LiteralValue::Number(100.0f64.ln())
1960 );
1961 }
1962 #[test]
1963 fn ceiling_floor_basic() {
1964 let wb = TestWorkbook::new()
1965 .with_function(std::sync::Arc::new(CeilingFn))
1966 .with_function(std::sync::Arc::new(FloorFn))
1967 .with_function(std::sync::Arc::new(CeilingMathFn))
1968 .with_function(std::sync::Arc::new(FloorMathFn));
1969 let ctx = interp(&wb);
1970 let c = ctx.context.get_function("", "CEILING").unwrap();
1971 let f = ctx.context.get_function("", "FLOOR").unwrap();
1972 let n = lit(LiteralValue::Number(5.1));
1973 let sig = lit(LiteralValue::Number(2.0));
1974 assert_eq!(
1975 c.dispatch(
1976 &[
1977 ArgumentHandle::new(&n, &ctx),
1978 ArgumentHandle::new(&sig, &ctx)
1979 ],
1980 &ctx.function_context(None)
1981 )
1982 .unwrap()
1983 .into_literal(),
1984 LiteralValue::Number(6.0)
1985 );
1986 assert_eq!(
1987 f.dispatch(
1988 &[
1989 ArgumentHandle::new(&n, &ctx),
1990 ArgumentHandle::new(&sig, &ctx)
1991 ],
1992 &ctx.function_context(None)
1993 )
1994 .unwrap()
1995 .into_literal(),
1996 LiteralValue::Number(4.0)
1997 );
1998 }
1999
2000 #[test]
2001 fn quotient_basic_and_div_zero() {
2002 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(QuotientFn));
2003 let ctx = interp(&wb);
2004 let f = ctx.context.get_function("", "QUOTIENT").unwrap();
2005
2006 let ten = lit(LiteralValue::Int(10));
2007 let three = lit(LiteralValue::Int(3));
2008 assert_eq!(
2009 f.dispatch(
2010 &[
2011 ArgumentHandle::new(&ten, &ctx),
2012 ArgumentHandle::new(&three, &ctx),
2013 ],
2014 &ctx.function_context(None),
2015 )
2016 .unwrap()
2017 .into_literal(),
2018 LiteralValue::Number(3.0)
2019 );
2020
2021 let neg_ten = lit(LiteralValue::Int(-10));
2022 assert_eq!(
2023 f.dispatch(
2024 &[
2025 ArgumentHandle::new(&neg_ten, &ctx),
2026 ArgumentHandle::new(&three, &ctx),
2027 ],
2028 &ctx.function_context(None),
2029 )
2030 .unwrap()
2031 .into_literal(),
2032 LiteralValue::Number(-3.0)
2033 );
2034
2035 let zero = lit(LiteralValue::Int(0));
2036 match f
2037 .dispatch(
2038 &[
2039 ArgumentHandle::new(&ten, &ctx),
2040 ArgumentHandle::new(&zero, &ctx),
2041 ],
2042 &ctx.function_context(None),
2043 )
2044 .unwrap()
2045 .into_literal()
2046 {
2047 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
2048 other => panic!("expected #DIV/0!, got {other:?}"),
2049 }
2050 }
2051
2052 #[test]
2053 fn even_odd_examples() {
2054 let wb = TestWorkbook::new()
2055 .with_function(std::sync::Arc::new(EvenFn))
2056 .with_function(std::sync::Arc::new(OddFn));
2057 let ctx = interp(&wb);
2058
2059 let even = ctx.context.get_function("", "EVEN").unwrap();
2060 let odd = ctx.context.get_function("", "ODD").unwrap();
2061
2062 let one_half = lit(LiteralValue::Number(1.5));
2063 let three = lit(LiteralValue::Int(3));
2064 let neg_one = lit(LiteralValue::Int(-1));
2065 let two = lit(LiteralValue::Int(2));
2066 let zero = lit(LiteralValue::Int(0));
2067
2068 assert_eq!(
2069 even.dispatch(
2070 &[ArgumentHandle::new(&one_half, &ctx)],
2071 &ctx.function_context(None),
2072 )
2073 .unwrap()
2074 .into_literal(),
2075 LiteralValue::Number(2.0)
2076 );
2077 assert_eq!(
2078 even.dispatch(
2079 &[ArgumentHandle::new(&three, &ctx)],
2080 &ctx.function_context(None),
2081 )
2082 .unwrap()
2083 .into_literal(),
2084 LiteralValue::Number(4.0)
2085 );
2086 assert_eq!(
2087 even.dispatch(
2088 &[ArgumentHandle::new(&neg_one, &ctx)],
2089 &ctx.function_context(None),
2090 )
2091 .unwrap()
2092 .into_literal(),
2093 LiteralValue::Number(-2.0)
2094 );
2095 assert_eq!(
2096 even.dispatch(
2097 &[ArgumentHandle::new(&two, &ctx)],
2098 &ctx.function_context(None),
2099 )
2100 .unwrap()
2101 .into_literal(),
2102 LiteralValue::Number(2.0)
2103 );
2104
2105 assert_eq!(
2106 odd.dispatch(
2107 &[ArgumentHandle::new(&one_half, &ctx)],
2108 &ctx.function_context(None),
2109 )
2110 .unwrap()
2111 .into_literal(),
2112 LiteralValue::Number(3.0)
2113 );
2114 assert_eq!(
2115 odd.dispatch(
2116 &[ArgumentHandle::new(&two, &ctx)],
2117 &ctx.function_context(None),
2118 )
2119 .unwrap()
2120 .into_literal(),
2121 LiteralValue::Number(3.0)
2122 );
2123 assert_eq!(
2124 odd.dispatch(
2125 &[ArgumentHandle::new(&neg_one, &ctx)],
2126 &ctx.function_context(None),
2127 )
2128 .unwrap()
2129 .into_literal(),
2130 LiteralValue::Number(-1.0)
2131 );
2132 assert_eq!(
2133 odd.dispatch(
2134 &[ArgumentHandle::new(&zero, &ctx)],
2135 &ctx.function_context(None),
2136 )
2137 .unwrap()
2138 .into_literal(),
2139 LiteralValue::Number(1.0)
2140 );
2141 }
2142
2143 #[test]
2144 fn sqrtpi_multinomial_and_seriessum_examples() {
2145 let wb = TestWorkbook::new()
2146 .with_function(std::sync::Arc::new(SqrtPiFn))
2147 .with_function(std::sync::Arc::new(MultinomialFn))
2148 .with_function(std::sync::Arc::new(SeriesSumFn));
2149 let ctx = interp(&wb);
2150
2151 let sqrtpi = ctx.context.get_function("", "SQRTPI").unwrap();
2152 let one = lit(LiteralValue::Int(1));
2153 match sqrtpi
2154 .dispatch(
2155 &[ArgumentHandle::new(&one, &ctx)],
2156 &ctx.function_context(None),
2157 )
2158 .unwrap()
2159 .into_literal()
2160 {
2161 LiteralValue::Number(v) => assert!((v - std::f64::consts::PI.sqrt()).abs() < 1e-12),
2162 other => panic!("expected numeric SQRTPI, got {other:?}"),
2163 }
2164
2165 let multinomial = ctx.context.get_function("", "MULTINOMIAL").unwrap();
2166 let two = lit(LiteralValue::Int(2));
2167 let three = lit(LiteralValue::Int(3));
2168 let four = lit(LiteralValue::Int(4));
2169 assert_eq!(
2170 multinomial
2171 .dispatch(
2172 &[
2173 ArgumentHandle::new(&two, &ctx),
2174 ArgumentHandle::new(&three, &ctx),
2175 ArgumentHandle::new(&four, &ctx),
2176 ],
2177 &ctx.function_context(None),
2178 )
2179 .unwrap()
2180 .into_literal(),
2181 LiteralValue::Number(1260.0)
2182 );
2183
2184 let seriessum = ctx.context.get_function("", "SERIESSUM").unwrap();
2185 let x = lit(LiteralValue::Int(2));
2186 let n0 = lit(LiteralValue::Int(0));
2187 let m1 = lit(LiteralValue::Int(1));
2188 let coeffs = ASTNode::new(
2189 ASTNodeType::Literal(LiteralValue::Array(vec![vec![
2190 LiteralValue::Int(1),
2191 LiteralValue::Int(2),
2192 LiteralValue::Int(3),
2193 ]])),
2194 None,
2195 );
2196 assert_eq!(
2197 seriessum
2198 .dispatch(
2199 &[
2200 ArgumentHandle::new(&x, &ctx),
2201 ArgumentHandle::new(&n0, &ctx),
2202 ArgumentHandle::new(&m1, &ctx),
2203 ArgumentHandle::new(&coeffs, &ctx),
2204 ],
2205 &ctx.function_context(None),
2206 )
2207 .unwrap()
2208 .into_literal(),
2209 LiteralValue::Number(17.0)
2210 );
2211 }
2212
2213 #[test]
2214 fn sumsq_basic() {
2215 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumsqFn));
2216 let ctx = interp(&wb);
2217 let f = ctx.context.get_function("", "SUMSQ").unwrap();
2218 let a = lit(LiteralValue::Int(3));
2219 let b = lit(LiteralValue::Int(4));
2220 assert_eq!(
2221 f.dispatch(
2222 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
2223 &ctx.function_context(None)
2224 )
2225 .unwrap()
2226 .into_literal(),
2227 LiteralValue::Number(25.0)
2228 );
2229 }
2230
2231 #[test]
2232 fn mround_sign_and_midpoint() {
2233 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(MroundFn));
2234 let ctx = interp(&wb);
2235 let f = ctx.context.get_function("", "MROUND").unwrap();
2236
2237 let n = lit(LiteralValue::Number(1.3));
2238 let m = lit(LiteralValue::Number(0.2));
2239 match f
2240 .dispatch(
2241 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&m, &ctx)],
2242 &ctx.function_context(None),
2243 )
2244 .unwrap()
2245 .into_literal()
2246 {
2247 LiteralValue::Number(v) => assert!((v - 1.4).abs() < 1e-12),
2248 other => panic!("expected numeric result, got {other:?}"),
2249 }
2250
2251 let bad_m = lit(LiteralValue::Number(-2.0));
2252 let five = lit(LiteralValue::Number(5.0));
2253 match f
2254 .dispatch(
2255 &[
2256 ArgumentHandle::new(&five, &ctx),
2257 ArgumentHandle::new(&bad_m, &ctx),
2258 ],
2259 &ctx.function_context(None),
2260 )
2261 .unwrap()
2262 .into_literal()
2263 {
2264 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
2265 other => panic!("expected #NUM!, got {other:?}"),
2266 }
2267 }
2268
2269 #[test]
2270 fn roman_and_arabic_examples() {
2271 let wb = TestWorkbook::new()
2272 .with_function(std::sync::Arc::new(RomanFn))
2273 .with_function(std::sync::Arc::new(ArabicFn));
2274 let ctx = interp(&wb);
2275
2276 let roman = ctx.context.get_function("", "ROMAN").unwrap();
2277 let n499 = lit(LiteralValue::Int(499));
2278 let out = roman
2279 .dispatch(
2280 &[ArgumentHandle::new(&n499, &ctx)],
2281 &ctx.function_context(None),
2282 )
2283 .unwrap()
2284 .into_literal();
2285 assert_eq!(out, LiteralValue::Text("CDXCIX".to_string()));
2286
2287 let form4 = lit(LiteralValue::Int(4));
2288 let out_form4 = roman
2289 .dispatch(
2290 &[
2291 ArgumentHandle::new(&n499, &ctx),
2292 ArgumentHandle::new(&form4, &ctx),
2293 ],
2294 &ctx.function_context(None),
2295 )
2296 .unwrap()
2297 .into_literal();
2298 assert_eq!(out_form4, LiteralValue::Text("ID".to_string()));
2299
2300 let arabic = ctx.context.get_function("", "ARABIC").unwrap();
2301 let roman_text = lit(LiteralValue::Text("CDXCIX".to_string()));
2302 let out_arabic = arabic
2303 .dispatch(
2304 &[ArgumentHandle::new(&roman_text, &ctx)],
2305 &ctx.function_context(None),
2306 )
2307 .unwrap()
2308 .into_literal();
2309 assert_eq!(out_arabic, LiteralValue::Number(499.0));
2310 }
2311}