1use super::super::utils::{ARG_NUM_LENIENT_ONE, ARG_NUM_LENIENT_TWO, coerce_num};
2use crate::args::ArgSchema;
3use crate::function::Function;
4use crate::traits::{ArgumentHandle, FunctionContext};
5use formualizer_common::{ExcelError, LiteralValue};
6use formualizer_macros::func_caps;
7
8#[derive(Debug)]
9pub struct AbsFn;
10impl Function for AbsFn {
11 func_caps!(PURE);
12 fn name(&self) -> &'static str {
13 "ABS"
14 }
15 fn min_args(&self) -> usize {
16 1
17 }
18 fn arg_schema(&self) -> &'static [ArgSchema] {
19 &ARG_NUM_LENIENT_ONE[..]
20 }
21 fn eval<'a, 'b, 'c>(
22 &self,
23 args: &'c [ArgumentHandle<'a, 'b>],
24 _: &dyn FunctionContext<'b>,
25 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
26 let v = args[0].value()?.into_literal();
27 match v {
28 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
29 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
30 coerce_num(&other)?.abs(),
31 ))),
32 }
33 }
34}
35
36#[derive(Debug)]
37pub struct SignFn;
38impl Function for SignFn {
39 func_caps!(PURE);
40 fn name(&self) -> &'static str {
41 "SIGN"
42 }
43 fn min_args(&self) -> usize {
44 1
45 }
46 fn arg_schema(&self) -> &'static [ArgSchema] {
47 &ARG_NUM_LENIENT_ONE[..]
48 }
49 fn eval<'a, 'b, 'c>(
50 &self,
51 args: &'c [ArgumentHandle<'a, 'b>],
52 _: &dyn FunctionContext<'b>,
53 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
54 let v = args[0].value()?.into_literal();
55 match v {
56 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
57 other => {
58 let n = coerce_num(&other)?;
59 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
60 if n > 0.0 {
61 1.0
62 } else if n < 0.0 {
63 -1.0
64 } else {
65 0.0
66 },
67 )))
68 }
69 }
70 }
71}
72
73#[derive(Debug)]
74pub struct IntFn; impl Function for IntFn {
76 func_caps!(PURE);
77 fn name(&self) -> &'static str {
78 "INT"
79 }
80 fn min_args(&self) -> usize {
81 1
82 }
83 fn arg_schema(&self) -> &'static [ArgSchema] {
84 &ARG_NUM_LENIENT_ONE[..]
85 }
86 fn eval<'a, 'b, 'c>(
87 &self,
88 args: &'c [ArgumentHandle<'a, 'b>],
89 _: &dyn FunctionContext<'b>,
90 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
91 let v = args[0].value()?.into_literal();
92 match v {
93 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
94 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
95 coerce_num(&other)?.floor(),
96 ))),
97 }
98 }
99}
100
101#[derive(Debug)]
102pub struct TruncFn; impl Function for TruncFn {
104 func_caps!(PURE);
105 fn name(&self) -> &'static str {
106 "TRUNC"
107 }
108 fn min_args(&self) -> usize {
109 1
110 }
111 fn variadic(&self) -> bool {
112 true
113 }
114 fn arg_schema(&self) -> &'static [ArgSchema] {
115 &ARG_NUM_LENIENT_TWO[..]
116 }
117 fn eval<'a, 'b, 'c>(
118 &self,
119 args: &'c [ArgumentHandle<'a, 'b>],
120 _: &dyn FunctionContext<'b>,
121 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
122 if args.is_empty() || args.len() > 2 {
123 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
124 ExcelError::new_value(),
125 )));
126 }
127 let mut n = match args[0].value()?.into_literal() {
128 LiteralValue::Error(e) => {
129 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
130 }
131 other => coerce_num(&other)?,
132 };
133 let digits: i32 = if args.len() == 2 {
134 match args[1].value()?.into_literal() {
135 LiteralValue::Error(e) => {
136 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
137 }
138 other => coerce_num(&other)? as i32,
139 }
140 } else {
141 0
142 };
143 if digits >= 0 {
144 let f = 10f64.powi(digits);
145 n = (n * f).trunc() / f;
146 } else {
147 let f = 10f64.powi(-digits);
148 n = (n / f).trunc() * f;
149 }
150 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(n)))
151 }
152}
153
154#[derive(Debug)]
155pub struct RoundFn; impl Function for RoundFn {
157 func_caps!(PURE);
158 fn name(&self) -> &'static str {
159 "ROUND"
160 }
161 fn min_args(&self) -> usize {
162 2
163 }
164 fn arg_schema(&self) -> &'static [ArgSchema] {
165 &ARG_NUM_LENIENT_TWO[..]
166 }
167 fn eval<'a, 'b, 'c>(
168 &self,
169 args: &'c [ArgumentHandle<'a, 'b>],
170 _: &dyn FunctionContext<'b>,
171 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
172 let n = match args[0].value()?.into_literal() {
173 LiteralValue::Error(e) => {
174 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
175 }
176 other => coerce_num(&other)?,
177 };
178 let digits = match args[1].value()?.into_literal() {
179 LiteralValue::Error(e) => {
180 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
181 }
182 other => coerce_num(&other)? as i32,
183 };
184 let f = 10f64.powi(digits.abs());
185 let out = if digits >= 0 {
186 (n * f).round() / f
187 } else {
188 (n / f).round() * f
189 };
190 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
191 }
192}
193
194#[derive(Debug)]
195pub struct RoundDownFn; impl Function for RoundDownFn {
197 func_caps!(PURE);
198 fn name(&self) -> &'static str {
199 "ROUNDDOWN"
200 }
201 fn min_args(&self) -> usize {
202 2
203 }
204 fn arg_schema(&self) -> &'static [ArgSchema] {
205 &ARG_NUM_LENIENT_TWO[..]
206 }
207 fn eval<'a, 'b, 'c>(
208 &self,
209 args: &'c [ArgumentHandle<'a, 'b>],
210 _: &dyn FunctionContext<'b>,
211 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
212 let n = match args[0].value()?.into_literal() {
213 LiteralValue::Error(e) => {
214 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
215 }
216 other => coerce_num(&other)?,
217 };
218 let digits = match args[1].value()?.into_literal() {
219 LiteralValue::Error(e) => {
220 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
221 }
222 other => coerce_num(&other)? as i32,
223 };
224 let f = 10f64.powi(digits.abs());
225 let out = if digits >= 0 {
226 (n * f).trunc() / f
227 } else {
228 (n / f).trunc() * f
229 };
230 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
231 }
232}
233
234#[derive(Debug)]
235pub struct RoundUpFn; impl Function for RoundUpFn {
237 func_caps!(PURE);
238 fn name(&self) -> &'static str {
239 "ROUNDUP"
240 }
241 fn min_args(&self) -> usize {
242 2
243 }
244 fn arg_schema(&self) -> &'static [ArgSchema] {
245 &ARG_NUM_LENIENT_TWO[..]
246 }
247 fn eval<'a, 'b, 'c>(
248 &self,
249 args: &'c [ArgumentHandle<'a, 'b>],
250 _: &dyn FunctionContext<'b>,
251 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
252 let n = match args[0].value()?.into_literal() {
253 LiteralValue::Error(e) => {
254 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
255 }
256 other => coerce_num(&other)?,
257 };
258 let digits = match args[1].value()?.into_literal() {
259 LiteralValue::Error(e) => {
260 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
261 }
262 other => coerce_num(&other)? as i32,
263 };
264 let f = 10f64.powi(digits.abs());
265 let mut scaled = if digits >= 0 { n * f } else { n / f };
266 if scaled > 0.0 {
267 scaled = scaled.ceil();
268 } else {
269 scaled = scaled.floor();
270 }
271 let out = if digits >= 0 { scaled / f } else { scaled * f };
272 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
273 }
274}
275
276#[derive(Debug)]
277pub struct ModFn; impl Function for ModFn {
279 func_caps!(PURE);
280 fn name(&self) -> &'static str {
281 "MOD"
282 }
283 fn min_args(&self) -> usize {
284 2
285 }
286 fn arg_schema(&self) -> &'static [ArgSchema] {
287 &ARG_NUM_LENIENT_TWO[..]
288 }
289 fn eval<'a, 'b, 'c>(
290 &self,
291 args: &'c [ArgumentHandle<'a, 'b>],
292 _: &dyn FunctionContext<'b>,
293 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
294 let x = match args[0].value()?.into_literal() {
295 LiteralValue::Error(e) => {
296 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
297 }
298 other => coerce_num(&other)?,
299 };
300 let y = match args[1].value()?.into_literal() {
301 LiteralValue::Error(e) => {
302 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
303 }
304 other => coerce_num(&other)?,
305 };
306 if y == 0.0 {
307 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
308 ExcelError::from_error_string("#DIV/0!"),
309 )));
310 }
311 let m = x % y;
312 let mut r = if m == 0.0 {
313 0.0
314 } else if (y > 0.0 && m < 0.0) || (y < 0.0 && m > 0.0) {
315 m + y
316 } else {
317 m
318 };
319 if r == -0.0 {
320 r = 0.0;
321 }
322 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(r)))
323 }
324}
325
326#[derive(Debug)]
329pub struct CeilingFn; impl Function for CeilingFn {
331 func_caps!(PURE);
332 fn name(&self) -> &'static str {
333 "CEILING"
334 }
335 fn min_args(&self) -> usize {
336 1
337 }
338 fn variadic(&self) -> bool {
339 true
340 }
341 fn arg_schema(&self) -> &'static [ArgSchema] {
342 &ARG_NUM_LENIENT_TWO[..]
343 }
344 fn eval<'a, 'b, 'c>(
345 &self,
346 args: &'c [ArgumentHandle<'a, 'b>],
347 _: &dyn FunctionContext<'b>,
348 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
349 if args.is_empty() || args.len() > 2 {
350 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
351 ExcelError::new_value(),
352 )));
353 }
354 let n = match args[0].value()?.into_literal() {
355 LiteralValue::Error(e) => {
356 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
357 }
358 other => coerce_num(&other)?,
359 };
360 let mut sig = if args.len() == 2 {
361 match args[1].value()?.into_literal() {
362 LiteralValue::Error(e) => {
363 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
364 }
365 other => coerce_num(&other)?,
366 }
367 } else {
368 1.0
369 };
370 if sig == 0.0 {
371 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
372 ExcelError::from_error_string("#DIV/0!"),
373 )));
374 }
375 if sig < 0.0 {
376 sig = sig.abs(); }
378 let k = (n / sig).ceil();
379 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
380 k * sig,
381 )))
382 }
383}
384
385#[derive(Debug)]
386pub struct CeilingMathFn; impl Function for CeilingMathFn {
388 func_caps!(PURE);
389 fn name(&self) -> &'static str {
390 "CEILING.MATH"
391 }
392 fn min_args(&self) -> usize {
393 1
394 }
395 fn variadic(&self) -> bool {
396 true
397 }
398 fn arg_schema(&self) -> &'static [ArgSchema] {
399 &ARG_NUM_LENIENT_TWO[..]
400 } fn eval<'a, 'b, 'c>(
402 &self,
403 args: &'c [ArgumentHandle<'a, 'b>],
404 _: &dyn FunctionContext<'b>,
405 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
406 if args.is_empty() || args.len() > 3 {
407 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
408 ExcelError::new_value(),
409 )));
410 }
411 let n = match args[0].value()?.into_literal() {
412 LiteralValue::Error(e) => {
413 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
414 }
415 other => coerce_num(&other)?,
416 };
417 let sig = if args.len() >= 2 {
418 match args[1].value()?.into_literal() {
419 LiteralValue::Error(e) => {
420 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
421 }
422 other => {
423 let v = coerce_num(&other)?;
424 if v == 0.0 { 1.0 } else { v.abs() }
425 }
426 }
427 } else {
428 1.0
429 };
430 let mode_nonzero = if args.len() == 3 {
431 match args[2].value()?.into_literal() {
432 LiteralValue::Error(e) => {
433 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
434 }
435 other => coerce_num(&other)? != 0.0,
436 }
437 } else {
438 false
439 };
440 let result = if n >= 0.0 {
441 (n / sig).ceil() * sig
442 } else if mode_nonzero {
443 (n / sig).floor() * sig } else {
445 (n / sig).ceil() * sig };
447 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
448 result,
449 )))
450 }
451}
452
453#[derive(Debug)]
454pub struct FloorFn; impl Function for FloorFn {
456 func_caps!(PURE);
457 fn name(&self) -> &'static str {
458 "FLOOR"
459 }
460 fn min_args(&self) -> usize {
461 1
462 }
463 fn variadic(&self) -> bool {
464 true
465 }
466 fn arg_schema(&self) -> &'static [ArgSchema] {
467 &ARG_NUM_LENIENT_TWO[..]
468 }
469 fn eval<'a, 'b, 'c>(
470 &self,
471 args: &'c [ArgumentHandle<'a, 'b>],
472 _: &dyn FunctionContext<'b>,
473 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
474 if args.is_empty() || args.len() > 2 {
475 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
476 ExcelError::new_value(),
477 )));
478 }
479 let n = match args[0].value()?.into_literal() {
480 LiteralValue::Error(e) => {
481 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
482 }
483 other => coerce_num(&other)?,
484 };
485 let mut sig = if args.len() == 2 {
486 match args[1].value()?.into_literal() {
487 LiteralValue::Error(e) => {
488 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
489 }
490 other => coerce_num(&other)?,
491 }
492 } else {
493 1.0
494 };
495 if sig == 0.0 {
496 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
497 ExcelError::from_error_string("#DIV/0!"),
498 )));
499 }
500 if sig < 0.0 {
501 sig = sig.abs();
502 }
503 let k = (n / sig).floor();
504 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
505 k * sig,
506 )))
507 }
508}
509
510#[derive(Debug)]
511pub struct FloorMathFn; impl Function for FloorMathFn {
513 func_caps!(PURE);
514 fn name(&self) -> &'static str {
515 "FLOOR.MATH"
516 }
517 fn min_args(&self) -> usize {
518 1
519 }
520 fn variadic(&self) -> bool {
521 true
522 }
523 fn arg_schema(&self) -> &'static [ArgSchema] {
524 &ARG_NUM_LENIENT_TWO[..]
525 }
526 fn eval<'a, 'b, 'c>(
527 &self,
528 args: &'c [ArgumentHandle<'a, 'b>],
529 _: &dyn FunctionContext<'b>,
530 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
531 if args.is_empty() || args.len() > 3 {
532 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
533 ExcelError::new_value(),
534 )));
535 }
536 let n = match args[0].value()?.into_literal() {
537 LiteralValue::Error(e) => {
538 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
539 }
540 other => coerce_num(&other)?,
541 };
542 let sig = if args.len() >= 2 {
543 match args[1].value()?.into_literal() {
544 LiteralValue::Error(e) => {
545 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
546 }
547 other => {
548 let v = coerce_num(&other)?;
549 if v == 0.0 { 1.0 } else { v.abs() }
550 }
551 }
552 } else {
553 1.0
554 };
555 let mode_nonzero = if args.len() == 3 {
556 match args[2].value()?.into_literal() {
557 LiteralValue::Error(e) => {
558 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
559 }
560 other => coerce_num(&other)? != 0.0,
561 }
562 } else {
563 false
564 };
565 let result = if n >= 0.0 {
566 (n / sig).floor() * sig
567 } else if mode_nonzero {
568 (n / sig).ceil() * sig
569 } else {
570 (n / sig).floor() * sig
571 };
572 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
573 result,
574 )))
575 }
576}
577
578#[derive(Debug)]
579pub struct SqrtFn; impl Function for SqrtFn {
581 func_caps!(PURE);
582 fn name(&self) -> &'static str {
583 "SQRT"
584 }
585 fn min_args(&self) -> usize {
586 1
587 }
588 fn arg_schema(&self) -> &'static [ArgSchema] {
589 &ARG_NUM_LENIENT_ONE[..]
590 }
591 fn eval<'a, 'b, 'c>(
592 &self,
593 args: &'c [ArgumentHandle<'a, 'b>],
594 _: &dyn FunctionContext<'b>,
595 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
596 let n = match args[0].value()?.into_literal() {
597 LiteralValue::Error(e) => {
598 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
599 }
600 other => coerce_num(&other)?,
601 };
602 if n < 0.0 {
603 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
604 ExcelError::new_num(),
605 )));
606 }
607 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
608 n.sqrt(),
609 )))
610 }
611}
612
613#[derive(Debug)]
614pub struct PowerFn; impl Function for PowerFn {
616 func_caps!(PURE);
617 fn name(&self) -> &'static str {
618 "POWER"
619 }
620 fn min_args(&self) -> usize {
621 2
622 }
623 fn arg_schema(&self) -> &'static [ArgSchema] {
624 &ARG_NUM_LENIENT_TWO[..]
625 }
626 fn eval<'a, 'b, 'c>(
627 &self,
628 args: &'c [ArgumentHandle<'a, 'b>],
629 _: &dyn FunctionContext<'b>,
630 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
631 let base = match args[0].value()?.into_literal() {
632 LiteralValue::Error(e) => {
633 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
634 }
635 other => coerce_num(&other)?,
636 };
637 let expv = match args[1].value()?.into_literal() {
638 LiteralValue::Error(e) => {
639 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
640 }
641 other => coerce_num(&other)?,
642 };
643 if base < 0.0 && (expv.fract().abs() > 1e-12) {
644 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
645 ExcelError::new_num(),
646 )));
647 }
648 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
649 base.powf(expv),
650 )))
651 }
652}
653
654#[derive(Debug)]
655pub struct ExpFn; impl Function for ExpFn {
657 func_caps!(PURE);
658 fn name(&self) -> &'static str {
659 "EXP"
660 }
661 fn min_args(&self) -> usize {
662 1
663 }
664 fn arg_schema(&self) -> &'static [ArgSchema] {
665 &ARG_NUM_LENIENT_ONE[..]
666 }
667 fn eval<'a, 'b, 'c>(
668 &self,
669 args: &'c [ArgumentHandle<'a, 'b>],
670 _: &dyn FunctionContext<'b>,
671 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
672 let n = match args[0].value()?.into_literal() {
673 LiteralValue::Error(e) => {
674 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
675 }
676 other => coerce_num(&other)?,
677 };
678 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
679 n.exp(),
680 )))
681 }
682}
683
684#[derive(Debug)]
685pub struct LnFn; impl Function for LnFn {
687 func_caps!(PURE);
688 fn name(&self) -> &'static str {
689 "LN"
690 }
691 fn min_args(&self) -> usize {
692 1
693 }
694 fn arg_schema(&self) -> &'static [ArgSchema] {
695 &ARG_NUM_LENIENT_ONE[..]
696 }
697 fn eval<'a, 'b, 'c>(
698 &self,
699 args: &'c [ArgumentHandle<'a, 'b>],
700 _: &dyn FunctionContext<'b>,
701 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
702 let n = match args[0].value()?.into_literal() {
703 LiteralValue::Error(e) => {
704 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
705 }
706 other => coerce_num(&other)?,
707 };
708 if n <= 0.0 {
709 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
710 ExcelError::new_num(),
711 )));
712 }
713 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
714 n.ln(),
715 )))
716 }
717}
718
719#[derive(Debug)]
720pub struct LogFn; impl Function for LogFn {
722 func_caps!(PURE);
723 fn name(&self) -> &'static str {
724 "LOG"
725 }
726 fn min_args(&self) -> usize {
727 1
728 }
729 fn variadic(&self) -> bool {
730 true
731 }
732 fn arg_schema(&self) -> &'static [ArgSchema] {
733 &ARG_NUM_LENIENT_TWO[..]
734 }
735 fn eval<'a, 'b, 'c>(
736 &self,
737 args: &'c [ArgumentHandle<'a, 'b>],
738 _: &dyn FunctionContext<'b>,
739 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
740 if args.is_empty() || args.len() > 2 {
741 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
742 ExcelError::new_value(),
743 )));
744 }
745 let n = match args[0].value()?.into_literal() {
746 LiteralValue::Error(e) => {
747 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
748 }
749 other => coerce_num(&other)?,
750 };
751 let base = if args.len() == 2 {
752 match args[1].value()?.into_literal() {
753 LiteralValue::Error(e) => {
754 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
755 }
756 other => coerce_num(&other)?,
757 }
758 } else {
759 10.0
760 };
761 if n <= 0.0 || base <= 0.0 || (base - 1.0).abs() < 1e-12 {
762 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
763 ExcelError::new_num(),
764 )));
765 }
766 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
767 n.log(base),
768 )))
769 }
770}
771
772#[derive(Debug)]
773pub struct Log10Fn; impl Function for Log10Fn {
775 func_caps!(PURE);
776 fn name(&self) -> &'static str {
777 "LOG10"
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<'a, 'b, 'c>(
786 &self,
787 args: &'c [ArgumentHandle<'a, 'b>],
788 _: &dyn FunctionContext<'b>,
789 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
790 let n = match args[0].value()?.into_literal() {
791 LiteralValue::Error(e) => {
792 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
793 }
794 other => coerce_num(&other)?,
795 };
796 if n <= 0.0 {
797 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
798 ExcelError::new_num(),
799 )));
800 }
801 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
802 n.log10(),
803 )))
804 }
805}
806
807pub fn register_builtins() {
808 use std::sync::Arc;
809 crate::function_registry::register_function(Arc::new(AbsFn));
810 crate::function_registry::register_function(Arc::new(SignFn));
811 crate::function_registry::register_function(Arc::new(IntFn));
812 crate::function_registry::register_function(Arc::new(TruncFn));
813 crate::function_registry::register_function(Arc::new(RoundFn));
814 crate::function_registry::register_function(Arc::new(RoundDownFn));
815 crate::function_registry::register_function(Arc::new(RoundUpFn));
816 crate::function_registry::register_function(Arc::new(ModFn));
817 crate::function_registry::register_function(Arc::new(CeilingFn));
818 crate::function_registry::register_function(Arc::new(CeilingMathFn));
819 crate::function_registry::register_function(Arc::new(FloorFn));
820 crate::function_registry::register_function(Arc::new(FloorMathFn));
821 crate::function_registry::register_function(Arc::new(SqrtFn));
822 crate::function_registry::register_function(Arc::new(PowerFn));
823 crate::function_registry::register_function(Arc::new(ExpFn));
824 crate::function_registry::register_function(Arc::new(LnFn));
825 crate::function_registry::register_function(Arc::new(LogFn));
826 crate::function_registry::register_function(Arc::new(Log10Fn));
827}
828
829#[cfg(test)]
830mod tests_numeric {
831 use super::*;
832 use crate::test_workbook::TestWorkbook;
833 use crate::traits::ArgumentHandle;
834 use formualizer_common::LiteralValue;
835 use formualizer_parse::parser::{ASTNode, ASTNodeType};
836
837 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
838 wb.interpreter()
839 }
840 fn lit(v: LiteralValue) -> ASTNode {
841 ASTNode::new(ASTNodeType::Literal(v), None)
842 }
843
844 #[test]
846 fn abs_basic() {
847 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
848 let ctx = interp(&wb);
849 let n = lit(LiteralValue::Number(-5.5));
850 let f = ctx.context.get_function("", "ABS").unwrap();
851 assert_eq!(
852 f.dispatch(
853 &[ArgumentHandle::new(&n, &ctx)],
854 &ctx.function_context(None)
855 )
856 .unwrap()
857 .into_literal(),
858 LiteralValue::Number(5.5)
859 );
860 }
861 #[test]
862 fn abs_error_passthrough() {
863 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
864 let ctx = interp(&wb);
865 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
866 "#VALUE!",
867 )));
868 let f = ctx.context.get_function("", "ABS").unwrap();
869 match f
870 .dispatch(
871 &[ArgumentHandle::new(&e, &ctx)],
872 &ctx.function_context(None),
873 )
874 .unwrap()
875 .into_literal()
876 {
877 LiteralValue::Error(er) => assert_eq!(er, "#VALUE!"),
878 _ => panic!(),
879 }
880 }
881
882 #[test]
884 fn sign_neg_zero_pos() {
885 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
886 let ctx = interp(&wb);
887 let f = ctx.context.get_function("", "SIGN").unwrap();
888 let neg = lit(LiteralValue::Number(-3.2));
889 let zero = lit(LiteralValue::Int(0));
890 let pos = lit(LiteralValue::Int(9));
891 assert_eq!(
892 f.dispatch(
893 &[ArgumentHandle::new(&neg, &ctx)],
894 &ctx.function_context(None)
895 )
896 .unwrap()
897 .into_literal(),
898 LiteralValue::Number(-1.0)
899 );
900 assert_eq!(
901 f.dispatch(
902 &[ArgumentHandle::new(&zero, &ctx)],
903 &ctx.function_context(None)
904 )
905 .unwrap()
906 .into_literal(),
907 LiteralValue::Number(0.0)
908 );
909 assert_eq!(
910 f.dispatch(
911 &[ArgumentHandle::new(&pos, &ctx)],
912 &ctx.function_context(None)
913 )
914 .unwrap()
915 .into_literal(),
916 LiteralValue::Number(1.0)
917 );
918 }
919 #[test]
920 fn sign_error_passthrough() {
921 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
922 let ctx = interp(&wb);
923 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
924 "#DIV/0!",
925 )));
926 let f = ctx.context.get_function("", "SIGN").unwrap();
927 match f
928 .dispatch(
929 &[ArgumentHandle::new(&e, &ctx)],
930 &ctx.function_context(None),
931 )
932 .unwrap()
933 .into_literal()
934 {
935 LiteralValue::Error(er) => assert_eq!(er, "#DIV/0!"),
936 _ => panic!(),
937 }
938 }
939
940 #[test]
942 fn int_floor_negative() {
943 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
944 let ctx = interp(&wb);
945 let f = ctx.context.get_function("", "INT").unwrap();
946 let n = lit(LiteralValue::Number(-3.2));
947 assert_eq!(
948 f.dispatch(
949 &[ArgumentHandle::new(&n, &ctx)],
950 &ctx.function_context(None)
951 )
952 .unwrap()
953 .into_literal(),
954 LiteralValue::Number(-4.0)
955 );
956 }
957 #[test]
958 fn int_floor_positive() {
959 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
960 let ctx = interp(&wb);
961 let f = ctx.context.get_function("", "INT").unwrap();
962 let n = lit(LiteralValue::Number(3.7));
963 assert_eq!(
964 f.dispatch(
965 &[ArgumentHandle::new(&n, &ctx)],
966 &ctx.function_context(None)
967 )
968 .unwrap()
969 .into_literal(),
970 LiteralValue::Number(3.0)
971 );
972 }
973
974 #[test]
976 fn trunc_digits_positive_and_negative() {
977 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
978 let ctx = interp(&wb);
979 let f = ctx.context.get_function("", "TRUNC").unwrap();
980 let n = lit(LiteralValue::Number(12.3456));
981 let d2 = lit(LiteralValue::Int(2));
982 let dneg1 = lit(LiteralValue::Int(-1));
983 assert_eq!(
984 f.dispatch(
985 &[
986 ArgumentHandle::new(&n, &ctx),
987 ArgumentHandle::new(&d2, &ctx)
988 ],
989 &ctx.function_context(None)
990 )
991 .unwrap()
992 .into_literal(),
993 LiteralValue::Number(12.34)
994 );
995 assert_eq!(
996 f.dispatch(
997 &[
998 ArgumentHandle::new(&n, &ctx),
999 ArgumentHandle::new(&dneg1, &ctx)
1000 ],
1001 &ctx.function_context(None)
1002 )
1003 .unwrap()
1004 .into_literal(),
1005 LiteralValue::Number(10.0)
1006 );
1007 }
1008 #[test]
1009 fn trunc_default_zero_digits() {
1010 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
1011 let ctx = interp(&wb);
1012 let f = ctx.context.get_function("", "TRUNC").unwrap();
1013 let n = lit(LiteralValue::Number(-12.999));
1014 assert_eq!(
1015 f.dispatch(
1016 &[ArgumentHandle::new(&n, &ctx)],
1017 &ctx.function_context(None)
1018 )
1019 .unwrap()
1020 .into_literal(),
1021 LiteralValue::Number(-12.0)
1022 );
1023 }
1024
1025 #[test]
1027 fn round_half_away_positive_and_negative() {
1028 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
1029 let ctx = interp(&wb);
1030 let f = ctx.context.get_function("", "ROUND").unwrap();
1031 let p = lit(LiteralValue::Number(2.5));
1032 let n = lit(LiteralValue::Number(-2.5));
1033 let d0 = lit(LiteralValue::Int(0));
1034 assert_eq!(
1035 f.dispatch(
1036 &[
1037 ArgumentHandle::new(&p, &ctx),
1038 ArgumentHandle::new(&d0, &ctx)
1039 ],
1040 &ctx.function_context(None)
1041 )
1042 .unwrap()
1043 .into_literal(),
1044 LiteralValue::Number(3.0)
1045 );
1046 assert_eq!(
1047 f.dispatch(
1048 &[
1049 ArgumentHandle::new(&n, &ctx),
1050 ArgumentHandle::new(&d0, &ctx)
1051 ],
1052 &ctx.function_context(None)
1053 )
1054 .unwrap()
1055 .into_literal(),
1056 LiteralValue::Number(-3.0)
1057 );
1058 }
1059 #[test]
1060 fn round_digits_positive() {
1061 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
1062 let ctx = interp(&wb);
1063 let f = ctx.context.get_function("", "ROUND").unwrap();
1064 let n = lit(LiteralValue::Number(1.2345));
1065 let d = lit(LiteralValue::Int(3));
1066 assert_eq!(
1067 f.dispatch(
1068 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1069 &ctx.function_context(None)
1070 )
1071 .unwrap()
1072 .into_literal(),
1073 LiteralValue::Number(1.235)
1074 );
1075 }
1076
1077 #[test]
1079 fn rounddown_truncates() {
1080 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
1081 let ctx = interp(&wb);
1082 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
1083 let n = lit(LiteralValue::Number(1.299));
1084 let d = lit(LiteralValue::Int(2));
1085 assert_eq!(
1086 f.dispatch(
1087 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1088 &ctx.function_context(None)
1089 )
1090 .unwrap()
1091 .into_literal(),
1092 LiteralValue::Number(1.29)
1093 );
1094 }
1095 #[test]
1096 fn rounddown_negative_number() {
1097 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
1098 let ctx = interp(&wb);
1099 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
1100 let n = lit(LiteralValue::Number(-1.299));
1101 let d = lit(LiteralValue::Int(2));
1102 assert_eq!(
1103 f.dispatch(
1104 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1105 &ctx.function_context(None)
1106 )
1107 .unwrap()
1108 .into_literal(),
1109 LiteralValue::Number(-1.29)
1110 );
1111 }
1112
1113 #[test]
1115 fn roundup_away_from_zero() {
1116 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
1117 let ctx = interp(&wb);
1118 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
1119 let n = lit(LiteralValue::Number(1.001));
1120 let d = lit(LiteralValue::Int(2));
1121 assert_eq!(
1122 f.dispatch(
1123 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1124 &ctx.function_context(None)
1125 )
1126 .unwrap()
1127 .into_literal(),
1128 LiteralValue::Number(1.01)
1129 );
1130 }
1131 #[test]
1132 fn roundup_negative() {
1133 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
1134 let ctx = interp(&wb);
1135 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
1136 let n = lit(LiteralValue::Number(-1.001));
1137 let d = lit(LiteralValue::Int(2));
1138 assert_eq!(
1139 f.dispatch(
1140 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
1141 &ctx.function_context(None)
1142 )
1143 .unwrap()
1144 .into_literal(),
1145 LiteralValue::Number(-1.01)
1146 );
1147 }
1148
1149 #[test]
1151 fn mod_positive_negative_cases() {
1152 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
1153 let ctx = interp(&wb);
1154 let f = ctx.context.get_function("", "MOD").unwrap();
1155 let a = lit(LiteralValue::Int(-3));
1156 let b = lit(LiteralValue::Int(2));
1157 let out = f
1158 .dispatch(
1159 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
1160 &ctx.function_context(None),
1161 )
1162 .unwrap();
1163 assert_eq!(out, LiteralValue::Number(1.0));
1164 let a2 = lit(LiteralValue::Int(3));
1165 let b2 = lit(LiteralValue::Int(-2));
1166 let out2 = f
1167 .dispatch(
1168 &[
1169 ArgumentHandle::new(&a2, &ctx),
1170 ArgumentHandle::new(&b2, &ctx),
1171 ],
1172 &ctx.function_context(None),
1173 )
1174 .unwrap();
1175 assert_eq!(out2, LiteralValue::Number(-1.0));
1176 }
1177 #[test]
1178 fn mod_div_by_zero_error() {
1179 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
1180 let ctx = interp(&wb);
1181 let f = ctx.context.get_function("", "MOD").unwrap();
1182 let a = lit(LiteralValue::Int(5));
1183 let zero = lit(LiteralValue::Int(0));
1184 match f
1185 .dispatch(
1186 &[
1187 ArgumentHandle::new(&a, &ctx),
1188 ArgumentHandle::new(&zero, &ctx),
1189 ],
1190 &ctx.function_context(None),
1191 )
1192 .unwrap()
1193 .into_literal()
1194 {
1195 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
1196 _ => panic!(),
1197 }
1198 }
1199
1200 #[test]
1202 fn sqrt_basic_and_domain() {
1203 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SqrtFn));
1204 let ctx = interp(&wb);
1205 let f = ctx.context.get_function("", "SQRT").unwrap();
1206 let n = lit(LiteralValue::Number(9.0));
1207 let out = f
1208 .dispatch(
1209 &[ArgumentHandle::new(&n, &ctx)],
1210 &ctx.function_context(None),
1211 )
1212 .unwrap();
1213 assert_eq!(out, LiteralValue::Number(3.0));
1214 let neg = lit(LiteralValue::Number(-1.0));
1215 let out2 = f
1216 .dispatch(
1217 &[ArgumentHandle::new(&neg, &ctx)],
1218 &ctx.function_context(None),
1219 )
1220 .unwrap();
1221 assert!(matches!(out2.into_literal(), LiteralValue::Error(_)));
1222 }
1223
1224 #[test]
1225 fn power_fractional_negative_domain() {
1226 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PowerFn));
1227 let ctx = interp(&wb);
1228 let f = ctx.context.get_function("", "POWER").unwrap();
1229 let a = lit(LiteralValue::Number(-4.0));
1230 let half = lit(LiteralValue::Number(0.5));
1231 let out = f
1232 .dispatch(
1233 &[
1234 ArgumentHandle::new(&a, &ctx),
1235 ArgumentHandle::new(&half, &ctx),
1236 ],
1237 &ctx.function_context(None),
1238 )
1239 .unwrap();
1240 assert!(matches!(out.into_literal(), LiteralValue::Error(_))); }
1242
1243 #[test]
1244 fn log_variants() {
1245 let wb = TestWorkbook::new()
1246 .with_function(std::sync::Arc::new(LogFn))
1247 .with_function(std::sync::Arc::new(Log10Fn))
1248 .with_function(std::sync::Arc::new(LnFn));
1249 let ctx = interp(&wb);
1250 let logf = ctx.context.get_function("", "LOG").unwrap();
1251 let log10f = ctx.context.get_function("", "LOG10").unwrap();
1252 let lnf = ctx.context.get_function("", "LN").unwrap();
1253 let n = lit(LiteralValue::Number(100.0));
1254 let base = lit(LiteralValue::Number(10.0));
1255 assert_eq!(
1256 logf.dispatch(
1257 &[
1258 ArgumentHandle::new(&n, &ctx),
1259 ArgumentHandle::new(&base, &ctx)
1260 ],
1261 &ctx.function_context(None)
1262 )
1263 .unwrap()
1264 .into_literal(),
1265 LiteralValue::Number(2.0)
1266 );
1267 assert_eq!(
1268 log10f
1269 .dispatch(
1270 &[ArgumentHandle::new(&n, &ctx)],
1271 &ctx.function_context(None)
1272 )
1273 .unwrap()
1274 .into_literal(),
1275 LiteralValue::Number(2.0)
1276 );
1277 assert_eq!(
1278 lnf.dispatch(
1279 &[ArgumentHandle::new(&n, &ctx)],
1280 &ctx.function_context(None)
1281 )
1282 .unwrap()
1283 .into_literal(),
1284 LiteralValue::Number(100.0f64.ln())
1285 );
1286 }
1287 #[test]
1288 fn ceiling_floor_basic() {
1289 let wb = TestWorkbook::new()
1290 .with_function(std::sync::Arc::new(CeilingFn))
1291 .with_function(std::sync::Arc::new(FloorFn))
1292 .with_function(std::sync::Arc::new(CeilingMathFn))
1293 .with_function(std::sync::Arc::new(FloorMathFn));
1294 let ctx = interp(&wb);
1295 let c = ctx.context.get_function("", "CEILING").unwrap();
1296 let f = ctx.context.get_function("", "FLOOR").unwrap();
1297 let n = lit(LiteralValue::Number(5.1));
1298 let sig = lit(LiteralValue::Number(2.0));
1299 assert_eq!(
1300 c.dispatch(
1301 &[
1302 ArgumentHandle::new(&n, &ctx),
1303 ArgumentHandle::new(&sig, &ctx)
1304 ],
1305 &ctx.function_context(None)
1306 )
1307 .unwrap()
1308 .into_literal(),
1309 LiteralValue::Number(6.0)
1310 );
1311 assert_eq!(
1312 f.dispatch(
1313 &[
1314 ArgumentHandle::new(&n, &ctx),
1315 ArgumentHandle::new(&sig, &ctx)
1316 ],
1317 &ctx.function_context(None)
1318 )
1319 .unwrap()
1320 .into_literal(),
1321 LiteralValue::Number(4.0)
1322 );
1323 }
1324}