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::function_contract::FunctionDependencyContract;
7use crate::traits::{ArgumentHandle, FunctionContext};
8use formualizer_common::{ExcelError, LiteralValue};
9use formualizer_macros::func_caps;
10
11#[derive(Debug)]
12pub struct AbsFn;
13impl Function for AbsFn {
55 func_caps!(PURE);
56 fn name(&self) -> &'static str {
57 "ABS"
58 }
59 fn min_args(&self) -> usize {
60 1
61 }
62 fn dependency_contract(&self, arity: usize) -> Option<FunctionDependencyContract> {
63 FunctionDependencyContract::static_scalar_all_args(arity)
64 }
65 fn arg_schema(&self) -> &'static [ArgSchema] {
66 &ARG_NUM_LENIENT_ONE[..]
67 }
68 fn eval<'a, 'b, 'c>(
69 &self,
70 args: &'c [ArgumentHandle<'a, 'b>],
71 _: &dyn FunctionContext<'b>,
72 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
73 let v = args[0].value()?.into_literal();
74 match v {
75 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
76 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
77 coerce_num(&other)?.abs(),
78 ))),
79 }
80 }
81}
82
83#[derive(Debug)]
84pub struct SignFn;
85impl Function for SignFn {
125 func_caps!(PURE);
126 fn name(&self) -> &'static str {
127 "SIGN"
128 }
129 fn min_args(&self) -> usize {
130 1
131 }
132 fn arg_schema(&self) -> &'static [ArgSchema] {
133 &ARG_NUM_LENIENT_ONE[..]
134 }
135 fn eval<'a, 'b, 'c>(
136 &self,
137 args: &'c [ArgumentHandle<'a, 'b>],
138 _: &dyn FunctionContext<'b>,
139 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
140 let v = args[0].value()?.into_literal();
141 match v {
142 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
143 other => {
144 let n = coerce_num(&other)?;
145 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
146 if n > 0.0 {
147 1.0
148 } else if n < 0.0 {
149 -1.0
150 } else {
151 0.0
152 },
153 )))
154 }
155 }
156 }
157}
158
159#[derive(Debug)]
160pub struct IntFn; impl Function for IntFn {
203 func_caps!(PURE);
204 fn name(&self) -> &'static str {
205 "INT"
206 }
207 fn min_args(&self) -> usize {
208 1
209 }
210 fn arg_schema(&self) -> &'static [ArgSchema] {
211 &ARG_NUM_LENIENT_ONE[..]
212 }
213 fn eval<'a, 'b, 'c>(
214 &self,
215 args: &'c [ArgumentHandle<'a, 'b>],
216 _: &dyn FunctionContext<'b>,
217 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
218 let v = args[0].value()?.into_literal();
219 match v {
220 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
221 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
222 coerce_num(&other)?.floor(),
223 ))),
224 }
225 }
226}
227
228#[derive(Debug)]
229pub struct TruncFn; impl Function for TruncFn {
270 func_caps!(PURE);
271 fn name(&self) -> &'static str {
272 "TRUNC"
273 }
274 fn min_args(&self) -> usize {
275 1
276 }
277 fn variadic(&self) -> bool {
278 true
279 }
280 fn arg_schema(&self) -> &'static [ArgSchema] {
281 &ARG_NUM_LENIENT_TWO[..]
282 }
283 fn eval<'a, 'b, 'c>(
284 &self,
285 args: &'c [ArgumentHandle<'a, 'b>],
286 _: &dyn FunctionContext<'b>,
287 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
288 if args.is_empty() || args.len() > 2 {
289 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
290 ExcelError::new_value(),
291 )));
292 }
293 let mut n = match args[0].value()?.into_literal() {
294 LiteralValue::Error(e) => {
295 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
296 }
297 other => coerce_num(&other)?,
298 };
299 let digits: i32 = if args.len() == 2 {
300 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)? as i32,
305 }
306 } else {
307 0
308 };
309 if digits >= 0 {
310 let f = 10f64.powi(digits);
311 n = (n * f).trunc() / f;
312 } else {
313 let f = 10f64.powi(-digits);
314 n = (n / f).trunc() * f;
315 }
316 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(n)))
317 }
318}
319
320#[derive(Debug)]
321pub struct RoundFn; impl Function for RoundFn {
362 func_caps!(PURE);
363 fn name(&self) -> &'static str {
364 "ROUND"
365 }
366 fn min_args(&self) -> usize {
367 2
368 }
369 fn arg_schema(&self) -> &'static [ArgSchema] {
370 &ARG_NUM_LENIENT_TWO[..]
371 }
372 fn eval<'a, 'b, 'c>(
373 &self,
374 args: &'c [ArgumentHandle<'a, 'b>],
375 _: &dyn FunctionContext<'b>,
376 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
377 let n = match args[0].value()?.into_literal() {
378 LiteralValue::Error(e) => {
379 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
380 }
381 other => coerce_num(&other)?,
382 };
383 let digits = match args[1].value()?.into_literal() {
384 LiteralValue::Error(e) => {
385 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
386 }
387 other => coerce_num(&other)? as i32,
388 };
389 let f = 10f64.powi(digits.abs());
390 let out = if digits >= 0 {
391 (n * f).round() / f
392 } else {
393 (n / f).round() * f
394 };
395 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
396 }
397}
398
399#[derive(Debug)]
400pub struct RoundDownFn; impl Function for RoundDownFn {
441 func_caps!(PURE);
442 fn name(&self) -> &'static str {
443 "ROUNDDOWN"
444 }
445 fn min_args(&self) -> usize {
446 2
447 }
448 fn arg_schema(&self) -> &'static [ArgSchema] {
449 &ARG_NUM_LENIENT_TWO[..]
450 }
451 fn eval<'a, 'b, 'c>(
452 &self,
453 args: &'c [ArgumentHandle<'a, 'b>],
454 _: &dyn FunctionContext<'b>,
455 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
456 let n = match args[0].value()?.into_literal() {
457 LiteralValue::Error(e) => {
458 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
459 }
460 other => coerce_num(&other)?,
461 };
462 let digits = match args[1].value()?.into_literal() {
463 LiteralValue::Error(e) => {
464 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
465 }
466 other => coerce_num(&other)? as i32,
467 };
468 let f = 10f64.powi(digits.abs());
469 let out = if digits >= 0 {
470 (n * f).trunc() / f
471 } else {
472 (n / f).trunc() * f
473 };
474 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
475 }
476}
477
478#[derive(Debug)]
479pub struct RoundUpFn; impl Function for RoundUpFn {
520 func_caps!(PURE);
521 fn name(&self) -> &'static str {
522 "ROUNDUP"
523 }
524 fn min_args(&self) -> usize {
525 2
526 }
527 fn arg_schema(&self) -> &'static [ArgSchema] {
528 &ARG_NUM_LENIENT_TWO[..]
529 }
530 fn eval<'a, 'b, 'c>(
531 &self,
532 args: &'c [ArgumentHandle<'a, 'b>],
533 _: &dyn FunctionContext<'b>,
534 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
535 let n = match args[0].value()?.into_literal() {
536 LiteralValue::Error(e) => {
537 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
538 }
539 other => coerce_num(&other)?,
540 };
541 let digits = match args[1].value()?.into_literal() {
542 LiteralValue::Error(e) => {
543 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
544 }
545 other => coerce_num(&other)? as i32,
546 };
547 let f = 10f64.powi(digits.abs());
548 let mut scaled = if digits >= 0 { n * f } else { n / f };
549 if scaled > 0.0 {
550 scaled = scaled.ceil();
551 } else {
552 scaled = scaled.floor();
553 }
554 let out = if digits >= 0 { scaled / f } else { scaled * f };
555 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
556 }
557}
558
559#[derive(Debug)]
560pub struct ModFn; impl Function for ModFn {
601 func_caps!(PURE);
602 fn name(&self) -> &'static str {
603 "MOD"
604 }
605 fn min_args(&self) -> usize {
606 2
607 }
608 fn arg_schema(&self) -> &'static [ArgSchema] {
609 &ARG_NUM_LENIENT_TWO[..]
610 }
611 fn eval<'a, 'b, 'c>(
612 &self,
613 args: &'c [ArgumentHandle<'a, 'b>],
614 _: &dyn FunctionContext<'b>,
615 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
616 let x = match args[0].value()?.into_literal() {
617 LiteralValue::Error(e) => {
618 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
619 }
620 other => coerce_num(&other)?,
621 };
622 let y = match args[1].value()?.into_literal() {
623 LiteralValue::Error(e) => {
624 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
625 }
626 other => coerce_num(&other)?,
627 };
628 if y == 0.0 {
629 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
630 ExcelError::from_error_string("#DIV/0!"),
631 )));
632 }
633 let m = x % y;
634 let mut r = if m == 0.0 {
635 0.0
636 } else if (y > 0.0 && m < 0.0) || (y < 0.0 && m > 0.0) {
637 m + y
638 } else {
639 m
640 };
641 if r == -0.0 {
642 r = 0.0;
643 }
644 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(r)))
645 }
646}
647
648#[derive(Debug)]
651pub struct CeilingFn; impl Function for CeilingFn {
694 func_caps!(PURE);
695 fn name(&self) -> &'static str {
696 "CEILING"
697 }
698 fn min_args(&self) -> usize {
699 1
700 }
701 fn variadic(&self) -> bool {
702 true
703 }
704 fn arg_schema(&self) -> &'static [ArgSchema] {
705 &ARG_NUM_LENIENT_TWO[..]
706 }
707 fn eval<'a, 'b, 'c>(
708 &self,
709 args: &'c [ArgumentHandle<'a, 'b>],
710 _: &dyn FunctionContext<'b>,
711 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
712 if args.is_empty() || args.len() > 2 {
713 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
714 ExcelError::new_value(),
715 )));
716 }
717 let n = match args[0].value()?.into_literal() {
718 LiteralValue::Error(e) => {
719 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
720 }
721 other => coerce_num(&other)?,
722 };
723 let mut sig = if args.len() == 2 {
724 match args[1].value()?.into_literal() {
725 LiteralValue::Error(e) => {
726 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
727 }
728 other => coerce_num(&other)?,
729 }
730 } else {
731 1.0
732 };
733 if sig == 0.0 {
734 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
735 ExcelError::from_error_string("#DIV/0!"),
736 )));
737 }
738 if sig < 0.0 {
739 sig = sig.abs(); }
741 let k = (n / sig).ceil();
742 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
743 k * sig,
744 )))
745 }
746}
747
748#[derive(Debug)]
749pub struct CeilingMathFn; impl Function for CeilingMathFn {
790 func_caps!(PURE);
791 fn name(&self) -> &'static str {
792 "CEILING.MATH"
793 }
794 fn min_args(&self) -> usize {
795 1
796 }
797 fn variadic(&self) -> bool {
798 true
799 }
800 fn arg_schema(&self) -> &'static [ArgSchema] {
801 &ARG_NUM_LENIENT_TWO[..]
802 } fn eval<'a, 'b, 'c>(
804 &self,
805 args: &'c [ArgumentHandle<'a, 'b>],
806 _: &dyn FunctionContext<'b>,
807 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
808 if args.is_empty() || args.len() > 3 {
809 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
810 ExcelError::new_value(),
811 )));
812 }
813 let n = match args[0].value()?.into_literal() {
814 LiteralValue::Error(e) => {
815 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
816 }
817 other => coerce_num(&other)?,
818 };
819 let sig = if args.len() >= 2 {
820 match args[1].value()?.into_literal() {
821 LiteralValue::Error(e) => {
822 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
823 }
824 other => {
825 let v = coerce_num(&other)?;
826 if v == 0.0 { 1.0 } else { v.abs() }
827 }
828 }
829 } else {
830 1.0
831 };
832 let mode_nonzero = if args.len() == 3 {
833 match args[2].value()?.into_literal() {
834 LiteralValue::Error(e) => {
835 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
836 }
837 other => coerce_num(&other)? != 0.0,
838 }
839 } else {
840 false
841 };
842 let result = if n >= 0.0 {
843 (n / sig).ceil() * sig
844 } else if mode_nonzero {
845 (n / sig).floor() * sig } else {
847 (n / sig).ceil() * sig };
849 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
850 result,
851 )))
852 }
853}
854
855#[derive(Debug)]
856pub struct FloorFn; impl Function for FloorFn {
899 func_caps!(PURE);
900 fn name(&self) -> &'static str {
901 "FLOOR"
902 }
903 fn min_args(&self) -> usize {
904 1
905 }
906 fn variadic(&self) -> bool {
907 true
908 }
909 fn arg_schema(&self) -> &'static [ArgSchema] {
910 &ARG_NUM_LENIENT_TWO[..]
911 }
912 fn eval<'a, 'b, 'c>(
913 &self,
914 args: &'c [ArgumentHandle<'a, 'b>],
915 _: &dyn FunctionContext<'b>,
916 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
917 if args.is_empty() || args.len() > 2 {
918 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
919 ExcelError::new_value(),
920 )));
921 }
922 let n = match args[0].value()?.into_literal() {
923 LiteralValue::Error(e) => {
924 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
925 }
926 other => coerce_num(&other)?,
927 };
928 let mut sig = if args.len() == 2 {
929 match args[1].value()?.into_literal() {
930 LiteralValue::Error(e) => {
931 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
932 }
933 other => coerce_num(&other)?,
934 }
935 } else {
936 1.0
937 };
938 if sig == 0.0 {
939 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
940 ExcelError::from_error_string("#DIV/0!"),
941 )));
942 }
943 if sig < 0.0 {
944 sig = sig.abs();
945 }
946 let k = (n / sig).floor();
947 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
948 k * sig,
949 )))
950 }
951}
952
953#[derive(Debug)]
954pub struct FloorMathFn; impl Function for FloorMathFn {
995 func_caps!(PURE);
996 fn name(&self) -> &'static str {
997 "FLOOR.MATH"
998 }
999 fn min_args(&self) -> usize {
1000 1
1001 }
1002 fn variadic(&self) -> bool {
1003 true
1004 }
1005 fn arg_schema(&self) -> &'static [ArgSchema] {
1006 &ARG_NUM_LENIENT_TWO[..]
1007 }
1008 fn eval<'a, 'b, 'c>(
1009 &self,
1010 args: &'c [ArgumentHandle<'a, 'b>],
1011 _: &dyn FunctionContext<'b>,
1012 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1013 if args.is_empty() || args.len() > 3 {
1014 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1015 ExcelError::new_value(),
1016 )));
1017 }
1018 let n = match args[0].value()?.into_literal() {
1019 LiteralValue::Error(e) => {
1020 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1021 }
1022 other => coerce_num(&other)?,
1023 };
1024 let sig = if args.len() >= 2 {
1025 match args[1].value()?.into_literal() {
1026 LiteralValue::Error(e) => {
1027 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1028 }
1029 other => {
1030 let v = coerce_num(&other)?;
1031 if v == 0.0 { 1.0 } else { v.abs() }
1032 }
1033 }
1034 } else {
1035 1.0
1036 };
1037 let mode_nonzero = if args.len() == 3 {
1038 match args[2].value()?.into_literal() {
1039 LiteralValue::Error(e) => {
1040 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1041 }
1042 other => coerce_num(&other)? != 0.0,
1043 }
1044 } else {
1045 false
1046 };
1047 let result = if n >= 0.0 {
1048 (n / sig).floor() * sig
1049 } else if mode_nonzero {
1050 (n / sig).ceil() * sig
1051 } else {
1052 (n / sig).floor() * sig
1053 };
1054 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1055 result,
1056 )))
1057 }
1058}
1059
1060#[derive(Debug)]
1061pub struct SqrtFn; impl Function for SqrtFn {
1103 func_caps!(PURE);
1104 fn name(&self) -> &'static str {
1105 "SQRT"
1106 }
1107 fn min_args(&self) -> usize {
1108 1
1109 }
1110 fn arg_schema(&self) -> &'static [ArgSchema] {
1111 &ARG_NUM_LENIENT_ONE[..]
1112 }
1113 fn eval<'a, 'b, 'c>(
1114 &self,
1115 args: &'c [ArgumentHandle<'a, 'b>],
1116 _: &dyn FunctionContext<'b>,
1117 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1118 let n = match args[0].value()?.into_literal() {
1119 LiteralValue::Error(e) => {
1120 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1121 }
1122 other => coerce_num(&other)?,
1123 };
1124 if n < 0.0 {
1125 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1126 ExcelError::new_num(),
1127 )));
1128 }
1129 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1130 n.sqrt(),
1131 )))
1132 }
1133}
1134
1135#[derive(Debug)]
1136pub struct PowerFn; impl Function for PowerFn {
1177 func_caps!(PURE);
1178 fn name(&self) -> &'static str {
1179 "POWER"
1180 }
1181 fn min_args(&self) -> usize {
1182 2
1183 }
1184 fn arg_schema(&self) -> &'static [ArgSchema] {
1185 &ARG_NUM_LENIENT_TWO[..]
1186 }
1187 fn eval<'a, 'b, 'c>(
1188 &self,
1189 args: &'c [ArgumentHandle<'a, 'b>],
1190 _: &dyn FunctionContext<'b>,
1191 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1192 let base = match args[0].value()?.into_literal() {
1193 LiteralValue::Error(e) => {
1194 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1195 }
1196 other => coerce_num(&other)?,
1197 };
1198 let expv = match args[1].value()?.into_literal() {
1199 LiteralValue::Error(e) => {
1200 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1201 }
1202 other => coerce_num(&other)?,
1203 };
1204 if base < 0.0 && (expv.fract().abs() > 1e-12) {
1205 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1206 ExcelError::new_num(),
1207 )));
1208 }
1209 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1210 base.powf(expv),
1211 )))
1212 }
1213}
1214
1215#[derive(Debug)]
1216pub struct ExpFn; impl Function for ExpFn {
1259 func_caps!(PURE);
1260 fn name(&self) -> &'static str {
1261 "EXP"
1262 }
1263 fn min_args(&self) -> usize {
1264 1
1265 }
1266 fn arg_schema(&self) -> &'static [ArgSchema] {
1267 &ARG_NUM_LENIENT_ONE[..]
1268 }
1269 fn eval<'a, 'b, 'c>(
1270 &self,
1271 args: &'c [ArgumentHandle<'a, 'b>],
1272 _: &dyn FunctionContext<'b>,
1273 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1274 let n = match args[0].value()?.into_literal() {
1275 LiteralValue::Error(e) => {
1276 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1277 }
1278 other => coerce_num(&other)?,
1279 };
1280 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1281 n.exp(),
1282 )))
1283 }
1284}
1285
1286#[derive(Debug)]
1287pub struct LnFn; impl Function for LnFn {
1328 func_caps!(PURE);
1329 fn name(&self) -> &'static str {
1330 "LN"
1331 }
1332 fn min_args(&self) -> usize {
1333 1
1334 }
1335 fn arg_schema(&self) -> &'static [ArgSchema] {
1336 &ARG_NUM_LENIENT_ONE[..]
1337 }
1338 fn eval<'a, 'b, 'c>(
1339 &self,
1340 args: &'c [ArgumentHandle<'a, 'b>],
1341 _: &dyn FunctionContext<'b>,
1342 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1343 let n = match args[0].value()?.into_literal() {
1344 LiteralValue::Error(e) => {
1345 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1346 }
1347 other => coerce_num(&other)?,
1348 };
1349 if n <= 0.0 {
1350 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1351 ExcelError::new_num(),
1352 )));
1353 }
1354 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1355 n.ln(),
1356 )))
1357 }
1358}
1359
1360#[derive(Debug)]
1361pub struct LogFn; impl Function for LogFn {
1403 func_caps!(PURE);
1404 fn name(&self) -> &'static str {
1405 "LOG"
1406 }
1407 fn min_args(&self) -> usize {
1408 1
1409 }
1410 fn variadic(&self) -> bool {
1411 true
1412 }
1413 fn arg_schema(&self) -> &'static [ArgSchema] {
1414 &ARG_NUM_LENIENT_TWO[..]
1415 }
1416 fn eval<'a, 'b, 'c>(
1417 &self,
1418 args: &'c [ArgumentHandle<'a, 'b>],
1419 _: &dyn FunctionContext<'b>,
1420 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1421 if args.is_empty() || args.len() > 2 {
1422 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1423 ExcelError::new_value(),
1424 )));
1425 }
1426 let n = match args[0].value()?.into_literal() {
1427 LiteralValue::Error(e) => {
1428 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1429 }
1430 other => coerce_num(&other)?,
1431 };
1432 let base = if args.len() == 2 {
1433 match args[1].value()?.into_literal() {
1434 LiteralValue::Error(e) => {
1435 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1436 }
1437 other => coerce_num(&other)?,
1438 }
1439 } else {
1440 10.0
1441 };
1442 if n <= 0.0 || base <= 0.0 || (base - 1.0).abs() < 1e-12 {
1443 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1444 ExcelError::new_num(),
1445 )));
1446 }
1447 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1448 n.log(base),
1449 )))
1450 }
1451}
1452
1453#[derive(Debug)]
1454pub struct Log10Fn; impl Function for Log10Fn {
1495 func_caps!(PURE);
1496 fn name(&self) -> &'static str {
1497 "LOG10"
1498 }
1499 fn min_args(&self) -> usize {
1500 1
1501 }
1502 fn arg_schema(&self) -> &'static [ArgSchema] {
1503 &ARG_NUM_LENIENT_ONE[..]
1504 }
1505 fn eval<'a, 'b, 'c>(
1506 &self,
1507 args: &'c [ArgumentHandle<'a, 'b>],
1508 _: &dyn FunctionContext<'b>,
1509 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1510 let n = match args[0].value()?.into_literal() {
1511 LiteralValue::Error(e) => {
1512 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1513 }
1514 other => coerce_num(&other)?,
1515 };
1516 if n <= 0.0 {
1517 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1518 ExcelError::new_num(),
1519 )));
1520 }
1521 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1522 n.log10(),
1523 )))
1524 }
1525}
1526
1527fn factorial_checked(n: i64) -> Option<f64> {
1528 if !(0..=170).contains(&n) {
1529 return None;
1530 }
1531 let mut out = 1.0;
1532 for i in 2..=n {
1533 out *= i as f64;
1534 }
1535 Some(out)
1536}
1537
1538#[derive(Debug)]
1539pub struct QuotientFn;
1540impl Function for QuotientFn {
1580 func_caps!(PURE);
1581 fn name(&self) -> &'static str {
1582 "QUOTIENT"
1583 }
1584 fn min_args(&self) -> usize {
1585 2
1586 }
1587 fn arg_schema(&self) -> &'static [ArgSchema] {
1588 &ARG_NUM_LENIENT_TWO[..]
1589 }
1590 fn eval<'a, 'b, 'c>(
1591 &self,
1592 args: &'c [ArgumentHandle<'a, 'b>],
1593 _: &dyn FunctionContext<'b>,
1594 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1595 let n = match args[0].value()?.into_literal() {
1596 LiteralValue::Error(e) => {
1597 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1598 }
1599 other => coerce_num(&other)?,
1600 };
1601 let d = match args[1].value()?.into_literal() {
1602 LiteralValue::Error(e) => {
1603 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1604 }
1605 other => coerce_num(&other)?,
1606 };
1607 if d == 0.0 {
1608 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1609 ExcelError::new_div(),
1610 )));
1611 }
1612 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1613 (n / d).trunc(),
1614 )))
1615 }
1616}
1617
1618#[derive(Debug)]
1619pub struct EvenFn;
1620impl Function for EvenFn {
1660 func_caps!(PURE);
1661 fn name(&self) -> &'static str {
1662 "EVEN"
1663 }
1664 fn min_args(&self) -> usize {
1665 1
1666 }
1667 fn arg_schema(&self) -> &'static [ArgSchema] {
1668 &ARG_NUM_LENIENT_ONE[..]
1669 }
1670 fn eval<'a, 'b, 'c>(
1671 &self,
1672 args: &'c [ArgumentHandle<'a, 'b>],
1673 _: &dyn FunctionContext<'b>,
1674 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1675 let number = match args[0].value()?.into_literal() {
1676 LiteralValue::Error(e) => {
1677 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1678 }
1679 other => coerce_num(&other)?,
1680 };
1681 if number == 0.0 {
1682 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
1683 }
1684
1685 let sign = number.signum();
1686 let mut v = number.abs().ceil() as i64;
1687 if v % 2 != 0 {
1688 v += 1;
1689 }
1690 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1691 sign * v as f64,
1692 )))
1693 }
1694}
1695
1696#[derive(Debug)]
1697pub struct OddFn;
1698impl Function for OddFn {
1738 func_caps!(PURE);
1739 fn name(&self) -> &'static str {
1740 "ODD"
1741 }
1742 fn min_args(&self) -> usize {
1743 1
1744 }
1745 fn arg_schema(&self) -> &'static [ArgSchema] {
1746 &ARG_NUM_LENIENT_ONE[..]
1747 }
1748 fn eval<'a, 'b, 'c>(
1749 &self,
1750 args: &'c [ArgumentHandle<'a, 'b>],
1751 _: &dyn FunctionContext<'b>,
1752 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1753 let number = match args[0].value()?.into_literal() {
1754 LiteralValue::Error(e) => {
1755 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1756 }
1757 other => coerce_num(&other)?,
1758 };
1759
1760 let sign = if number < 0.0 { -1.0 } else { 1.0 };
1761 let mut v = number.abs().ceil() as i64;
1762 if v % 2 == 0 {
1763 v += 1;
1764 }
1765 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1766 sign * v as f64,
1767 )))
1768 }
1769}
1770
1771#[derive(Debug)]
1772pub struct SqrtPiFn;
1773impl Function for SqrtPiFn {
1813 func_caps!(PURE);
1814 fn name(&self) -> &'static str {
1815 "SQRTPI"
1816 }
1817 fn min_args(&self) -> usize {
1818 1
1819 }
1820 fn arg_schema(&self) -> &'static [ArgSchema] {
1821 &ARG_NUM_LENIENT_ONE[..]
1822 }
1823 fn eval<'a, 'b, 'c>(
1824 &self,
1825 args: &'c [ArgumentHandle<'a, 'b>],
1826 _: &dyn FunctionContext<'b>,
1827 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1828 let n = match args[0].value()?.into_literal() {
1829 LiteralValue::Error(e) => {
1830 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1831 }
1832 other => coerce_num(&other)?,
1833 };
1834 if n < 0.0 {
1835 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1836 ExcelError::new_num(),
1837 )));
1838 }
1839 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1840 (n * std::f64::consts::PI).sqrt(),
1841 )))
1842 }
1843}
1844
1845#[derive(Debug)]
1846pub struct MultinomialFn;
1847impl Function for MultinomialFn {
1887 func_caps!(PURE);
1888 fn name(&self) -> &'static str {
1889 "MULTINOMIAL"
1890 }
1891 fn min_args(&self) -> usize {
1892 1
1893 }
1894 fn variadic(&self) -> bool {
1895 true
1896 }
1897 fn arg_schema(&self) -> &'static [ArgSchema] {
1898 &ARG_NUM_LENIENT_ONE[..]
1899 }
1900 fn eval<'a, 'b, 'c>(
1901 &self,
1902 args: &'c [ArgumentHandle<'a, 'b>],
1903 _ctx: &dyn FunctionContext<'b>,
1904 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1905 let mut values: Vec<i64> = Vec::new();
1906 for arg in args {
1907 for value in arg.lazy_values_owned()? {
1908 let n = match value {
1909 LiteralValue::Error(e) => {
1910 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1911 }
1912 other => coerce_num(&other)?.trunc() as i64,
1913 };
1914 if n < 0 {
1915 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1916 ExcelError::new_num(),
1917 )));
1918 }
1919 values.push(n);
1920 }
1921 }
1922
1923 let sum: i64 = values.iter().sum();
1924 let num = match factorial_checked(sum) {
1925 Some(v) => v,
1926 None => {
1927 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1928 ExcelError::new_num(),
1929 )));
1930 }
1931 };
1932
1933 let mut den = 1.0;
1934 for n in values {
1935 let fact = match factorial_checked(n) {
1936 Some(v) => v,
1937 None => {
1938 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1939 ExcelError::new_num(),
1940 )));
1941 }
1942 };
1943 den *= fact;
1944 }
1945
1946 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1947 (num / den).round(),
1948 )))
1949 }
1950}
1951
1952#[derive(Debug)]
1953pub struct SeriesSumFn;
1954impl Function for SeriesSumFn {
1998 func_caps!(PURE);
1999 fn name(&self) -> &'static str {
2000 "SERIESSUM"
2001 }
2002 fn min_args(&self) -> usize {
2003 4
2004 }
2005 fn arg_schema(&self) -> &'static [ArgSchema] {
2006 use std::sync::LazyLock;
2007 static SCHEMA: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| {
2008 vec![
2009 ArgSchema::number_lenient_scalar(),
2010 ArgSchema::number_lenient_scalar(),
2011 ArgSchema::number_lenient_scalar(),
2012 ArgSchema::any(),
2013 ]
2014 });
2015 &SCHEMA[..]
2016 }
2017 fn eval<'a, 'b, 'c>(
2018 &self,
2019 args: &'c [ArgumentHandle<'a, 'b>],
2020 _ctx: &dyn FunctionContext<'b>,
2021 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2022 let x = match args[0].value()?.into_literal() {
2023 LiteralValue::Error(e) => {
2024 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2025 }
2026 other => coerce_num(&other)?,
2027 };
2028 let n = match args[1].value()?.into_literal() {
2029 LiteralValue::Error(e) => {
2030 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2031 }
2032 other => coerce_num(&other)?,
2033 };
2034 let m = match args[2].value()?.into_literal() {
2035 LiteralValue::Error(e) => {
2036 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2037 }
2038 other => coerce_num(&other)?,
2039 };
2040
2041 let mut coeffs: Vec<f64> = Vec::new();
2042 if let Ok(view) = args[3].range_view() {
2043 view.for_each_cell(&mut |cell| {
2044 match cell {
2045 LiteralValue::Error(e) => return Err(e.clone()),
2046 other => coeffs.push(coerce_num(other)?),
2047 }
2048 Ok(())
2049 })?;
2050 } else {
2051 match args[3].value()?.into_literal() {
2052 LiteralValue::Array(rows) => {
2053 for row in rows {
2054 for cell in row {
2055 match cell {
2056 LiteralValue::Error(e) => {
2057 return Ok(crate::traits::CalcValue::Scalar(
2058 LiteralValue::Error(e),
2059 ));
2060 }
2061 other => coeffs.push(coerce_num(&other)?),
2062 }
2063 }
2064 }
2065 }
2066 LiteralValue::Error(e) => {
2067 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2068 }
2069 other => coeffs.push(coerce_num(&other)?),
2070 }
2071 }
2072
2073 let mut sum = 0.0;
2074 for (i, c) in coeffs.into_iter().enumerate() {
2075 sum += c * x.powf(n + (i as f64) * m);
2076 }
2077
2078 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(sum)))
2079 }
2080}
2081
2082#[derive(Debug)]
2083pub struct SumsqFn;
2084impl Function for SumsqFn {
2128 func_caps!(PURE, REDUCTION, NUMERIC_ONLY);
2129 fn name(&self) -> &'static str {
2130 "SUMSQ"
2131 }
2132 fn min_args(&self) -> usize {
2133 1
2134 }
2135 fn variadic(&self) -> bool {
2136 true
2137 }
2138 fn arg_schema(&self) -> &'static [ArgSchema] {
2139 &ARG_RANGE_NUM_LENIENT_ONE[..]
2140 }
2141 fn eval<'a, 'b, 'c>(
2142 &self,
2143 args: &'c [ArgumentHandle<'a, 'b>],
2144 _ctx: &dyn FunctionContext<'b>,
2145 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2146 let mut total = 0.0;
2147 for arg in args {
2148 if let Ok(view) = arg.range_view() {
2149 view.for_each_cell(&mut |cell| {
2150 match cell {
2151 LiteralValue::Error(e) => return Err(e.clone()),
2152 LiteralValue::Number(n) => total += n * n,
2153 LiteralValue::Int(i) => {
2154 let n = *i as f64;
2155 total += n * n;
2156 }
2157 LiteralValue::Date(d) => {
2158 let n = crate::builtins::datetime::date_to_serial(d);
2159 total += n * n;
2160 }
2161 LiteralValue::DateTime(dt) => {
2162 let n = crate::builtins::datetime::datetime_to_serial(dt);
2163 total += n * n;
2164 }
2165 LiteralValue::Time(t) => {
2166 let n = crate::builtins::datetime::time_to_fraction(t);
2167 total += n * n;
2168 }
2169 LiteralValue::Duration(d) => {
2170 let n = d.num_seconds() as f64 / 86_400.0;
2171 total += n * n;
2172 }
2173 _ => {}
2174 }
2175 Ok(())
2176 })?;
2177 } else {
2178 let v = arg.value()?.into_literal();
2179 match v {
2180 LiteralValue::Error(e) => {
2181 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2182 }
2183 other => {
2184 let n = coerce_num(&other)?;
2185 total += n * n;
2186 }
2187 }
2188 }
2189 }
2190 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2191 total,
2192 )))
2193 }
2194}
2195
2196#[derive(Debug)]
2197pub struct MroundFn;
2198impl Function for MroundFn {
2238 func_caps!(PURE);
2239 fn name(&self) -> &'static str {
2240 "MROUND"
2241 }
2242 fn min_args(&self) -> usize {
2243 2
2244 }
2245 fn arg_schema(&self) -> &'static [ArgSchema] {
2246 &ARG_NUM_LENIENT_TWO[..]
2247 }
2248 fn eval<'a, 'b, 'c>(
2249 &self,
2250 args: &'c [ArgumentHandle<'a, 'b>],
2251 _ctx: &dyn FunctionContext<'b>,
2252 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2253 let number = match args[0].value()?.into_literal() {
2254 LiteralValue::Error(e) => {
2255 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2256 }
2257 other => coerce_num(&other)?,
2258 };
2259 let multiple = match args[1].value()?.into_literal() {
2260 LiteralValue::Error(e) => {
2261 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2262 }
2263 other => coerce_num(&other)?,
2264 };
2265
2266 if multiple == 0.0 {
2267 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2268 }
2269 if number != 0.0 && number.signum() != multiple.signum() {
2270 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2271 ExcelError::new_num(),
2272 )));
2273 }
2274
2275 let m = multiple.abs();
2276 let scaled = number.abs() / m;
2277 let rounded = (scaled + 0.5 + 1e-12).floor();
2278 let out = rounded * m * number.signum();
2279 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
2280 }
2281}
2282
2283fn roman_classic(mut n: u32) -> String {
2284 let table = [
2285 (1000, "M"),
2286 (900, "CM"),
2287 (500, "D"),
2288 (400, "CD"),
2289 (100, "C"),
2290 (90, "XC"),
2291 (50, "L"),
2292 (40, "XL"),
2293 (10, "X"),
2294 (9, "IX"),
2295 (5, "V"),
2296 (4, "IV"),
2297 (1, "I"),
2298 ];
2299
2300 let mut out = String::new();
2301 for (value, glyph) in table {
2302 while n >= value {
2303 n -= value;
2304 out.push_str(glyph);
2305 }
2306 }
2307 out
2308}
2309
2310fn roman_apply_form(classic: String, form: i64) -> String {
2311 match form {
2312 0 => classic,
2313 1 => classic
2314 .replace("CM", "LM")
2315 .replace("CD", "LD")
2316 .replace("XC", "VL")
2317 .replace("XL", "VL")
2318 .replace("IX", "IV"),
2319 2 => roman_apply_form(classic, 1)
2320 .replace("LD", "XD")
2321 .replace("LM", "XM")
2322 .replace("VLIV", "IX"),
2323 3 => roman_apply_form(classic, 2)
2324 .replace("XD", "VD")
2325 .replace("XM", "VM")
2326 .replace("IX", "IV"),
2327 4 => roman_apply_form(classic, 3)
2328 .replace("VDIV", "ID")
2329 .replace("VMIV", "IM"),
2330 _ => classic,
2331 }
2332}
2333
2334#[derive(Debug)]
2335pub struct RomanFn;
2336impl Function for RomanFn {
2376 func_caps!(PURE);
2377 fn name(&self) -> &'static str {
2378 "ROMAN"
2379 }
2380 fn min_args(&self) -> usize {
2381 1
2382 }
2383 fn variadic(&self) -> bool {
2384 true
2385 }
2386 fn arg_schema(&self) -> &'static [ArgSchema] {
2387 &ARG_NUM_LENIENT_TWO[..]
2388 }
2389 fn eval<'a, 'b, 'c>(
2390 &self,
2391 args: &'c [ArgumentHandle<'a, 'b>],
2392 _ctx: &dyn FunctionContext<'b>,
2393 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2394 if args.len() > 2 {
2395 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2396 ExcelError::new_value(),
2397 )));
2398 }
2399
2400 let number = match args[0].value()?.into_literal() {
2401 LiteralValue::Error(e) => {
2402 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2403 }
2404 other => coerce_num(&other)?.trunc() as i64,
2405 };
2406
2407 if !(0..=3999).contains(&number) {
2408 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2409 ExcelError::new_value(),
2410 )));
2411 }
2412 if number == 0 {
2413 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
2414 "".to_string(),
2415 )));
2416 }
2417
2418 let form = if args.len() >= 2 {
2419 match args[1].value()?.into_literal() {
2420 LiteralValue::Boolean(b) => {
2421 if b {
2422 0
2423 } else {
2424 4
2425 }
2426 }
2427 LiteralValue::Number(n) => n.trunc() as i64,
2428 LiteralValue::Int(i) => i,
2429 LiteralValue::Empty => 0,
2430 LiteralValue::Error(e) => {
2431 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2432 }
2433 _ => {
2434 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2435 ExcelError::new_value(),
2436 )));
2437 }
2438 }
2439 } else {
2440 0
2441 };
2442
2443 if !(0..=4).contains(&form) {
2444 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2445 ExcelError::new_value(),
2446 )));
2447 }
2448
2449 let classic = roman_classic(number as u32);
2450 let text = roman_apply_form(classic, form);
2451 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(text)))
2452 }
2453}
2454
2455fn roman_digit_value(ch: char) -> Option<i64> {
2456 match ch {
2457 'I' => Some(1),
2458 'V' => Some(5),
2459 'X' => Some(10),
2460 'L' => Some(50),
2461 'C' => Some(100),
2462 'D' => Some(500),
2463 'M' => Some(1000),
2464 _ => None,
2465 }
2466}
2467
2468#[derive(Debug)]
2469pub struct ArabicFn;
2470impl Function for ArabicFn {
2510 func_caps!(PURE);
2511 fn name(&self) -> &'static str {
2512 "ARABIC"
2513 }
2514 fn min_args(&self) -> usize {
2515 1
2516 }
2517 fn arg_schema(&self) -> &'static [ArgSchema] {
2518 use std::sync::LazyLock;
2519 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
2520 &ONE[..]
2521 }
2522 fn eval<'a, 'b, 'c>(
2523 &self,
2524 args: &'c [ArgumentHandle<'a, 'b>],
2525 _ctx: &dyn FunctionContext<'b>,
2526 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2527 let raw = match args[0].value()?.into_literal() {
2528 LiteralValue::Text(s) => s,
2529 LiteralValue::Empty => String::new(),
2530 LiteralValue::Error(e) => {
2531 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2532 }
2533 _ => {
2534 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2535 ExcelError::new_value(),
2536 )));
2537 }
2538 };
2539
2540 let mut text = raw.trim().to_uppercase();
2541 if text.len() > 255 {
2542 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2543 ExcelError::new_value(),
2544 )));
2545 }
2546 if text.is_empty() {
2547 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2548 }
2549
2550 let sign = if text.starts_with('-') {
2551 text.remove(0);
2552 -1.0
2553 } else {
2554 1.0
2555 };
2556
2557 if text.is_empty() {
2558 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2559 ExcelError::new_value(),
2560 )));
2561 }
2562
2563 let mut total = 0i64;
2564 let mut prev = 0i64;
2565 for ch in text.chars().rev() {
2566 let v = match roman_digit_value(ch) {
2567 Some(v) => v,
2568 None => {
2569 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2570 ExcelError::new_value(),
2571 )));
2572 }
2573 };
2574 if v < prev {
2575 total -= v;
2576 } else {
2577 total += v;
2578 prev = v;
2579 }
2580 }
2581
2582 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2583 sign * total as f64,
2584 )))
2585 }
2586}
2587
2588#[derive(Debug)]
2591pub struct BaseFn;
2592impl Function for BaseFn {
2625 func_caps!(PURE);
2626 fn name(&self) -> &'static str {
2627 "BASE"
2628 }
2629 fn min_args(&self) -> usize {
2630 2
2631 }
2632 fn variadic(&self) -> bool {
2633 true
2634 }
2635 fn arg_schema(&self) -> &'static [ArgSchema] {
2636 use std::sync::LazyLock;
2637 static THREE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| {
2638 vec![
2639 ArgSchema::number_lenient_scalar(),
2640 ArgSchema::number_lenient_scalar(),
2641 ArgSchema::number_lenient_scalar(),
2642 ]
2643 });
2644 &THREE[..]
2645 }
2646 fn eval<'a, 'b, 'c>(
2647 &self,
2648 args: &'c [ArgumentHandle<'a, 'b>],
2649 _: &dyn FunctionContext<'b>,
2650 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2651 if args.len() < 2 || args.len() > 3 {
2652 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2653 ExcelError::new_value(),
2654 )));
2655 }
2656 let number = match args[0].value()?.into_literal() {
2657 LiteralValue::Error(e) => {
2658 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2659 }
2660 other => coerce_num(&other)?.trunc() as i64,
2661 };
2662 let radix = match args[1].value()?.into_literal() {
2663 LiteralValue::Error(e) => {
2664 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2665 }
2666 other => coerce_num(&other)?.trunc() as i64,
2667 };
2668 let min_len = if args.len() == 3 {
2669 match args[2].value()?.into_literal() {
2670 LiteralValue::Error(e) => {
2671 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2672 }
2673 other => coerce_num(&other)?.trunc() as usize,
2674 }
2675 } else {
2676 0
2677 };
2678 if !(2..=36).contains(&radix) || number < 0 {
2679 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2680 ExcelError::new_num(),
2681 )));
2682 }
2683 let mut digits = Vec::new();
2684 let mut n = number as u64;
2685 if n == 0 {
2686 digits.push('0');
2687 } else {
2688 while n > 0 {
2689 let d = (n % radix as u64) as u32;
2690 digits.push(
2691 char::from_digit(d, radix as u32)
2692 .unwrap()
2693 .to_ascii_uppercase(),
2694 );
2695 n /= radix as u64;
2696 }
2697 digits.reverse();
2698 }
2699 while digits.len() < min_len {
2700 digits.insert(0, '0');
2701 }
2702 let text: String = digits.into_iter().collect();
2703 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(text)))
2704 }
2705}
2706
2707#[derive(Debug)]
2708pub struct DecimalFn;
2709impl Function for DecimalFn {
2742 func_caps!(PURE);
2743 fn name(&self) -> &'static str {
2744 "DECIMAL"
2745 }
2746 fn min_args(&self) -> usize {
2747 2
2748 }
2749 fn arg_schema(&self) -> &'static [ArgSchema] {
2750 use std::sync::LazyLock;
2751 static SCHEMA: LazyLock<Vec<ArgSchema>> =
2752 LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::number_lenient_scalar()]);
2753 &SCHEMA[..]
2754 }
2755 fn eval<'a, 'b, 'c>(
2756 &self,
2757 args: &'c [ArgumentHandle<'a, 'b>],
2758 _: &dyn FunctionContext<'b>,
2759 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2760 if args.len() != 2 {
2761 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2762 ExcelError::new_value(),
2763 )));
2764 }
2765 let text = match args[0].value()?.into_literal() {
2766 LiteralValue::Text(s) => s,
2767 LiteralValue::Number(n) => format!("{}", n.trunc() as i64),
2768 LiteralValue::Int(i) => i.to_string(),
2769 LiteralValue::Error(e) => {
2770 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2771 }
2772 _ => {
2773 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2774 ExcelError::new_value(),
2775 )));
2776 }
2777 };
2778 let radix = match args[1].value()?.into_literal() {
2779 LiteralValue::Error(e) => {
2780 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2781 }
2782 other => coerce_num(&other)?.trunc() as u32,
2783 };
2784 if !(2..=36).contains(&radix) {
2785 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2786 ExcelError::new_num(),
2787 )));
2788 }
2789 let trimmed = text.trim();
2790 match i64::from_str_radix(trimmed, radix) {
2791 Ok(v) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2792 v as f64,
2793 ))),
2794 Err(_) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2795 ExcelError::new_num(),
2796 ))),
2797 }
2798 }
2799}
2800
2801#[derive(Debug)]
2802pub struct CeilingPreciseFn;
2803impl Function for CeilingPreciseFn {
2837 func_caps!(PURE);
2838 fn name(&self) -> &'static str {
2839 "CEILING.PRECISE"
2840 }
2841 fn min_args(&self) -> usize {
2842 1
2843 }
2844 fn variadic(&self) -> bool {
2845 true
2846 }
2847 fn arg_schema(&self) -> &'static [ArgSchema] {
2848 &ARG_NUM_LENIENT_TWO[..]
2849 }
2850 fn eval<'a, 'b, 'c>(
2851 &self,
2852 args: &'c [ArgumentHandle<'a, 'b>],
2853 _: &dyn FunctionContext<'b>,
2854 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2855 if args.is_empty() || args.len() > 2 {
2856 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2857 ExcelError::new_value(),
2858 )));
2859 }
2860 let n = match args[0].value()?.into_literal() {
2861 LiteralValue::Error(e) => {
2862 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2863 }
2864 other => coerce_num(&other)?,
2865 };
2866 let sig = if args.len() == 2 {
2867 match args[1].value()?.into_literal() {
2868 LiteralValue::Error(e) => {
2869 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2870 }
2871 other => {
2872 let v = coerce_num(&other)?;
2873 if v == 0.0 {
2874 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2875 }
2876 v.abs()
2877 }
2878 }
2879 } else {
2880 1.0
2881 };
2882 let result = (n / sig).ceil() * sig;
2883 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2884 result,
2885 )))
2886 }
2887}
2888
2889#[derive(Debug)]
2890pub struct FloorPreciseFn;
2891impl Function for FloorPreciseFn {
2924 func_caps!(PURE);
2925 fn name(&self) -> &'static str {
2926 "FLOOR.PRECISE"
2927 }
2928 fn min_args(&self) -> usize {
2929 1
2930 }
2931 fn variadic(&self) -> bool {
2932 true
2933 }
2934 fn arg_schema(&self) -> &'static [ArgSchema] {
2935 &ARG_NUM_LENIENT_TWO[..]
2936 }
2937 fn eval<'a, 'b, 'c>(
2938 &self,
2939 args: &'c [ArgumentHandle<'a, 'b>],
2940 _: &dyn FunctionContext<'b>,
2941 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2942 if args.is_empty() || args.len() > 2 {
2943 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2944 ExcelError::new_value(),
2945 )));
2946 }
2947 let n = match args[0].value()?.into_literal() {
2948 LiteralValue::Error(e) => {
2949 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2950 }
2951 other => coerce_num(&other)?,
2952 };
2953 let sig = if args.len() == 2 {
2954 match args[1].value()?.into_literal() {
2955 LiteralValue::Error(e) => {
2956 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2957 }
2958 other => {
2959 let v = coerce_num(&other)?;
2960 if v == 0.0 {
2961 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2962 }
2963 v.abs()
2964 }
2965 }
2966 } else {
2967 1.0
2968 };
2969 let result = (n / sig).floor() * sig;
2970 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2971 result,
2972 )))
2973 }
2974}
2975
2976#[derive(Debug)]
2977pub struct IsoCeilingFn;
2978impl Function for IsoCeilingFn {
3011 func_caps!(PURE);
3012 fn name(&self) -> &'static str {
3013 "ISO.CEILING"
3014 }
3015 fn min_args(&self) -> usize {
3016 1
3017 }
3018 fn variadic(&self) -> bool {
3019 true
3020 }
3021 fn arg_schema(&self) -> &'static [ArgSchema] {
3022 &ARG_NUM_LENIENT_TWO[..]
3023 }
3024 fn eval<'a, 'b, 'c>(
3025 &self,
3026 args: &'c [ArgumentHandle<'a, 'b>],
3027 _: &dyn FunctionContext<'b>,
3028 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3029 if args.is_empty() || args.len() > 2 {
3030 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3031 ExcelError::new_value(),
3032 )));
3033 }
3034 let n = match args[0].value()?.into_literal() {
3035 LiteralValue::Error(e) => {
3036 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
3037 }
3038 other => coerce_num(&other)?,
3039 };
3040 let sig = if args.len() == 2 {
3041 match args[1].value()?.into_literal() {
3042 LiteralValue::Error(e) => {
3043 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
3044 }
3045 other => {
3046 let v = coerce_num(&other)?;
3047 if v == 0.0 {
3048 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
3049 }
3050 v.abs()
3051 }
3052 }
3053 } else {
3054 1.0
3055 };
3056 let result = (n / sig).ceil() * sig;
3057 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3058 result,
3059 )))
3060 }
3061}
3062
3063fn collect_nums_from_arg<'a, 'b>(
3066 arg: &'a crate::traits::ArgumentHandle<'a, 'b>,
3067) -> Result<Vec<f64>, ExcelError> {
3068 let mut out = Vec::new();
3069 if let Ok(view) = arg.range_view() {
3070 view.for_each_cell(&mut |cell| {
3071 match cell {
3072 LiteralValue::Error(e) => return Err(e.clone()),
3073 LiteralValue::Number(n) => out.push(*n),
3074 LiteralValue::Int(i) => out.push(*i as f64),
3075 LiteralValue::Boolean(b) => out.push(if *b { 1.0 } else { 0.0 }),
3076 _ => out.push(0.0),
3077 }
3078 Ok(())
3079 })?;
3080 } else {
3081 match arg.value()?.into_literal() {
3082 LiteralValue::Error(e) => return Err(e),
3083 LiteralValue::Array(rows) => {
3084 for row in rows {
3085 for cell in row {
3086 match cell {
3087 LiteralValue::Error(e) => return Err(e),
3088 LiteralValue::Number(n) => out.push(n),
3089 LiteralValue::Int(i) => out.push(i as f64),
3090 LiteralValue::Boolean(b) => out.push(if b { 1.0 } else { 0.0 }),
3091 _ => out.push(0.0),
3092 }
3093 }
3094 }
3095 }
3096 other => {
3097 out.push(coerce_num(&other)?);
3098 }
3099 }
3100 }
3101 Ok(out)
3102}
3103
3104#[derive(Debug)]
3105pub struct SumX2MY2Fn;
3106impl Function for SumX2MY2Fn {
3140 func_caps!(PURE);
3141 fn name(&self) -> &'static str {
3142 "SUMX2MY2"
3143 }
3144 fn min_args(&self) -> usize {
3145 2
3146 }
3147 fn arg_schema(&self) -> &'static [ArgSchema] {
3148 &ARG_RANGE_NUM_LENIENT_ONE[..]
3149 }
3150 fn eval<'a, 'b, 'c>(
3151 &self,
3152 args: &'c [ArgumentHandle<'a, 'b>],
3153 _: &dyn FunctionContext<'b>,
3154 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3155 if args.len() != 2 {
3156 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3157 ExcelError::new_value(),
3158 )));
3159 }
3160 let xs = collect_nums_from_arg(&args[0])?;
3161 let ys = collect_nums_from_arg(&args[1])?;
3162 if xs.len() != ys.len() {
3163 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3164 ExcelError::new_na(),
3165 )));
3166 }
3167 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * x - y * y).sum();
3168 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3169 total,
3170 )))
3171 }
3172}
3173
3174#[derive(Debug)]
3175pub struct SumX2PY2Fn;
3176impl Function for SumX2PY2Fn {
3210 func_caps!(PURE);
3211 fn name(&self) -> &'static str {
3212 "SUMX2PY2"
3213 }
3214 fn min_args(&self) -> usize {
3215 2
3216 }
3217 fn arg_schema(&self) -> &'static [ArgSchema] {
3218 &ARG_RANGE_NUM_LENIENT_ONE[..]
3219 }
3220 fn eval<'a, 'b, 'c>(
3221 &self,
3222 args: &'c [ArgumentHandle<'a, 'b>],
3223 _: &dyn FunctionContext<'b>,
3224 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3225 if args.len() != 2 {
3226 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3227 ExcelError::new_value(),
3228 )));
3229 }
3230 let xs = collect_nums_from_arg(&args[0])?;
3231 let ys = collect_nums_from_arg(&args[1])?;
3232 if xs.len() != ys.len() {
3233 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3234 ExcelError::new_na(),
3235 )));
3236 }
3237 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * x + y * y).sum();
3238 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3239 total,
3240 )))
3241 }
3242}
3243
3244#[derive(Debug)]
3245pub struct SumXMY2Fn;
3246impl Function for SumXMY2Fn {
3280 func_caps!(PURE);
3281 fn name(&self) -> &'static str {
3282 "SUMXMY2"
3283 }
3284 fn min_args(&self) -> usize {
3285 2
3286 }
3287 fn arg_schema(&self) -> &'static [ArgSchema] {
3288 &ARG_RANGE_NUM_LENIENT_ONE[..]
3289 }
3290 fn eval<'a, 'b, 'c>(
3291 &self,
3292 args: &'c [ArgumentHandle<'a, 'b>],
3293 _: &dyn FunctionContext<'b>,
3294 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3295 if args.len() != 2 {
3296 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3297 ExcelError::new_value(),
3298 )));
3299 }
3300 let xs = collect_nums_from_arg(&args[0])?;
3301 let ys = collect_nums_from_arg(&args[1])?;
3302 if xs.len() != ys.len() {
3303 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3304 ExcelError::new_na(),
3305 )));
3306 }
3307 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| (x - y).powi(2)).sum();
3308 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3309 total,
3310 )))
3311 }
3312}
3313
3314pub fn register_builtins() {
3315 use std::sync::Arc;
3316 crate::function_registry::register_function(Arc::new(AbsFn));
3317 crate::function_registry::register_function(Arc::new(SignFn));
3318 crate::function_registry::register_function(Arc::new(IntFn));
3319 crate::function_registry::register_function(Arc::new(TruncFn));
3320 crate::function_registry::register_function(Arc::new(RoundFn));
3321 crate::function_registry::register_function(Arc::new(RoundDownFn));
3322 crate::function_registry::register_function(Arc::new(RoundUpFn));
3323 crate::function_registry::register_function(Arc::new(ModFn));
3324 crate::function_registry::register_function(Arc::new(CeilingFn));
3325 crate::function_registry::register_function(Arc::new(CeilingMathFn));
3326 crate::function_registry::register_function(Arc::new(CeilingPreciseFn));
3327 crate::function_registry::register_function(Arc::new(IsoCeilingFn));
3328 crate::function_registry::register_function(Arc::new(FloorFn));
3329 crate::function_registry::register_function(Arc::new(FloorMathFn));
3330 crate::function_registry::register_function(Arc::new(FloorPreciseFn));
3331 crate::function_registry::register_function(Arc::new(SqrtFn));
3332 crate::function_registry::register_function(Arc::new(PowerFn));
3333 crate::function_registry::register_function(Arc::new(ExpFn));
3334 crate::function_registry::register_function(Arc::new(LnFn));
3335 crate::function_registry::register_function(Arc::new(LogFn));
3336 crate::function_registry::register_function(Arc::new(Log10Fn));
3337 crate::function_registry::register_function(Arc::new(QuotientFn));
3338 crate::function_registry::register_function(Arc::new(EvenFn));
3339 crate::function_registry::register_function(Arc::new(OddFn));
3340 crate::function_registry::register_function(Arc::new(SqrtPiFn));
3341 crate::function_registry::register_function(Arc::new(MultinomialFn));
3342 crate::function_registry::register_function(Arc::new(SeriesSumFn));
3343 crate::function_registry::register_function(Arc::new(SumsqFn));
3344 crate::function_registry::register_function(Arc::new(MroundFn));
3345 crate::function_registry::register_function(Arc::new(RomanFn));
3346 crate::function_registry::register_function(Arc::new(ArabicFn));
3347 crate::function_registry::register_function(Arc::new(BaseFn));
3348 crate::function_registry::register_function(Arc::new(DecimalFn));
3349 crate::function_registry::register_function(Arc::new(SumX2MY2Fn));
3350 crate::function_registry::register_function(Arc::new(SumX2PY2Fn));
3351 crate::function_registry::register_function(Arc::new(SumXMY2Fn));
3352}
3353
3354#[cfg(test)]
3355mod tests_numeric {
3356 use super::*;
3357 use crate::test_workbook::TestWorkbook;
3358 use crate::traits::ArgumentHandle;
3359 use formualizer_common::LiteralValue;
3360 use formualizer_parse::parser::{ASTNode, ASTNodeType};
3361
3362 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
3363 wb.interpreter()
3364 }
3365 fn lit(v: LiteralValue) -> ASTNode {
3366 ASTNode::new(ASTNodeType::Literal(v), None)
3367 }
3368
3369 #[test]
3371 fn abs_basic() {
3372 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
3373 let ctx = interp(&wb);
3374 let n = lit(LiteralValue::Number(-5.5));
3375 let f = ctx.context.get_function("", "ABS").unwrap();
3376 assert_eq!(
3377 f.dispatch(
3378 &[ArgumentHandle::new(&n, &ctx)],
3379 &ctx.function_context(None)
3380 )
3381 .unwrap()
3382 .into_literal(),
3383 LiteralValue::Number(5.5)
3384 );
3385 }
3386 #[test]
3387 fn abs_error_passthrough() {
3388 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
3389 let ctx = interp(&wb);
3390 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
3391 "#VALUE!",
3392 )));
3393 let f = ctx.context.get_function("", "ABS").unwrap();
3394 match f
3395 .dispatch(
3396 &[ArgumentHandle::new(&e, &ctx)],
3397 &ctx.function_context(None),
3398 )
3399 .unwrap()
3400 .into_literal()
3401 {
3402 LiteralValue::Error(er) => assert_eq!(er, "#VALUE!"),
3403 _ => panic!(),
3404 }
3405 }
3406
3407 #[test]
3409 fn sign_neg_zero_pos() {
3410 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
3411 let ctx = interp(&wb);
3412 let f = ctx.context.get_function("", "SIGN").unwrap();
3413 let neg = lit(LiteralValue::Number(-3.2));
3414 let zero = lit(LiteralValue::Int(0));
3415 let pos = lit(LiteralValue::Int(9));
3416 assert_eq!(
3417 f.dispatch(
3418 &[ArgumentHandle::new(&neg, &ctx)],
3419 &ctx.function_context(None)
3420 )
3421 .unwrap()
3422 .into_literal(),
3423 LiteralValue::Number(-1.0)
3424 );
3425 assert_eq!(
3426 f.dispatch(
3427 &[ArgumentHandle::new(&zero, &ctx)],
3428 &ctx.function_context(None)
3429 )
3430 .unwrap()
3431 .into_literal(),
3432 LiteralValue::Number(0.0)
3433 );
3434 assert_eq!(
3435 f.dispatch(
3436 &[ArgumentHandle::new(&pos, &ctx)],
3437 &ctx.function_context(None)
3438 )
3439 .unwrap()
3440 .into_literal(),
3441 LiteralValue::Number(1.0)
3442 );
3443 }
3444 #[test]
3445 fn sign_error_passthrough() {
3446 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
3447 let ctx = interp(&wb);
3448 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
3449 "#DIV/0!",
3450 )));
3451 let f = ctx.context.get_function("", "SIGN").unwrap();
3452 match f
3453 .dispatch(
3454 &[ArgumentHandle::new(&e, &ctx)],
3455 &ctx.function_context(None),
3456 )
3457 .unwrap()
3458 .into_literal()
3459 {
3460 LiteralValue::Error(er) => assert_eq!(er, "#DIV/0!"),
3461 _ => panic!(),
3462 }
3463 }
3464
3465 #[test]
3467 fn int_floor_negative() {
3468 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
3469 let ctx = interp(&wb);
3470 let f = ctx.context.get_function("", "INT").unwrap();
3471 let n = lit(LiteralValue::Number(-3.2));
3472 assert_eq!(
3473 f.dispatch(
3474 &[ArgumentHandle::new(&n, &ctx)],
3475 &ctx.function_context(None)
3476 )
3477 .unwrap()
3478 .into_literal(),
3479 LiteralValue::Number(-4.0)
3480 );
3481 }
3482 #[test]
3483 fn int_floor_positive() {
3484 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
3485 let ctx = interp(&wb);
3486 let f = ctx.context.get_function("", "INT").unwrap();
3487 let n = lit(LiteralValue::Number(3.7));
3488 assert_eq!(
3489 f.dispatch(
3490 &[ArgumentHandle::new(&n, &ctx)],
3491 &ctx.function_context(None)
3492 )
3493 .unwrap()
3494 .into_literal(),
3495 LiteralValue::Number(3.0)
3496 );
3497 }
3498
3499 #[test]
3501 fn trunc_digits_positive_and_negative() {
3502 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
3503 let ctx = interp(&wb);
3504 let f = ctx.context.get_function("", "TRUNC").unwrap();
3505 let n = lit(LiteralValue::Number(12.3456));
3506 let d2 = lit(LiteralValue::Int(2));
3507 let dneg1 = lit(LiteralValue::Int(-1));
3508 assert_eq!(
3509 f.dispatch(
3510 &[
3511 ArgumentHandle::new(&n, &ctx),
3512 ArgumentHandle::new(&d2, &ctx)
3513 ],
3514 &ctx.function_context(None)
3515 )
3516 .unwrap()
3517 .into_literal(),
3518 LiteralValue::Number(12.34)
3519 );
3520 assert_eq!(
3521 f.dispatch(
3522 &[
3523 ArgumentHandle::new(&n, &ctx),
3524 ArgumentHandle::new(&dneg1, &ctx)
3525 ],
3526 &ctx.function_context(None)
3527 )
3528 .unwrap()
3529 .into_literal(),
3530 LiteralValue::Number(10.0)
3531 );
3532 }
3533 #[test]
3534 fn trunc_default_zero_digits() {
3535 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
3536 let ctx = interp(&wb);
3537 let f = ctx.context.get_function("", "TRUNC").unwrap();
3538 let n = lit(LiteralValue::Number(-12.999));
3539 assert_eq!(
3540 f.dispatch(
3541 &[ArgumentHandle::new(&n, &ctx)],
3542 &ctx.function_context(None)
3543 )
3544 .unwrap()
3545 .into_literal(),
3546 LiteralValue::Number(-12.0)
3547 );
3548 }
3549
3550 #[test]
3552 fn round_half_away_positive_and_negative() {
3553 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
3554 let ctx = interp(&wb);
3555 let f = ctx.context.get_function("", "ROUND").unwrap();
3556 let p = lit(LiteralValue::Number(2.5));
3557 let n = lit(LiteralValue::Number(-2.5));
3558 let d0 = lit(LiteralValue::Int(0));
3559 assert_eq!(
3560 f.dispatch(
3561 &[
3562 ArgumentHandle::new(&p, &ctx),
3563 ArgumentHandle::new(&d0, &ctx)
3564 ],
3565 &ctx.function_context(None)
3566 )
3567 .unwrap()
3568 .into_literal(),
3569 LiteralValue::Number(3.0)
3570 );
3571 assert_eq!(
3572 f.dispatch(
3573 &[
3574 ArgumentHandle::new(&n, &ctx),
3575 ArgumentHandle::new(&d0, &ctx)
3576 ],
3577 &ctx.function_context(None)
3578 )
3579 .unwrap()
3580 .into_literal(),
3581 LiteralValue::Number(-3.0)
3582 );
3583 }
3584 #[test]
3585 fn round_digits_positive() {
3586 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
3587 let ctx = interp(&wb);
3588 let f = ctx.context.get_function("", "ROUND").unwrap();
3589 let n = lit(LiteralValue::Number(1.2345));
3590 let d = lit(LiteralValue::Int(3));
3591 assert_eq!(
3592 f.dispatch(
3593 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3594 &ctx.function_context(None)
3595 )
3596 .unwrap()
3597 .into_literal(),
3598 LiteralValue::Number(1.235)
3599 );
3600 }
3601
3602 #[test]
3604 fn rounddown_truncates() {
3605 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
3606 let ctx = interp(&wb);
3607 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
3608 let n = lit(LiteralValue::Number(1.299));
3609 let d = lit(LiteralValue::Int(2));
3610 assert_eq!(
3611 f.dispatch(
3612 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3613 &ctx.function_context(None)
3614 )
3615 .unwrap()
3616 .into_literal(),
3617 LiteralValue::Number(1.29)
3618 );
3619 }
3620 #[test]
3621 fn rounddown_negative_number() {
3622 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
3623 let ctx = interp(&wb);
3624 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
3625 let n = lit(LiteralValue::Number(-1.299));
3626 let d = lit(LiteralValue::Int(2));
3627 assert_eq!(
3628 f.dispatch(
3629 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3630 &ctx.function_context(None)
3631 )
3632 .unwrap()
3633 .into_literal(),
3634 LiteralValue::Number(-1.29)
3635 );
3636 }
3637
3638 #[test]
3640 fn roundup_away_from_zero() {
3641 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
3642 let ctx = interp(&wb);
3643 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
3644 let n = lit(LiteralValue::Number(1.001));
3645 let d = lit(LiteralValue::Int(2));
3646 assert_eq!(
3647 f.dispatch(
3648 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3649 &ctx.function_context(None)
3650 )
3651 .unwrap()
3652 .into_literal(),
3653 LiteralValue::Number(1.01)
3654 );
3655 }
3656 #[test]
3657 fn roundup_negative() {
3658 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
3659 let ctx = interp(&wb);
3660 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
3661 let n = lit(LiteralValue::Number(-1.001));
3662 let d = lit(LiteralValue::Int(2));
3663 assert_eq!(
3664 f.dispatch(
3665 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3666 &ctx.function_context(None)
3667 )
3668 .unwrap()
3669 .into_literal(),
3670 LiteralValue::Number(-1.01)
3671 );
3672 }
3673
3674 #[test]
3676 fn mod_positive_negative_cases() {
3677 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
3678 let ctx = interp(&wb);
3679 let f = ctx.context.get_function("", "MOD").unwrap();
3680 let a = lit(LiteralValue::Int(-3));
3681 let b = lit(LiteralValue::Int(2));
3682 let out = f
3683 .dispatch(
3684 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
3685 &ctx.function_context(None),
3686 )
3687 .unwrap();
3688 assert_eq!(out, LiteralValue::Number(1.0));
3689 let a2 = lit(LiteralValue::Int(3));
3690 let b2 = lit(LiteralValue::Int(-2));
3691 let out2 = f
3692 .dispatch(
3693 &[
3694 ArgumentHandle::new(&a2, &ctx),
3695 ArgumentHandle::new(&b2, &ctx),
3696 ],
3697 &ctx.function_context(None),
3698 )
3699 .unwrap();
3700 assert_eq!(out2, LiteralValue::Number(-1.0));
3701 }
3702 #[test]
3703 fn mod_div_by_zero_error() {
3704 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
3705 let ctx = interp(&wb);
3706 let f = ctx.context.get_function("", "MOD").unwrap();
3707 let a = lit(LiteralValue::Int(5));
3708 let zero = lit(LiteralValue::Int(0));
3709 match f
3710 .dispatch(
3711 &[
3712 ArgumentHandle::new(&a, &ctx),
3713 ArgumentHandle::new(&zero, &ctx),
3714 ],
3715 &ctx.function_context(None),
3716 )
3717 .unwrap()
3718 .into_literal()
3719 {
3720 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
3721 _ => panic!(),
3722 }
3723 }
3724
3725 #[test]
3727 fn sqrt_basic_and_domain() {
3728 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SqrtFn));
3729 let ctx = interp(&wb);
3730 let f = ctx.context.get_function("", "SQRT").unwrap();
3731 let n = lit(LiteralValue::Number(9.0));
3732 let out = f
3733 .dispatch(
3734 &[ArgumentHandle::new(&n, &ctx)],
3735 &ctx.function_context(None),
3736 )
3737 .unwrap();
3738 assert_eq!(out, LiteralValue::Number(3.0));
3739 let neg = lit(LiteralValue::Number(-1.0));
3740 let out2 = f
3741 .dispatch(
3742 &[ArgumentHandle::new(&neg, &ctx)],
3743 &ctx.function_context(None),
3744 )
3745 .unwrap();
3746 assert!(matches!(out2.into_literal(), LiteralValue::Error(_)));
3747 }
3748
3749 #[test]
3750 fn power_fractional_negative_domain() {
3751 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PowerFn));
3752 let ctx = interp(&wb);
3753 let f = ctx.context.get_function("", "POWER").unwrap();
3754 let a = lit(LiteralValue::Number(-4.0));
3755 let half = lit(LiteralValue::Number(0.5));
3756 let out = f
3757 .dispatch(
3758 &[
3759 ArgumentHandle::new(&a, &ctx),
3760 ArgumentHandle::new(&half, &ctx),
3761 ],
3762 &ctx.function_context(None),
3763 )
3764 .unwrap();
3765 assert!(matches!(out.into_literal(), LiteralValue::Error(_))); }
3767
3768 #[test]
3769 fn log_variants() {
3770 let wb = TestWorkbook::new()
3771 .with_function(std::sync::Arc::new(LogFn))
3772 .with_function(std::sync::Arc::new(Log10Fn))
3773 .with_function(std::sync::Arc::new(LnFn));
3774 let ctx = interp(&wb);
3775 let logf = ctx.context.get_function("", "LOG").unwrap();
3776 let log10f = ctx.context.get_function("", "LOG10").unwrap();
3777 let lnf = ctx.context.get_function("", "LN").unwrap();
3778 let n = lit(LiteralValue::Number(100.0));
3779 let base = lit(LiteralValue::Number(10.0));
3780 assert_eq!(
3781 logf.dispatch(
3782 &[
3783 ArgumentHandle::new(&n, &ctx),
3784 ArgumentHandle::new(&base, &ctx)
3785 ],
3786 &ctx.function_context(None)
3787 )
3788 .unwrap()
3789 .into_literal(),
3790 LiteralValue::Number(2.0)
3791 );
3792 assert_eq!(
3793 log10f
3794 .dispatch(
3795 &[ArgumentHandle::new(&n, &ctx)],
3796 &ctx.function_context(None)
3797 )
3798 .unwrap()
3799 .into_literal(),
3800 LiteralValue::Number(2.0)
3801 );
3802 assert_eq!(
3803 lnf.dispatch(
3804 &[ArgumentHandle::new(&n, &ctx)],
3805 &ctx.function_context(None)
3806 )
3807 .unwrap()
3808 .into_literal(),
3809 LiteralValue::Number(100.0f64.ln())
3810 );
3811 }
3812 #[test]
3813 fn ceiling_floor_basic() {
3814 let wb = TestWorkbook::new()
3815 .with_function(std::sync::Arc::new(CeilingFn))
3816 .with_function(std::sync::Arc::new(FloorFn))
3817 .with_function(std::sync::Arc::new(CeilingMathFn))
3818 .with_function(std::sync::Arc::new(FloorMathFn));
3819 let ctx = interp(&wb);
3820 let c = ctx.context.get_function("", "CEILING").unwrap();
3821 let f = ctx.context.get_function("", "FLOOR").unwrap();
3822 let n = lit(LiteralValue::Number(5.1));
3823 let sig = lit(LiteralValue::Number(2.0));
3824 assert_eq!(
3825 c.dispatch(
3826 &[
3827 ArgumentHandle::new(&n, &ctx),
3828 ArgumentHandle::new(&sig, &ctx)
3829 ],
3830 &ctx.function_context(None)
3831 )
3832 .unwrap()
3833 .into_literal(),
3834 LiteralValue::Number(6.0)
3835 );
3836 assert_eq!(
3837 f.dispatch(
3838 &[
3839 ArgumentHandle::new(&n, &ctx),
3840 ArgumentHandle::new(&sig, &ctx)
3841 ],
3842 &ctx.function_context(None)
3843 )
3844 .unwrap()
3845 .into_literal(),
3846 LiteralValue::Number(4.0)
3847 );
3848 }
3849
3850 #[test]
3851 fn quotient_basic_and_div_zero() {
3852 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(QuotientFn));
3853 let ctx = interp(&wb);
3854 let f = ctx.context.get_function("", "QUOTIENT").unwrap();
3855
3856 let ten = lit(LiteralValue::Int(10));
3857 let three = lit(LiteralValue::Int(3));
3858 assert_eq!(
3859 f.dispatch(
3860 &[
3861 ArgumentHandle::new(&ten, &ctx),
3862 ArgumentHandle::new(&three, &ctx),
3863 ],
3864 &ctx.function_context(None),
3865 )
3866 .unwrap()
3867 .into_literal(),
3868 LiteralValue::Number(3.0)
3869 );
3870
3871 let neg_ten = lit(LiteralValue::Int(-10));
3872 assert_eq!(
3873 f.dispatch(
3874 &[
3875 ArgumentHandle::new(&neg_ten, &ctx),
3876 ArgumentHandle::new(&three, &ctx),
3877 ],
3878 &ctx.function_context(None),
3879 )
3880 .unwrap()
3881 .into_literal(),
3882 LiteralValue::Number(-3.0)
3883 );
3884
3885 let zero = lit(LiteralValue::Int(0));
3886 match f
3887 .dispatch(
3888 &[
3889 ArgumentHandle::new(&ten, &ctx),
3890 ArgumentHandle::new(&zero, &ctx),
3891 ],
3892 &ctx.function_context(None),
3893 )
3894 .unwrap()
3895 .into_literal()
3896 {
3897 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
3898 other => panic!("expected #DIV/0!, got {other:?}"),
3899 }
3900 }
3901
3902 #[test]
3903 fn even_odd_examples() {
3904 let wb = TestWorkbook::new()
3905 .with_function(std::sync::Arc::new(EvenFn))
3906 .with_function(std::sync::Arc::new(OddFn));
3907 let ctx = interp(&wb);
3908
3909 let even = ctx.context.get_function("", "EVEN").unwrap();
3910 let odd = ctx.context.get_function("", "ODD").unwrap();
3911
3912 let one_half = lit(LiteralValue::Number(1.5));
3913 let three = lit(LiteralValue::Int(3));
3914 let neg_one = lit(LiteralValue::Int(-1));
3915 let two = lit(LiteralValue::Int(2));
3916 let zero = lit(LiteralValue::Int(0));
3917
3918 assert_eq!(
3919 even.dispatch(
3920 &[ArgumentHandle::new(&one_half, &ctx)],
3921 &ctx.function_context(None),
3922 )
3923 .unwrap()
3924 .into_literal(),
3925 LiteralValue::Number(2.0)
3926 );
3927 assert_eq!(
3928 even.dispatch(
3929 &[ArgumentHandle::new(&three, &ctx)],
3930 &ctx.function_context(None),
3931 )
3932 .unwrap()
3933 .into_literal(),
3934 LiteralValue::Number(4.0)
3935 );
3936 assert_eq!(
3937 even.dispatch(
3938 &[ArgumentHandle::new(&neg_one, &ctx)],
3939 &ctx.function_context(None),
3940 )
3941 .unwrap()
3942 .into_literal(),
3943 LiteralValue::Number(-2.0)
3944 );
3945 assert_eq!(
3946 even.dispatch(
3947 &[ArgumentHandle::new(&two, &ctx)],
3948 &ctx.function_context(None),
3949 )
3950 .unwrap()
3951 .into_literal(),
3952 LiteralValue::Number(2.0)
3953 );
3954
3955 assert_eq!(
3956 odd.dispatch(
3957 &[ArgumentHandle::new(&one_half, &ctx)],
3958 &ctx.function_context(None),
3959 )
3960 .unwrap()
3961 .into_literal(),
3962 LiteralValue::Number(3.0)
3963 );
3964 assert_eq!(
3965 odd.dispatch(
3966 &[ArgumentHandle::new(&two, &ctx)],
3967 &ctx.function_context(None),
3968 )
3969 .unwrap()
3970 .into_literal(),
3971 LiteralValue::Number(3.0)
3972 );
3973 assert_eq!(
3974 odd.dispatch(
3975 &[ArgumentHandle::new(&neg_one, &ctx)],
3976 &ctx.function_context(None),
3977 )
3978 .unwrap()
3979 .into_literal(),
3980 LiteralValue::Number(-1.0)
3981 );
3982 assert_eq!(
3983 odd.dispatch(
3984 &[ArgumentHandle::new(&zero, &ctx)],
3985 &ctx.function_context(None),
3986 )
3987 .unwrap()
3988 .into_literal(),
3989 LiteralValue::Number(1.0)
3990 );
3991 }
3992
3993 #[test]
3994 fn sqrtpi_multinomial_and_seriessum_examples() {
3995 let wb = TestWorkbook::new()
3996 .with_function(std::sync::Arc::new(SqrtPiFn))
3997 .with_function(std::sync::Arc::new(MultinomialFn))
3998 .with_function(std::sync::Arc::new(SeriesSumFn));
3999 let ctx = interp(&wb);
4000
4001 let sqrtpi = ctx.context.get_function("", "SQRTPI").unwrap();
4002 let one = lit(LiteralValue::Int(1));
4003 match sqrtpi
4004 .dispatch(
4005 &[ArgumentHandle::new(&one, &ctx)],
4006 &ctx.function_context(None),
4007 )
4008 .unwrap()
4009 .into_literal()
4010 {
4011 LiteralValue::Number(v) => assert!((v - std::f64::consts::PI.sqrt()).abs() < 1e-12),
4012 other => panic!("expected numeric SQRTPI, got {other:?}"),
4013 }
4014
4015 let multinomial = ctx.context.get_function("", "MULTINOMIAL").unwrap();
4016 let two = lit(LiteralValue::Int(2));
4017 let three = lit(LiteralValue::Int(3));
4018 let four = lit(LiteralValue::Int(4));
4019 assert_eq!(
4020 multinomial
4021 .dispatch(
4022 &[
4023 ArgumentHandle::new(&two, &ctx),
4024 ArgumentHandle::new(&three, &ctx),
4025 ArgumentHandle::new(&four, &ctx),
4026 ],
4027 &ctx.function_context(None),
4028 )
4029 .unwrap()
4030 .into_literal(),
4031 LiteralValue::Number(1260.0)
4032 );
4033
4034 let seriessum = ctx.context.get_function("", "SERIESSUM").unwrap();
4035 let x = lit(LiteralValue::Int(2));
4036 let n0 = lit(LiteralValue::Int(0));
4037 let m1 = lit(LiteralValue::Int(1));
4038 let coeffs = ASTNode::new(
4039 ASTNodeType::Literal(LiteralValue::Array(vec![vec![
4040 LiteralValue::Int(1),
4041 LiteralValue::Int(2),
4042 LiteralValue::Int(3),
4043 ]])),
4044 None,
4045 );
4046 assert_eq!(
4047 seriessum
4048 .dispatch(
4049 &[
4050 ArgumentHandle::new(&x, &ctx),
4051 ArgumentHandle::new(&n0, &ctx),
4052 ArgumentHandle::new(&m1, &ctx),
4053 ArgumentHandle::new(&coeffs, &ctx),
4054 ],
4055 &ctx.function_context(None),
4056 )
4057 .unwrap()
4058 .into_literal(),
4059 LiteralValue::Number(17.0)
4060 );
4061 }
4062
4063 #[test]
4064 fn sumsq_basic() {
4065 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumsqFn));
4066 let ctx = interp(&wb);
4067 let f = ctx.context.get_function("", "SUMSQ").unwrap();
4068 let a = lit(LiteralValue::Int(3));
4069 let b = lit(LiteralValue::Int(4));
4070 assert_eq!(
4071 f.dispatch(
4072 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
4073 &ctx.function_context(None)
4074 )
4075 .unwrap()
4076 .into_literal(),
4077 LiteralValue::Number(25.0)
4078 );
4079 }
4080
4081 #[test]
4082 fn mround_sign_and_midpoint() {
4083 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(MroundFn));
4084 let ctx = interp(&wb);
4085 let f = ctx.context.get_function("", "MROUND").unwrap();
4086
4087 let n = lit(LiteralValue::Number(1.3));
4088 let m = lit(LiteralValue::Number(0.2));
4089 match f
4090 .dispatch(
4091 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&m, &ctx)],
4092 &ctx.function_context(None),
4093 )
4094 .unwrap()
4095 .into_literal()
4096 {
4097 LiteralValue::Number(v) => assert!((v - 1.4).abs() < 1e-12),
4098 other => panic!("expected numeric result, got {other:?}"),
4099 }
4100
4101 let bad_m = lit(LiteralValue::Number(-2.0));
4102 let five = lit(LiteralValue::Number(5.0));
4103 match f
4104 .dispatch(
4105 &[
4106 ArgumentHandle::new(&five, &ctx),
4107 ArgumentHandle::new(&bad_m, &ctx),
4108 ],
4109 &ctx.function_context(None),
4110 )
4111 .unwrap()
4112 .into_literal()
4113 {
4114 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
4115 other => panic!("expected #NUM!, got {other:?}"),
4116 }
4117 }
4118
4119 #[test]
4120 fn roman_and_arabic_examples() {
4121 let wb = TestWorkbook::new()
4122 .with_function(std::sync::Arc::new(RomanFn))
4123 .with_function(std::sync::Arc::new(ArabicFn));
4124 let ctx = interp(&wb);
4125
4126 let roman = ctx.context.get_function("", "ROMAN").unwrap();
4127 let n499 = lit(LiteralValue::Int(499));
4128 let out = roman
4129 .dispatch(
4130 &[ArgumentHandle::new(&n499, &ctx)],
4131 &ctx.function_context(None),
4132 )
4133 .unwrap()
4134 .into_literal();
4135 assert_eq!(out, LiteralValue::Text("CDXCIX".to_string()));
4136
4137 let form4 = lit(LiteralValue::Int(4));
4138 let out_form4 = roman
4139 .dispatch(
4140 &[
4141 ArgumentHandle::new(&n499, &ctx),
4142 ArgumentHandle::new(&form4, &ctx),
4143 ],
4144 &ctx.function_context(None),
4145 )
4146 .unwrap()
4147 .into_literal();
4148 assert_eq!(out_form4, LiteralValue::Text("ID".to_string()));
4149
4150 let arabic = ctx.context.get_function("", "ARABIC").unwrap();
4151 let roman_text = lit(LiteralValue::Text("CDXCIX".to_string()));
4152 let out_arabic = arabic
4153 .dispatch(
4154 &[ArgumentHandle::new(&roman_text, &ctx)],
4155 &ctx.function_context(None),
4156 )
4157 .unwrap()
4158 .into_literal();
4159 assert_eq!(out_arabic, LiteralValue::Number(499.0));
4160 }
4161
4162 #[test]
4165 fn round_one_arg_returns_error_not_panic() {
4166 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
4167 let ctx = interp(&wb);
4168 let f = ctx.context.get_function("", "ROUND").unwrap();
4169 let n = lit(LiteralValue::Number(2.5));
4170 let result = f
4171 .dispatch(
4172 &[ArgumentHandle::new(&n, &ctx)],
4173 &ctx.function_context(None),
4174 )
4175 .unwrap()
4176 .into_literal();
4177 assert!(
4178 matches!(result, LiteralValue::Error(_)),
4179 "Expected an error, got {result:?}"
4180 );
4181 }
4182
4183 #[test]
4184 fn rounddown_one_arg_returns_error_not_panic() {
4185 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
4186 let ctx = interp(&wb);
4187 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
4188 let n = lit(LiteralValue::Number(1.9));
4189 let result = f
4190 .dispatch(
4191 &[ArgumentHandle::new(&n, &ctx)],
4192 &ctx.function_context(None),
4193 )
4194 .unwrap()
4195 .into_literal();
4196 assert!(
4197 matches!(result, LiteralValue::Error(_)),
4198 "Expected an error, got {result:?}"
4199 );
4200 }
4201
4202 #[test]
4203 fn abs_zero_args_returns_error_not_panic() {
4204 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
4205 let ctx = interp(&wb);
4206 let f = ctx.context.get_function("", "ABS").unwrap();
4207 let result = f
4208 .dispatch(&[], &ctx.function_context(None))
4209 .unwrap()
4210 .into_literal();
4211 assert!(
4212 matches!(result, LiteralValue::Error(_)),
4213 "Expected an error, got {result:?}"
4214 );
4215 }
4216
4217 #[test]
4218 fn mod_one_arg_returns_error_not_panic() {
4219 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
4220 let ctx = interp(&wb);
4221 let f = ctx.context.get_function("", "MOD").unwrap();
4222 let n = lit(LiteralValue::Number(10.0));
4223 let result = f
4224 .dispatch(
4225 &[ArgumentHandle::new(&n, &ctx)],
4226 &ctx.function_context(None),
4227 )
4228 .unwrap()
4229 .into_literal();
4230 assert!(
4231 matches!(result, LiteralValue::Error(_)),
4232 "Expected an error, got {result:?}"
4233 );
4234 }
4235}