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