1use super::super::utils::{
2 ARG_NUM_LENIENT_ONE, ARG_NUM_LENIENT_TWO, ARG_RANGE_NUM_LENIENT_ONE, coerce_num,
3};
4use crate::args::ArgSchema;
5use crate::function::Function;
6use crate::traits::{ArgumentHandle, FunctionContext};
7use formualizer_common::{ExcelError, LiteralValue};
8use formualizer_macros::func_caps;
9
10#[derive(Debug)]
11pub struct AbsFn;
12impl Function for AbsFn {
54 func_caps!(PURE);
55 fn name(&self) -> &'static str {
56 "ABS"
57 }
58 fn min_args(&self) -> usize {
59 1
60 }
61 fn arg_schema(&self) -> &'static [ArgSchema] {
62 &ARG_NUM_LENIENT_ONE[..]
63 }
64 fn eval<'a, 'b, 'c>(
65 &self,
66 args: &'c [ArgumentHandle<'a, 'b>],
67 _: &dyn FunctionContext<'b>,
68 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
69 let v = args[0].value()?.into_literal();
70 match v {
71 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
72 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
73 coerce_num(&other)?.abs(),
74 ))),
75 }
76 }
77}
78
79#[derive(Debug)]
80pub struct SignFn;
81impl Function for SignFn {
121 func_caps!(PURE);
122 fn name(&self) -> &'static str {
123 "SIGN"
124 }
125 fn min_args(&self) -> usize {
126 1
127 }
128 fn arg_schema(&self) -> &'static [ArgSchema] {
129 &ARG_NUM_LENIENT_ONE[..]
130 }
131 fn eval<'a, 'b, 'c>(
132 &self,
133 args: &'c [ArgumentHandle<'a, 'b>],
134 _: &dyn FunctionContext<'b>,
135 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
136 let v = args[0].value()?.into_literal();
137 match v {
138 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
139 other => {
140 let n = coerce_num(&other)?;
141 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
142 if n > 0.0 {
143 1.0
144 } else if n < 0.0 {
145 -1.0
146 } else {
147 0.0
148 },
149 )))
150 }
151 }
152 }
153}
154
155#[derive(Debug)]
156pub struct IntFn; impl Function for IntFn {
199 func_caps!(PURE);
200 fn name(&self) -> &'static str {
201 "INT"
202 }
203 fn min_args(&self) -> usize {
204 1
205 }
206 fn arg_schema(&self) -> &'static [ArgSchema] {
207 &ARG_NUM_LENIENT_ONE[..]
208 }
209 fn eval<'a, 'b, 'c>(
210 &self,
211 args: &'c [ArgumentHandle<'a, 'b>],
212 _: &dyn FunctionContext<'b>,
213 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
214 let v = args[0].value()?.into_literal();
215 match v {
216 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
217 other => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
218 coerce_num(&other)?.floor(),
219 ))),
220 }
221 }
222}
223
224#[derive(Debug)]
225pub struct TruncFn; impl Function for TruncFn {
266 func_caps!(PURE);
267 fn name(&self) -> &'static str {
268 "TRUNC"
269 }
270 fn min_args(&self) -> usize {
271 1
272 }
273 fn variadic(&self) -> bool {
274 true
275 }
276 fn arg_schema(&self) -> &'static [ArgSchema] {
277 &ARG_NUM_LENIENT_TWO[..]
278 }
279 fn eval<'a, 'b, 'c>(
280 &self,
281 args: &'c [ArgumentHandle<'a, 'b>],
282 _: &dyn FunctionContext<'b>,
283 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
284 if args.is_empty() || args.len() > 2 {
285 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
286 ExcelError::new_value(),
287 )));
288 }
289 let mut n = match args[0].value()?.into_literal() {
290 LiteralValue::Error(e) => {
291 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
292 }
293 other => coerce_num(&other)?,
294 };
295 let digits: i32 = if args.len() == 2 {
296 match args[1].value()?.into_literal() {
297 LiteralValue::Error(e) => {
298 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
299 }
300 other => coerce_num(&other)? as i32,
301 }
302 } else {
303 0
304 };
305 if digits >= 0 {
306 let f = 10f64.powi(digits);
307 n = (n * f).trunc() / f;
308 } else {
309 let f = 10f64.powi(-digits);
310 n = (n / f).trunc() * f;
311 }
312 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(n)))
313 }
314}
315
316#[derive(Debug)]
317pub struct RoundFn; impl Function for RoundFn {
358 func_caps!(PURE);
359 fn name(&self) -> &'static str {
360 "ROUND"
361 }
362 fn min_args(&self) -> usize {
363 2
364 }
365 fn arg_schema(&self) -> &'static [ArgSchema] {
366 &ARG_NUM_LENIENT_TWO[..]
367 }
368 fn eval<'a, 'b, 'c>(
369 &self,
370 args: &'c [ArgumentHandle<'a, 'b>],
371 _: &dyn FunctionContext<'b>,
372 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
373 let n = match args[0].value()?.into_literal() {
374 LiteralValue::Error(e) => {
375 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
376 }
377 other => coerce_num(&other)?,
378 };
379 let digits = match args[1].value()?.into_literal() {
380 LiteralValue::Error(e) => {
381 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
382 }
383 other => coerce_num(&other)? as i32,
384 };
385 let f = 10f64.powi(digits.abs());
386 let out = if digits >= 0 {
387 (n * f).round() / f
388 } else {
389 (n / f).round() * f
390 };
391 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
392 }
393}
394
395#[derive(Debug)]
396pub struct RoundDownFn; impl Function for RoundDownFn {
437 func_caps!(PURE);
438 fn name(&self) -> &'static str {
439 "ROUNDDOWN"
440 }
441 fn min_args(&self) -> usize {
442 2
443 }
444 fn arg_schema(&self) -> &'static [ArgSchema] {
445 &ARG_NUM_LENIENT_TWO[..]
446 }
447 fn eval<'a, 'b, 'c>(
448 &self,
449 args: &'c [ArgumentHandle<'a, 'b>],
450 _: &dyn FunctionContext<'b>,
451 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
452 let n = match args[0].value()?.into_literal() {
453 LiteralValue::Error(e) => {
454 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
455 }
456 other => coerce_num(&other)?,
457 };
458 let digits = match args[1].value()?.into_literal() {
459 LiteralValue::Error(e) => {
460 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
461 }
462 other => coerce_num(&other)? as i32,
463 };
464 let f = 10f64.powi(digits.abs());
465 let out = if digits >= 0 {
466 (n * f).trunc() / f
467 } else {
468 (n / f).trunc() * f
469 };
470 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
471 }
472}
473
474#[derive(Debug)]
475pub struct RoundUpFn; impl Function for RoundUpFn {
516 func_caps!(PURE);
517 fn name(&self) -> &'static str {
518 "ROUNDUP"
519 }
520 fn min_args(&self) -> usize {
521 2
522 }
523 fn arg_schema(&self) -> &'static [ArgSchema] {
524 &ARG_NUM_LENIENT_TWO[..]
525 }
526 fn eval<'a, 'b, 'c>(
527 &self,
528 args: &'c [ArgumentHandle<'a, 'b>],
529 _: &dyn FunctionContext<'b>,
530 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
531 let n = match args[0].value()?.into_literal() {
532 LiteralValue::Error(e) => {
533 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
534 }
535 other => coerce_num(&other)?,
536 };
537 let digits = match args[1].value()?.into_literal() {
538 LiteralValue::Error(e) => {
539 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
540 }
541 other => coerce_num(&other)? as i32,
542 };
543 let f = 10f64.powi(digits.abs());
544 let mut scaled = if digits >= 0 { n * f } else { n / f };
545 if scaled > 0.0 {
546 scaled = scaled.ceil();
547 } else {
548 scaled = scaled.floor();
549 }
550 let out = if digits >= 0 { scaled / f } else { scaled * f };
551 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
552 }
553}
554
555#[derive(Debug)]
556pub struct ModFn; impl Function for ModFn {
597 func_caps!(PURE);
598 fn name(&self) -> &'static str {
599 "MOD"
600 }
601 fn min_args(&self) -> usize {
602 2
603 }
604 fn arg_schema(&self) -> &'static [ArgSchema] {
605 &ARG_NUM_LENIENT_TWO[..]
606 }
607 fn eval<'a, 'b, 'c>(
608 &self,
609 args: &'c [ArgumentHandle<'a, 'b>],
610 _: &dyn FunctionContext<'b>,
611 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
612 let x = match args[0].value()?.into_literal() {
613 LiteralValue::Error(e) => {
614 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
615 }
616 other => coerce_num(&other)?,
617 };
618 let y = match args[1].value()?.into_literal() {
619 LiteralValue::Error(e) => {
620 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
621 }
622 other => coerce_num(&other)?,
623 };
624 if y == 0.0 {
625 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
626 ExcelError::from_error_string("#DIV/0!"),
627 )));
628 }
629 let m = x % y;
630 let mut r = if m == 0.0 {
631 0.0
632 } else if (y > 0.0 && m < 0.0) || (y < 0.0 && m > 0.0) {
633 m + y
634 } else {
635 m
636 };
637 if r == -0.0 {
638 r = 0.0;
639 }
640 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(r)))
641 }
642}
643
644#[derive(Debug)]
647pub struct CeilingFn; impl Function for CeilingFn {
690 func_caps!(PURE);
691 fn name(&self) -> &'static str {
692 "CEILING"
693 }
694 fn min_args(&self) -> usize {
695 1
696 }
697 fn variadic(&self) -> bool {
698 true
699 }
700 fn arg_schema(&self) -> &'static [ArgSchema] {
701 &ARG_NUM_LENIENT_TWO[..]
702 }
703 fn eval<'a, 'b, 'c>(
704 &self,
705 args: &'c [ArgumentHandle<'a, 'b>],
706 _: &dyn FunctionContext<'b>,
707 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
708 if args.is_empty() || args.len() > 2 {
709 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
710 ExcelError::new_value(),
711 )));
712 }
713 let n = match args[0].value()?.into_literal() {
714 LiteralValue::Error(e) => {
715 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
716 }
717 other => coerce_num(&other)?,
718 };
719 let mut sig = if args.len() == 2 {
720 match args[1].value()?.into_literal() {
721 LiteralValue::Error(e) => {
722 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
723 }
724 other => coerce_num(&other)?,
725 }
726 } else {
727 1.0
728 };
729 if sig == 0.0 {
730 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
731 ExcelError::from_error_string("#DIV/0!"),
732 )));
733 }
734 if sig < 0.0 {
735 sig = sig.abs(); }
737 let k = (n / sig).ceil();
738 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
739 k * sig,
740 )))
741 }
742}
743
744#[derive(Debug)]
745pub struct CeilingMathFn; impl Function for CeilingMathFn {
786 func_caps!(PURE);
787 fn name(&self) -> &'static str {
788 "CEILING.MATH"
789 }
790 fn min_args(&self) -> usize {
791 1
792 }
793 fn variadic(&self) -> bool {
794 true
795 }
796 fn arg_schema(&self) -> &'static [ArgSchema] {
797 &ARG_NUM_LENIENT_TWO[..]
798 } fn eval<'a, 'b, 'c>(
800 &self,
801 args: &'c [ArgumentHandle<'a, 'b>],
802 _: &dyn FunctionContext<'b>,
803 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
804 if args.is_empty() || args.len() > 3 {
805 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
806 ExcelError::new_value(),
807 )));
808 }
809 let n = match args[0].value()?.into_literal() {
810 LiteralValue::Error(e) => {
811 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
812 }
813 other => coerce_num(&other)?,
814 };
815 let sig = if args.len() >= 2 {
816 match args[1].value()?.into_literal() {
817 LiteralValue::Error(e) => {
818 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
819 }
820 other => {
821 let v = coerce_num(&other)?;
822 if v == 0.0 { 1.0 } else { v.abs() }
823 }
824 }
825 } else {
826 1.0
827 };
828 let mode_nonzero = if args.len() == 3 {
829 match args[2].value()?.into_literal() {
830 LiteralValue::Error(e) => {
831 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
832 }
833 other => coerce_num(&other)? != 0.0,
834 }
835 } else {
836 false
837 };
838 let result = if n >= 0.0 {
839 (n / sig).ceil() * sig
840 } else if mode_nonzero {
841 (n / sig).floor() * sig } else {
843 (n / sig).ceil() * sig };
845 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
846 result,
847 )))
848 }
849}
850
851#[derive(Debug)]
852pub struct FloorFn; impl Function for FloorFn {
895 func_caps!(PURE);
896 fn name(&self) -> &'static str {
897 "FLOOR"
898 }
899 fn min_args(&self) -> usize {
900 1
901 }
902 fn variadic(&self) -> bool {
903 true
904 }
905 fn arg_schema(&self) -> &'static [ArgSchema] {
906 &ARG_NUM_LENIENT_TWO[..]
907 }
908 fn eval<'a, 'b, 'c>(
909 &self,
910 args: &'c [ArgumentHandle<'a, 'b>],
911 _: &dyn FunctionContext<'b>,
912 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
913 if args.is_empty() || args.len() > 2 {
914 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
915 ExcelError::new_value(),
916 )));
917 }
918 let n = match args[0].value()?.into_literal() {
919 LiteralValue::Error(e) => {
920 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
921 }
922 other => coerce_num(&other)?,
923 };
924 let mut sig = if args.len() == 2 {
925 match args[1].value()?.into_literal() {
926 LiteralValue::Error(e) => {
927 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
928 }
929 other => coerce_num(&other)?,
930 }
931 } else {
932 1.0
933 };
934 if sig == 0.0 {
935 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
936 ExcelError::from_error_string("#DIV/0!"),
937 )));
938 }
939 if sig < 0.0 {
940 sig = sig.abs();
941 }
942 let k = (n / sig).floor();
943 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
944 k * sig,
945 )))
946 }
947}
948
949#[derive(Debug)]
950pub struct FloorMathFn; impl Function for FloorMathFn {
991 func_caps!(PURE);
992 fn name(&self) -> &'static str {
993 "FLOOR.MATH"
994 }
995 fn min_args(&self) -> usize {
996 1
997 }
998 fn variadic(&self) -> bool {
999 true
1000 }
1001 fn arg_schema(&self) -> &'static [ArgSchema] {
1002 &ARG_NUM_LENIENT_TWO[..]
1003 }
1004 fn eval<'a, 'b, 'c>(
1005 &self,
1006 args: &'c [ArgumentHandle<'a, 'b>],
1007 _: &dyn FunctionContext<'b>,
1008 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1009 if args.is_empty() || args.len() > 3 {
1010 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1011 ExcelError::new_value(),
1012 )));
1013 }
1014 let n = match args[0].value()?.into_literal() {
1015 LiteralValue::Error(e) => {
1016 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1017 }
1018 other => coerce_num(&other)?,
1019 };
1020 let sig = if args.len() >= 2 {
1021 match args[1].value()?.into_literal() {
1022 LiteralValue::Error(e) => {
1023 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1024 }
1025 other => {
1026 let v = coerce_num(&other)?;
1027 if v == 0.0 { 1.0 } else { v.abs() }
1028 }
1029 }
1030 } else {
1031 1.0
1032 };
1033 let mode_nonzero = if args.len() == 3 {
1034 match args[2].value()?.into_literal() {
1035 LiteralValue::Error(e) => {
1036 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1037 }
1038 other => coerce_num(&other)? != 0.0,
1039 }
1040 } else {
1041 false
1042 };
1043 let result = if n >= 0.0 {
1044 (n / sig).floor() * sig
1045 } else if mode_nonzero {
1046 (n / sig).ceil() * sig
1047 } else {
1048 (n / sig).floor() * sig
1049 };
1050 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1051 result,
1052 )))
1053 }
1054}
1055
1056#[derive(Debug)]
1057pub struct SqrtFn; impl Function for SqrtFn {
1099 func_caps!(PURE);
1100 fn name(&self) -> &'static str {
1101 "SQRT"
1102 }
1103 fn min_args(&self) -> usize {
1104 1
1105 }
1106 fn arg_schema(&self) -> &'static [ArgSchema] {
1107 &ARG_NUM_LENIENT_ONE[..]
1108 }
1109 fn eval<'a, 'b, 'c>(
1110 &self,
1111 args: &'c [ArgumentHandle<'a, 'b>],
1112 _: &dyn FunctionContext<'b>,
1113 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1114 let n = match args[0].value()?.into_literal() {
1115 LiteralValue::Error(e) => {
1116 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1117 }
1118 other => coerce_num(&other)?,
1119 };
1120 if n < 0.0 {
1121 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1122 ExcelError::new_num(),
1123 )));
1124 }
1125 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1126 n.sqrt(),
1127 )))
1128 }
1129}
1130
1131#[derive(Debug)]
1132pub struct PowerFn; impl Function for PowerFn {
1173 func_caps!(PURE);
1174 fn name(&self) -> &'static str {
1175 "POWER"
1176 }
1177 fn min_args(&self) -> usize {
1178 2
1179 }
1180 fn arg_schema(&self) -> &'static [ArgSchema] {
1181 &ARG_NUM_LENIENT_TWO[..]
1182 }
1183 fn eval<'a, 'b, 'c>(
1184 &self,
1185 args: &'c [ArgumentHandle<'a, 'b>],
1186 _: &dyn FunctionContext<'b>,
1187 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1188 let base = match args[0].value()?.into_literal() {
1189 LiteralValue::Error(e) => {
1190 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1191 }
1192 other => coerce_num(&other)?,
1193 };
1194 let expv = match args[1].value()?.into_literal() {
1195 LiteralValue::Error(e) => {
1196 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1197 }
1198 other => coerce_num(&other)?,
1199 };
1200 if base < 0.0 && (expv.fract().abs() > 1e-12) {
1201 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1202 ExcelError::new_num(),
1203 )));
1204 }
1205 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1206 base.powf(expv),
1207 )))
1208 }
1209}
1210
1211#[derive(Debug)]
1212pub struct ExpFn; impl Function for ExpFn {
1255 func_caps!(PURE);
1256 fn name(&self) -> &'static str {
1257 "EXP"
1258 }
1259 fn min_args(&self) -> usize {
1260 1
1261 }
1262 fn arg_schema(&self) -> &'static [ArgSchema] {
1263 &ARG_NUM_LENIENT_ONE[..]
1264 }
1265 fn eval<'a, 'b, 'c>(
1266 &self,
1267 args: &'c [ArgumentHandle<'a, 'b>],
1268 _: &dyn FunctionContext<'b>,
1269 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1270 let n = match args[0].value()?.into_literal() {
1271 LiteralValue::Error(e) => {
1272 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1273 }
1274 other => coerce_num(&other)?,
1275 };
1276 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1277 n.exp(),
1278 )))
1279 }
1280}
1281
1282#[derive(Debug)]
1283pub struct LnFn; impl Function for LnFn {
1324 func_caps!(PURE);
1325 fn name(&self) -> &'static str {
1326 "LN"
1327 }
1328 fn min_args(&self) -> usize {
1329 1
1330 }
1331 fn arg_schema(&self) -> &'static [ArgSchema] {
1332 &ARG_NUM_LENIENT_ONE[..]
1333 }
1334 fn eval<'a, 'b, 'c>(
1335 &self,
1336 args: &'c [ArgumentHandle<'a, 'b>],
1337 _: &dyn FunctionContext<'b>,
1338 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1339 let n = match args[0].value()?.into_literal() {
1340 LiteralValue::Error(e) => {
1341 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1342 }
1343 other => coerce_num(&other)?,
1344 };
1345 if n <= 0.0 {
1346 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1347 ExcelError::new_num(),
1348 )));
1349 }
1350 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1351 n.ln(),
1352 )))
1353 }
1354}
1355
1356#[derive(Debug)]
1357pub struct LogFn; impl Function for LogFn {
1399 func_caps!(PURE);
1400 fn name(&self) -> &'static str {
1401 "LOG"
1402 }
1403 fn min_args(&self) -> usize {
1404 1
1405 }
1406 fn variadic(&self) -> bool {
1407 true
1408 }
1409 fn arg_schema(&self) -> &'static [ArgSchema] {
1410 &ARG_NUM_LENIENT_TWO[..]
1411 }
1412 fn eval<'a, 'b, 'c>(
1413 &self,
1414 args: &'c [ArgumentHandle<'a, 'b>],
1415 _: &dyn FunctionContext<'b>,
1416 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1417 if args.is_empty() || args.len() > 2 {
1418 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1419 ExcelError::new_value(),
1420 )));
1421 }
1422 let n = match args[0].value()?.into_literal() {
1423 LiteralValue::Error(e) => {
1424 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1425 }
1426 other => coerce_num(&other)?,
1427 };
1428 let base = if args.len() == 2 {
1429 match args[1].value()?.into_literal() {
1430 LiteralValue::Error(e) => {
1431 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1432 }
1433 other => coerce_num(&other)?,
1434 }
1435 } else {
1436 10.0
1437 };
1438 if n <= 0.0 || base <= 0.0 || (base - 1.0).abs() < 1e-12 {
1439 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1440 ExcelError::new_num(),
1441 )));
1442 }
1443 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1444 n.log(base),
1445 )))
1446 }
1447}
1448
1449#[derive(Debug)]
1450pub struct Log10Fn; impl Function for Log10Fn {
1491 func_caps!(PURE);
1492 fn name(&self) -> &'static str {
1493 "LOG10"
1494 }
1495 fn min_args(&self) -> usize {
1496 1
1497 }
1498 fn arg_schema(&self) -> &'static [ArgSchema] {
1499 &ARG_NUM_LENIENT_ONE[..]
1500 }
1501 fn eval<'a, 'b, 'c>(
1502 &self,
1503 args: &'c [ArgumentHandle<'a, 'b>],
1504 _: &dyn FunctionContext<'b>,
1505 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1506 let n = match args[0].value()?.into_literal() {
1507 LiteralValue::Error(e) => {
1508 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1509 }
1510 other => coerce_num(&other)?,
1511 };
1512 if n <= 0.0 {
1513 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1514 ExcelError::new_num(),
1515 )));
1516 }
1517 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1518 n.log10(),
1519 )))
1520 }
1521}
1522
1523fn factorial_checked(n: i64) -> Option<f64> {
1524 if !(0..=170).contains(&n) {
1525 return None;
1526 }
1527 let mut out = 1.0;
1528 for i in 2..=n {
1529 out *= i as f64;
1530 }
1531 Some(out)
1532}
1533
1534#[derive(Debug)]
1535pub struct QuotientFn;
1536impl Function for QuotientFn {
1576 func_caps!(PURE);
1577 fn name(&self) -> &'static str {
1578 "QUOTIENT"
1579 }
1580 fn min_args(&self) -> usize {
1581 2
1582 }
1583 fn arg_schema(&self) -> &'static [ArgSchema] {
1584 &ARG_NUM_LENIENT_TWO[..]
1585 }
1586 fn eval<'a, 'b, 'c>(
1587 &self,
1588 args: &'c [ArgumentHandle<'a, 'b>],
1589 _: &dyn FunctionContext<'b>,
1590 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1591 let n = match args[0].value()?.into_literal() {
1592 LiteralValue::Error(e) => {
1593 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1594 }
1595 other => coerce_num(&other)?,
1596 };
1597 let d = match args[1].value()?.into_literal() {
1598 LiteralValue::Error(e) => {
1599 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1600 }
1601 other => coerce_num(&other)?,
1602 };
1603 if d == 0.0 {
1604 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1605 ExcelError::new_div(),
1606 )));
1607 }
1608 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1609 (n / d).trunc(),
1610 )))
1611 }
1612}
1613
1614#[derive(Debug)]
1615pub struct EvenFn;
1616impl Function for EvenFn {
1656 func_caps!(PURE);
1657 fn name(&self) -> &'static str {
1658 "EVEN"
1659 }
1660 fn min_args(&self) -> usize {
1661 1
1662 }
1663 fn arg_schema(&self) -> &'static [ArgSchema] {
1664 &ARG_NUM_LENIENT_ONE[..]
1665 }
1666 fn eval<'a, 'b, 'c>(
1667 &self,
1668 args: &'c [ArgumentHandle<'a, 'b>],
1669 _: &dyn FunctionContext<'b>,
1670 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1671 let number = match args[0].value()?.into_literal() {
1672 LiteralValue::Error(e) => {
1673 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1674 }
1675 other => coerce_num(&other)?,
1676 };
1677 if number == 0.0 {
1678 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
1679 }
1680
1681 let sign = number.signum();
1682 let mut v = number.abs().ceil() as i64;
1683 if v % 2 != 0 {
1684 v += 1;
1685 }
1686 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1687 sign * v as f64,
1688 )))
1689 }
1690}
1691
1692#[derive(Debug)]
1693pub struct OddFn;
1694impl Function for OddFn {
1734 func_caps!(PURE);
1735 fn name(&self) -> &'static str {
1736 "ODD"
1737 }
1738 fn min_args(&self) -> usize {
1739 1
1740 }
1741 fn arg_schema(&self) -> &'static [ArgSchema] {
1742 &ARG_NUM_LENIENT_ONE[..]
1743 }
1744 fn eval<'a, 'b, 'c>(
1745 &self,
1746 args: &'c [ArgumentHandle<'a, 'b>],
1747 _: &dyn FunctionContext<'b>,
1748 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1749 let number = match args[0].value()?.into_literal() {
1750 LiteralValue::Error(e) => {
1751 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1752 }
1753 other => coerce_num(&other)?,
1754 };
1755
1756 let sign = if number < 0.0 { -1.0 } else { 1.0 };
1757 let mut v = number.abs().ceil() as i64;
1758 if v % 2 == 0 {
1759 v += 1;
1760 }
1761 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1762 sign * v as f64,
1763 )))
1764 }
1765}
1766
1767#[derive(Debug)]
1768pub struct SqrtPiFn;
1769impl Function for SqrtPiFn {
1809 func_caps!(PURE);
1810 fn name(&self) -> &'static str {
1811 "SQRTPI"
1812 }
1813 fn min_args(&self) -> usize {
1814 1
1815 }
1816 fn arg_schema(&self) -> &'static [ArgSchema] {
1817 &ARG_NUM_LENIENT_ONE[..]
1818 }
1819 fn eval<'a, 'b, 'c>(
1820 &self,
1821 args: &'c [ArgumentHandle<'a, 'b>],
1822 _: &dyn FunctionContext<'b>,
1823 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1824 let n = match args[0].value()?.into_literal() {
1825 LiteralValue::Error(e) => {
1826 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1827 }
1828 other => coerce_num(&other)?,
1829 };
1830 if n < 0.0 {
1831 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1832 ExcelError::new_num(),
1833 )));
1834 }
1835 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1836 (n * std::f64::consts::PI).sqrt(),
1837 )))
1838 }
1839}
1840
1841#[derive(Debug)]
1842pub struct MultinomialFn;
1843impl Function for MultinomialFn {
1883 func_caps!(PURE);
1884 fn name(&self) -> &'static str {
1885 "MULTINOMIAL"
1886 }
1887 fn min_args(&self) -> usize {
1888 1
1889 }
1890 fn variadic(&self) -> bool {
1891 true
1892 }
1893 fn arg_schema(&self) -> &'static [ArgSchema] {
1894 &ARG_NUM_LENIENT_ONE[..]
1895 }
1896 fn eval<'a, 'b, 'c>(
1897 &self,
1898 args: &'c [ArgumentHandle<'a, 'b>],
1899 _ctx: &dyn FunctionContext<'b>,
1900 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1901 let mut values: Vec<i64> = Vec::new();
1902 for arg in args {
1903 for value in arg.lazy_values_owned()? {
1904 let n = match value {
1905 LiteralValue::Error(e) => {
1906 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1907 }
1908 other => coerce_num(&other)?.trunc() as i64,
1909 };
1910 if n < 0 {
1911 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1912 ExcelError::new_num(),
1913 )));
1914 }
1915 values.push(n);
1916 }
1917 }
1918
1919 let sum: i64 = values.iter().sum();
1920 let num = match factorial_checked(sum) {
1921 Some(v) => v,
1922 None => {
1923 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1924 ExcelError::new_num(),
1925 )));
1926 }
1927 };
1928
1929 let mut den = 1.0;
1930 for n in values {
1931 let fact = match factorial_checked(n) {
1932 Some(v) => v,
1933 None => {
1934 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1935 ExcelError::new_num(),
1936 )));
1937 }
1938 };
1939 den *= fact;
1940 }
1941
1942 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1943 (num / den).round(),
1944 )))
1945 }
1946}
1947
1948#[derive(Debug)]
1949pub struct SeriesSumFn;
1950impl Function for SeriesSumFn {
1994 func_caps!(PURE);
1995 fn name(&self) -> &'static str {
1996 "SERIESSUM"
1997 }
1998 fn min_args(&self) -> usize {
1999 4
2000 }
2001 fn arg_schema(&self) -> &'static [ArgSchema] {
2002 use std::sync::LazyLock;
2003 static SCHEMA: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| {
2004 vec![
2005 ArgSchema::number_lenient_scalar(),
2006 ArgSchema::number_lenient_scalar(),
2007 ArgSchema::number_lenient_scalar(),
2008 ArgSchema::any(),
2009 ]
2010 });
2011 &SCHEMA[..]
2012 }
2013 fn eval<'a, 'b, 'c>(
2014 &self,
2015 args: &'c [ArgumentHandle<'a, 'b>],
2016 _ctx: &dyn FunctionContext<'b>,
2017 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2018 let x = match args[0].value()?.into_literal() {
2019 LiteralValue::Error(e) => {
2020 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2021 }
2022 other => coerce_num(&other)?,
2023 };
2024 let n = match args[1].value()?.into_literal() {
2025 LiteralValue::Error(e) => {
2026 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2027 }
2028 other => coerce_num(&other)?,
2029 };
2030 let m = match args[2].value()?.into_literal() {
2031 LiteralValue::Error(e) => {
2032 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2033 }
2034 other => coerce_num(&other)?,
2035 };
2036
2037 let mut coeffs: Vec<f64> = Vec::new();
2038 if let Ok(view) = args[3].range_view() {
2039 view.for_each_cell(&mut |cell| {
2040 match cell {
2041 LiteralValue::Error(e) => return Err(e.clone()),
2042 other => coeffs.push(coerce_num(other)?),
2043 }
2044 Ok(())
2045 })?;
2046 } else {
2047 match args[3].value()?.into_literal() {
2048 LiteralValue::Array(rows) => {
2049 for row in rows {
2050 for cell in row {
2051 match cell {
2052 LiteralValue::Error(e) => {
2053 return Ok(crate::traits::CalcValue::Scalar(
2054 LiteralValue::Error(e),
2055 ));
2056 }
2057 other => coeffs.push(coerce_num(&other)?),
2058 }
2059 }
2060 }
2061 }
2062 LiteralValue::Error(e) => {
2063 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2064 }
2065 other => coeffs.push(coerce_num(&other)?),
2066 }
2067 }
2068
2069 let mut sum = 0.0;
2070 for (i, c) in coeffs.into_iter().enumerate() {
2071 sum += c * x.powf(n + (i as f64) * m);
2072 }
2073
2074 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(sum)))
2075 }
2076}
2077
2078#[derive(Debug)]
2079pub struct SumsqFn;
2080impl Function for SumsqFn {
2124 func_caps!(PURE, REDUCTION, NUMERIC_ONLY);
2125 fn name(&self) -> &'static str {
2126 "SUMSQ"
2127 }
2128 fn min_args(&self) -> usize {
2129 1
2130 }
2131 fn variadic(&self) -> bool {
2132 true
2133 }
2134 fn arg_schema(&self) -> &'static [ArgSchema] {
2135 &ARG_RANGE_NUM_LENIENT_ONE[..]
2136 }
2137 fn eval<'a, 'b, 'c>(
2138 &self,
2139 args: &'c [ArgumentHandle<'a, 'b>],
2140 _ctx: &dyn FunctionContext<'b>,
2141 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2142 let mut total = 0.0;
2143 for arg in args {
2144 if let Ok(view) = arg.range_view() {
2145 view.for_each_cell(&mut |cell| {
2146 match cell {
2147 LiteralValue::Error(e) => return Err(e.clone()),
2148 LiteralValue::Number(n) => total += n * n,
2149 LiteralValue::Int(i) => {
2150 let n = *i as f64;
2151 total += n * n;
2152 }
2153 LiteralValue::Date(d) => {
2154 let n = crate::builtins::datetime::date_to_serial(d);
2155 total += n * n;
2156 }
2157 LiteralValue::DateTime(dt) => {
2158 let n = crate::builtins::datetime::datetime_to_serial(dt);
2159 total += n * n;
2160 }
2161 LiteralValue::Time(t) => {
2162 let n = crate::builtins::datetime::time_to_fraction(t);
2163 total += n * n;
2164 }
2165 LiteralValue::Duration(d) => {
2166 let n = d.num_seconds() as f64 / 86_400.0;
2167 total += n * n;
2168 }
2169 _ => {}
2170 }
2171 Ok(())
2172 })?;
2173 } else {
2174 let v = arg.value()?.into_literal();
2175 match v {
2176 LiteralValue::Error(e) => {
2177 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2178 }
2179 other => {
2180 let n = coerce_num(&other)?;
2181 total += n * n;
2182 }
2183 }
2184 }
2185 }
2186 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2187 total,
2188 )))
2189 }
2190}
2191
2192#[derive(Debug)]
2193pub struct MroundFn;
2194impl Function for MroundFn {
2234 func_caps!(PURE);
2235 fn name(&self) -> &'static str {
2236 "MROUND"
2237 }
2238 fn min_args(&self) -> usize {
2239 2
2240 }
2241 fn arg_schema(&self) -> &'static [ArgSchema] {
2242 &ARG_NUM_LENIENT_TWO[..]
2243 }
2244 fn eval<'a, 'b, 'c>(
2245 &self,
2246 args: &'c [ArgumentHandle<'a, 'b>],
2247 _ctx: &dyn FunctionContext<'b>,
2248 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2249 let number = match args[0].value()?.into_literal() {
2250 LiteralValue::Error(e) => {
2251 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2252 }
2253 other => coerce_num(&other)?,
2254 };
2255 let multiple = match args[1].value()?.into_literal() {
2256 LiteralValue::Error(e) => {
2257 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2258 }
2259 other => coerce_num(&other)?,
2260 };
2261
2262 if multiple == 0.0 {
2263 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2264 }
2265 if number != 0.0 && number.signum() != multiple.signum() {
2266 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2267 ExcelError::new_num(),
2268 )));
2269 }
2270
2271 let m = multiple.abs();
2272 let scaled = number.abs() / m;
2273 let rounded = (scaled + 0.5 + 1e-12).floor();
2274 let out = rounded * m * number.signum();
2275 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(out)))
2276 }
2277}
2278
2279fn roman_classic(mut n: u32) -> String {
2280 let table = [
2281 (1000, "M"),
2282 (900, "CM"),
2283 (500, "D"),
2284 (400, "CD"),
2285 (100, "C"),
2286 (90, "XC"),
2287 (50, "L"),
2288 (40, "XL"),
2289 (10, "X"),
2290 (9, "IX"),
2291 (5, "V"),
2292 (4, "IV"),
2293 (1, "I"),
2294 ];
2295
2296 let mut out = String::new();
2297 for (value, glyph) in table {
2298 while n >= value {
2299 n -= value;
2300 out.push_str(glyph);
2301 }
2302 }
2303 out
2304}
2305
2306fn roman_apply_form(classic: String, form: i64) -> String {
2307 match form {
2308 0 => classic,
2309 1 => classic
2310 .replace("CM", "LM")
2311 .replace("CD", "LD")
2312 .replace("XC", "VL")
2313 .replace("XL", "VL")
2314 .replace("IX", "IV"),
2315 2 => roman_apply_form(classic, 1)
2316 .replace("LD", "XD")
2317 .replace("LM", "XM")
2318 .replace("VLIV", "IX"),
2319 3 => roman_apply_form(classic, 2)
2320 .replace("XD", "VD")
2321 .replace("XM", "VM")
2322 .replace("IX", "IV"),
2323 4 => roman_apply_form(classic, 3)
2324 .replace("VDIV", "ID")
2325 .replace("VMIV", "IM"),
2326 _ => classic,
2327 }
2328}
2329
2330#[derive(Debug)]
2331pub struct RomanFn;
2332impl Function for RomanFn {
2372 func_caps!(PURE);
2373 fn name(&self) -> &'static str {
2374 "ROMAN"
2375 }
2376 fn min_args(&self) -> usize {
2377 1
2378 }
2379 fn variadic(&self) -> bool {
2380 true
2381 }
2382 fn arg_schema(&self) -> &'static [ArgSchema] {
2383 &ARG_NUM_LENIENT_TWO[..]
2384 }
2385 fn eval<'a, 'b, 'c>(
2386 &self,
2387 args: &'c [ArgumentHandle<'a, 'b>],
2388 _ctx: &dyn FunctionContext<'b>,
2389 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2390 if args.len() > 2 {
2391 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2392 ExcelError::new_value(),
2393 )));
2394 }
2395
2396 let number = match args[0].value()?.into_literal() {
2397 LiteralValue::Error(e) => {
2398 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2399 }
2400 other => coerce_num(&other)?.trunc() as i64,
2401 };
2402
2403 if !(0..=3999).contains(&number) {
2404 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2405 ExcelError::new_value(),
2406 )));
2407 }
2408 if number == 0 {
2409 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
2410 "".to_string(),
2411 )));
2412 }
2413
2414 let form = if args.len() >= 2 {
2415 match args[1].value()?.into_literal() {
2416 LiteralValue::Boolean(b) => {
2417 if b {
2418 0
2419 } else {
2420 4
2421 }
2422 }
2423 LiteralValue::Number(n) => n.trunc() as i64,
2424 LiteralValue::Int(i) => i,
2425 LiteralValue::Empty => 0,
2426 LiteralValue::Error(e) => {
2427 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2428 }
2429 _ => {
2430 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2431 ExcelError::new_value(),
2432 )));
2433 }
2434 }
2435 } else {
2436 0
2437 };
2438
2439 if !(0..=4).contains(&form) {
2440 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2441 ExcelError::new_value(),
2442 )));
2443 }
2444
2445 let classic = roman_classic(number as u32);
2446 let text = roman_apply_form(classic, form);
2447 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(text)))
2448 }
2449}
2450
2451fn roman_digit_value(ch: char) -> Option<i64> {
2452 match ch {
2453 'I' => Some(1),
2454 'V' => Some(5),
2455 'X' => Some(10),
2456 'L' => Some(50),
2457 'C' => Some(100),
2458 'D' => Some(500),
2459 'M' => Some(1000),
2460 _ => None,
2461 }
2462}
2463
2464#[derive(Debug)]
2465pub struct ArabicFn;
2466impl Function for ArabicFn {
2506 func_caps!(PURE);
2507 fn name(&self) -> &'static str {
2508 "ARABIC"
2509 }
2510 fn min_args(&self) -> usize {
2511 1
2512 }
2513 fn arg_schema(&self) -> &'static [ArgSchema] {
2514 use std::sync::LazyLock;
2515 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
2516 &ONE[..]
2517 }
2518 fn eval<'a, 'b, 'c>(
2519 &self,
2520 args: &'c [ArgumentHandle<'a, 'b>],
2521 _ctx: &dyn FunctionContext<'b>,
2522 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2523 let raw = match args[0].value()?.into_literal() {
2524 LiteralValue::Text(s) => s,
2525 LiteralValue::Empty => String::new(),
2526 LiteralValue::Error(e) => {
2527 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2528 }
2529 _ => {
2530 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2531 ExcelError::new_value(),
2532 )));
2533 }
2534 };
2535
2536 let mut text = raw.trim().to_uppercase();
2537 if text.len() > 255 {
2538 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2539 ExcelError::new_value(),
2540 )));
2541 }
2542 if text.is_empty() {
2543 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2544 }
2545
2546 let sign = if text.starts_with('-') {
2547 text.remove(0);
2548 -1.0
2549 } else {
2550 1.0
2551 };
2552
2553 if text.is_empty() {
2554 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2555 ExcelError::new_value(),
2556 )));
2557 }
2558
2559 let mut total = 0i64;
2560 let mut prev = 0i64;
2561 for ch in text.chars().rev() {
2562 let v = match roman_digit_value(ch) {
2563 Some(v) => v,
2564 None => {
2565 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2566 ExcelError::new_value(),
2567 )));
2568 }
2569 };
2570 if v < prev {
2571 total -= v;
2572 } else {
2573 total += v;
2574 prev = v;
2575 }
2576 }
2577
2578 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2579 sign * total as f64,
2580 )))
2581 }
2582}
2583
2584pub fn register_builtins() {
2585 use std::sync::Arc;
2586 crate::function_registry::register_function(Arc::new(AbsFn));
2587 crate::function_registry::register_function(Arc::new(SignFn));
2588 crate::function_registry::register_function(Arc::new(IntFn));
2589 crate::function_registry::register_function(Arc::new(TruncFn));
2590 crate::function_registry::register_function(Arc::new(RoundFn));
2591 crate::function_registry::register_function(Arc::new(RoundDownFn));
2592 crate::function_registry::register_function(Arc::new(RoundUpFn));
2593 crate::function_registry::register_function(Arc::new(ModFn));
2594 crate::function_registry::register_function(Arc::new(CeilingFn));
2595 crate::function_registry::register_function(Arc::new(CeilingMathFn));
2596 crate::function_registry::register_function(Arc::new(FloorFn));
2597 crate::function_registry::register_function(Arc::new(FloorMathFn));
2598 crate::function_registry::register_function(Arc::new(SqrtFn));
2599 crate::function_registry::register_function(Arc::new(PowerFn));
2600 crate::function_registry::register_function(Arc::new(ExpFn));
2601 crate::function_registry::register_function(Arc::new(LnFn));
2602 crate::function_registry::register_function(Arc::new(LogFn));
2603 crate::function_registry::register_function(Arc::new(Log10Fn));
2604 crate::function_registry::register_function(Arc::new(QuotientFn));
2605 crate::function_registry::register_function(Arc::new(EvenFn));
2606 crate::function_registry::register_function(Arc::new(OddFn));
2607 crate::function_registry::register_function(Arc::new(SqrtPiFn));
2608 crate::function_registry::register_function(Arc::new(MultinomialFn));
2609 crate::function_registry::register_function(Arc::new(SeriesSumFn));
2610 crate::function_registry::register_function(Arc::new(SumsqFn));
2611 crate::function_registry::register_function(Arc::new(MroundFn));
2612 crate::function_registry::register_function(Arc::new(RomanFn));
2613 crate::function_registry::register_function(Arc::new(ArabicFn));
2614}
2615
2616#[cfg(test)]
2617mod tests_numeric {
2618 use super::*;
2619 use crate::test_workbook::TestWorkbook;
2620 use crate::traits::ArgumentHandle;
2621 use formualizer_common::LiteralValue;
2622 use formualizer_parse::parser::{ASTNode, ASTNodeType};
2623
2624 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
2625 wb.interpreter()
2626 }
2627 fn lit(v: LiteralValue) -> ASTNode {
2628 ASTNode::new(ASTNodeType::Literal(v), None)
2629 }
2630
2631 #[test]
2633 fn abs_basic() {
2634 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
2635 let ctx = interp(&wb);
2636 let n = lit(LiteralValue::Number(-5.5));
2637 let f = ctx.context.get_function("", "ABS").unwrap();
2638 assert_eq!(
2639 f.dispatch(
2640 &[ArgumentHandle::new(&n, &ctx)],
2641 &ctx.function_context(None)
2642 )
2643 .unwrap()
2644 .into_literal(),
2645 LiteralValue::Number(5.5)
2646 );
2647 }
2648 #[test]
2649 fn abs_error_passthrough() {
2650 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
2651 let ctx = interp(&wb);
2652 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
2653 "#VALUE!",
2654 )));
2655 let f = ctx.context.get_function("", "ABS").unwrap();
2656 match f
2657 .dispatch(
2658 &[ArgumentHandle::new(&e, &ctx)],
2659 &ctx.function_context(None),
2660 )
2661 .unwrap()
2662 .into_literal()
2663 {
2664 LiteralValue::Error(er) => assert_eq!(er, "#VALUE!"),
2665 _ => panic!(),
2666 }
2667 }
2668
2669 #[test]
2671 fn sign_neg_zero_pos() {
2672 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
2673 let ctx = interp(&wb);
2674 let f = ctx.context.get_function("", "SIGN").unwrap();
2675 let neg = lit(LiteralValue::Number(-3.2));
2676 let zero = lit(LiteralValue::Int(0));
2677 let pos = lit(LiteralValue::Int(9));
2678 assert_eq!(
2679 f.dispatch(
2680 &[ArgumentHandle::new(&neg, &ctx)],
2681 &ctx.function_context(None)
2682 )
2683 .unwrap()
2684 .into_literal(),
2685 LiteralValue::Number(-1.0)
2686 );
2687 assert_eq!(
2688 f.dispatch(
2689 &[ArgumentHandle::new(&zero, &ctx)],
2690 &ctx.function_context(None)
2691 )
2692 .unwrap()
2693 .into_literal(),
2694 LiteralValue::Number(0.0)
2695 );
2696 assert_eq!(
2697 f.dispatch(
2698 &[ArgumentHandle::new(&pos, &ctx)],
2699 &ctx.function_context(None)
2700 )
2701 .unwrap()
2702 .into_literal(),
2703 LiteralValue::Number(1.0)
2704 );
2705 }
2706 #[test]
2707 fn sign_error_passthrough() {
2708 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
2709 let ctx = interp(&wb);
2710 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
2711 "#DIV/0!",
2712 )));
2713 let f = ctx.context.get_function("", "SIGN").unwrap();
2714 match f
2715 .dispatch(
2716 &[ArgumentHandle::new(&e, &ctx)],
2717 &ctx.function_context(None),
2718 )
2719 .unwrap()
2720 .into_literal()
2721 {
2722 LiteralValue::Error(er) => assert_eq!(er, "#DIV/0!"),
2723 _ => panic!(),
2724 }
2725 }
2726
2727 #[test]
2729 fn int_floor_negative() {
2730 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
2731 let ctx = interp(&wb);
2732 let f = ctx.context.get_function("", "INT").unwrap();
2733 let n = lit(LiteralValue::Number(-3.2));
2734 assert_eq!(
2735 f.dispatch(
2736 &[ArgumentHandle::new(&n, &ctx)],
2737 &ctx.function_context(None)
2738 )
2739 .unwrap()
2740 .into_literal(),
2741 LiteralValue::Number(-4.0)
2742 );
2743 }
2744 #[test]
2745 fn int_floor_positive() {
2746 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
2747 let ctx = interp(&wb);
2748 let f = ctx.context.get_function("", "INT").unwrap();
2749 let n = lit(LiteralValue::Number(3.7));
2750 assert_eq!(
2751 f.dispatch(
2752 &[ArgumentHandle::new(&n, &ctx)],
2753 &ctx.function_context(None)
2754 )
2755 .unwrap()
2756 .into_literal(),
2757 LiteralValue::Number(3.0)
2758 );
2759 }
2760
2761 #[test]
2763 fn trunc_digits_positive_and_negative() {
2764 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
2765 let ctx = interp(&wb);
2766 let f = ctx.context.get_function("", "TRUNC").unwrap();
2767 let n = lit(LiteralValue::Number(12.3456));
2768 let d2 = lit(LiteralValue::Int(2));
2769 let dneg1 = lit(LiteralValue::Int(-1));
2770 assert_eq!(
2771 f.dispatch(
2772 &[
2773 ArgumentHandle::new(&n, &ctx),
2774 ArgumentHandle::new(&d2, &ctx)
2775 ],
2776 &ctx.function_context(None)
2777 )
2778 .unwrap()
2779 .into_literal(),
2780 LiteralValue::Number(12.34)
2781 );
2782 assert_eq!(
2783 f.dispatch(
2784 &[
2785 ArgumentHandle::new(&n, &ctx),
2786 ArgumentHandle::new(&dneg1, &ctx)
2787 ],
2788 &ctx.function_context(None)
2789 )
2790 .unwrap()
2791 .into_literal(),
2792 LiteralValue::Number(10.0)
2793 );
2794 }
2795 #[test]
2796 fn trunc_default_zero_digits() {
2797 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
2798 let ctx = interp(&wb);
2799 let f = ctx.context.get_function("", "TRUNC").unwrap();
2800 let n = lit(LiteralValue::Number(-12.999));
2801 assert_eq!(
2802 f.dispatch(
2803 &[ArgumentHandle::new(&n, &ctx)],
2804 &ctx.function_context(None)
2805 )
2806 .unwrap()
2807 .into_literal(),
2808 LiteralValue::Number(-12.0)
2809 );
2810 }
2811
2812 #[test]
2814 fn round_half_away_positive_and_negative() {
2815 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
2816 let ctx = interp(&wb);
2817 let f = ctx.context.get_function("", "ROUND").unwrap();
2818 let p = lit(LiteralValue::Number(2.5));
2819 let n = lit(LiteralValue::Number(-2.5));
2820 let d0 = lit(LiteralValue::Int(0));
2821 assert_eq!(
2822 f.dispatch(
2823 &[
2824 ArgumentHandle::new(&p, &ctx),
2825 ArgumentHandle::new(&d0, &ctx)
2826 ],
2827 &ctx.function_context(None)
2828 )
2829 .unwrap()
2830 .into_literal(),
2831 LiteralValue::Number(3.0)
2832 );
2833 assert_eq!(
2834 f.dispatch(
2835 &[
2836 ArgumentHandle::new(&n, &ctx),
2837 ArgumentHandle::new(&d0, &ctx)
2838 ],
2839 &ctx.function_context(None)
2840 )
2841 .unwrap()
2842 .into_literal(),
2843 LiteralValue::Number(-3.0)
2844 );
2845 }
2846 #[test]
2847 fn round_digits_positive() {
2848 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
2849 let ctx = interp(&wb);
2850 let f = ctx.context.get_function("", "ROUND").unwrap();
2851 let n = lit(LiteralValue::Number(1.2345));
2852 let d = lit(LiteralValue::Int(3));
2853 assert_eq!(
2854 f.dispatch(
2855 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
2856 &ctx.function_context(None)
2857 )
2858 .unwrap()
2859 .into_literal(),
2860 LiteralValue::Number(1.235)
2861 );
2862 }
2863
2864 #[test]
2866 fn rounddown_truncates() {
2867 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
2868 let ctx = interp(&wb);
2869 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
2870 let n = lit(LiteralValue::Number(1.299));
2871 let d = lit(LiteralValue::Int(2));
2872 assert_eq!(
2873 f.dispatch(
2874 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
2875 &ctx.function_context(None)
2876 )
2877 .unwrap()
2878 .into_literal(),
2879 LiteralValue::Number(1.29)
2880 );
2881 }
2882 #[test]
2883 fn rounddown_negative_number() {
2884 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
2885 let ctx = interp(&wb);
2886 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
2887 let n = lit(LiteralValue::Number(-1.299));
2888 let d = lit(LiteralValue::Int(2));
2889 assert_eq!(
2890 f.dispatch(
2891 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
2892 &ctx.function_context(None)
2893 )
2894 .unwrap()
2895 .into_literal(),
2896 LiteralValue::Number(-1.29)
2897 );
2898 }
2899
2900 #[test]
2902 fn roundup_away_from_zero() {
2903 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
2904 let ctx = interp(&wb);
2905 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
2906 let n = lit(LiteralValue::Number(1.001));
2907 let d = lit(LiteralValue::Int(2));
2908 assert_eq!(
2909 f.dispatch(
2910 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
2911 &ctx.function_context(None)
2912 )
2913 .unwrap()
2914 .into_literal(),
2915 LiteralValue::Number(1.01)
2916 );
2917 }
2918 #[test]
2919 fn roundup_negative() {
2920 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
2921 let ctx = interp(&wb);
2922 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
2923 let n = lit(LiteralValue::Number(-1.001));
2924 let d = lit(LiteralValue::Int(2));
2925 assert_eq!(
2926 f.dispatch(
2927 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
2928 &ctx.function_context(None)
2929 )
2930 .unwrap()
2931 .into_literal(),
2932 LiteralValue::Number(-1.01)
2933 );
2934 }
2935
2936 #[test]
2938 fn mod_positive_negative_cases() {
2939 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
2940 let ctx = interp(&wb);
2941 let f = ctx.context.get_function("", "MOD").unwrap();
2942 let a = lit(LiteralValue::Int(-3));
2943 let b = lit(LiteralValue::Int(2));
2944 let out = f
2945 .dispatch(
2946 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
2947 &ctx.function_context(None),
2948 )
2949 .unwrap();
2950 assert_eq!(out, LiteralValue::Number(1.0));
2951 let a2 = lit(LiteralValue::Int(3));
2952 let b2 = lit(LiteralValue::Int(-2));
2953 let out2 = f
2954 .dispatch(
2955 &[
2956 ArgumentHandle::new(&a2, &ctx),
2957 ArgumentHandle::new(&b2, &ctx),
2958 ],
2959 &ctx.function_context(None),
2960 )
2961 .unwrap();
2962 assert_eq!(out2, LiteralValue::Number(-1.0));
2963 }
2964 #[test]
2965 fn mod_div_by_zero_error() {
2966 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
2967 let ctx = interp(&wb);
2968 let f = ctx.context.get_function("", "MOD").unwrap();
2969 let a = lit(LiteralValue::Int(5));
2970 let zero = lit(LiteralValue::Int(0));
2971 match f
2972 .dispatch(
2973 &[
2974 ArgumentHandle::new(&a, &ctx),
2975 ArgumentHandle::new(&zero, &ctx),
2976 ],
2977 &ctx.function_context(None),
2978 )
2979 .unwrap()
2980 .into_literal()
2981 {
2982 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
2983 _ => panic!(),
2984 }
2985 }
2986
2987 #[test]
2989 fn sqrt_basic_and_domain() {
2990 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SqrtFn));
2991 let ctx = interp(&wb);
2992 let f = ctx.context.get_function("", "SQRT").unwrap();
2993 let n = lit(LiteralValue::Number(9.0));
2994 let out = f
2995 .dispatch(
2996 &[ArgumentHandle::new(&n, &ctx)],
2997 &ctx.function_context(None),
2998 )
2999 .unwrap();
3000 assert_eq!(out, LiteralValue::Number(3.0));
3001 let neg = lit(LiteralValue::Number(-1.0));
3002 let out2 = f
3003 .dispatch(
3004 &[ArgumentHandle::new(&neg, &ctx)],
3005 &ctx.function_context(None),
3006 )
3007 .unwrap();
3008 assert!(matches!(out2.into_literal(), LiteralValue::Error(_)));
3009 }
3010
3011 #[test]
3012 fn power_fractional_negative_domain() {
3013 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PowerFn));
3014 let ctx = interp(&wb);
3015 let f = ctx.context.get_function("", "POWER").unwrap();
3016 let a = lit(LiteralValue::Number(-4.0));
3017 let half = lit(LiteralValue::Number(0.5));
3018 let out = f
3019 .dispatch(
3020 &[
3021 ArgumentHandle::new(&a, &ctx),
3022 ArgumentHandle::new(&half, &ctx),
3023 ],
3024 &ctx.function_context(None),
3025 )
3026 .unwrap();
3027 assert!(matches!(out.into_literal(), LiteralValue::Error(_))); }
3029
3030 #[test]
3031 fn log_variants() {
3032 let wb = TestWorkbook::new()
3033 .with_function(std::sync::Arc::new(LogFn))
3034 .with_function(std::sync::Arc::new(Log10Fn))
3035 .with_function(std::sync::Arc::new(LnFn));
3036 let ctx = interp(&wb);
3037 let logf = ctx.context.get_function("", "LOG").unwrap();
3038 let log10f = ctx.context.get_function("", "LOG10").unwrap();
3039 let lnf = ctx.context.get_function("", "LN").unwrap();
3040 let n = lit(LiteralValue::Number(100.0));
3041 let base = lit(LiteralValue::Number(10.0));
3042 assert_eq!(
3043 logf.dispatch(
3044 &[
3045 ArgumentHandle::new(&n, &ctx),
3046 ArgumentHandle::new(&base, &ctx)
3047 ],
3048 &ctx.function_context(None)
3049 )
3050 .unwrap()
3051 .into_literal(),
3052 LiteralValue::Number(2.0)
3053 );
3054 assert_eq!(
3055 log10f
3056 .dispatch(
3057 &[ArgumentHandle::new(&n, &ctx)],
3058 &ctx.function_context(None)
3059 )
3060 .unwrap()
3061 .into_literal(),
3062 LiteralValue::Number(2.0)
3063 );
3064 assert_eq!(
3065 lnf.dispatch(
3066 &[ArgumentHandle::new(&n, &ctx)],
3067 &ctx.function_context(None)
3068 )
3069 .unwrap()
3070 .into_literal(),
3071 LiteralValue::Number(100.0f64.ln())
3072 );
3073 }
3074 #[test]
3075 fn ceiling_floor_basic() {
3076 let wb = TestWorkbook::new()
3077 .with_function(std::sync::Arc::new(CeilingFn))
3078 .with_function(std::sync::Arc::new(FloorFn))
3079 .with_function(std::sync::Arc::new(CeilingMathFn))
3080 .with_function(std::sync::Arc::new(FloorMathFn));
3081 let ctx = interp(&wb);
3082 let c = ctx.context.get_function("", "CEILING").unwrap();
3083 let f = ctx.context.get_function("", "FLOOR").unwrap();
3084 let n = lit(LiteralValue::Number(5.1));
3085 let sig = lit(LiteralValue::Number(2.0));
3086 assert_eq!(
3087 c.dispatch(
3088 &[
3089 ArgumentHandle::new(&n, &ctx),
3090 ArgumentHandle::new(&sig, &ctx)
3091 ],
3092 &ctx.function_context(None)
3093 )
3094 .unwrap()
3095 .into_literal(),
3096 LiteralValue::Number(6.0)
3097 );
3098 assert_eq!(
3099 f.dispatch(
3100 &[
3101 ArgumentHandle::new(&n, &ctx),
3102 ArgumentHandle::new(&sig, &ctx)
3103 ],
3104 &ctx.function_context(None)
3105 )
3106 .unwrap()
3107 .into_literal(),
3108 LiteralValue::Number(4.0)
3109 );
3110 }
3111
3112 #[test]
3113 fn quotient_basic_and_div_zero() {
3114 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(QuotientFn));
3115 let ctx = interp(&wb);
3116 let f = ctx.context.get_function("", "QUOTIENT").unwrap();
3117
3118 let ten = lit(LiteralValue::Int(10));
3119 let three = lit(LiteralValue::Int(3));
3120 assert_eq!(
3121 f.dispatch(
3122 &[
3123 ArgumentHandle::new(&ten, &ctx),
3124 ArgumentHandle::new(&three, &ctx),
3125 ],
3126 &ctx.function_context(None),
3127 )
3128 .unwrap()
3129 .into_literal(),
3130 LiteralValue::Number(3.0)
3131 );
3132
3133 let neg_ten = lit(LiteralValue::Int(-10));
3134 assert_eq!(
3135 f.dispatch(
3136 &[
3137 ArgumentHandle::new(&neg_ten, &ctx),
3138 ArgumentHandle::new(&three, &ctx),
3139 ],
3140 &ctx.function_context(None),
3141 )
3142 .unwrap()
3143 .into_literal(),
3144 LiteralValue::Number(-3.0)
3145 );
3146
3147 let zero = lit(LiteralValue::Int(0));
3148 match f
3149 .dispatch(
3150 &[
3151 ArgumentHandle::new(&ten, &ctx),
3152 ArgumentHandle::new(&zero, &ctx),
3153 ],
3154 &ctx.function_context(None),
3155 )
3156 .unwrap()
3157 .into_literal()
3158 {
3159 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
3160 other => panic!("expected #DIV/0!, got {other:?}"),
3161 }
3162 }
3163
3164 #[test]
3165 fn even_odd_examples() {
3166 let wb = TestWorkbook::new()
3167 .with_function(std::sync::Arc::new(EvenFn))
3168 .with_function(std::sync::Arc::new(OddFn));
3169 let ctx = interp(&wb);
3170
3171 let even = ctx.context.get_function("", "EVEN").unwrap();
3172 let odd = ctx.context.get_function("", "ODD").unwrap();
3173
3174 let one_half = lit(LiteralValue::Number(1.5));
3175 let three = lit(LiteralValue::Int(3));
3176 let neg_one = lit(LiteralValue::Int(-1));
3177 let two = lit(LiteralValue::Int(2));
3178 let zero = lit(LiteralValue::Int(0));
3179
3180 assert_eq!(
3181 even.dispatch(
3182 &[ArgumentHandle::new(&one_half, &ctx)],
3183 &ctx.function_context(None),
3184 )
3185 .unwrap()
3186 .into_literal(),
3187 LiteralValue::Number(2.0)
3188 );
3189 assert_eq!(
3190 even.dispatch(
3191 &[ArgumentHandle::new(&three, &ctx)],
3192 &ctx.function_context(None),
3193 )
3194 .unwrap()
3195 .into_literal(),
3196 LiteralValue::Number(4.0)
3197 );
3198 assert_eq!(
3199 even.dispatch(
3200 &[ArgumentHandle::new(&neg_one, &ctx)],
3201 &ctx.function_context(None),
3202 )
3203 .unwrap()
3204 .into_literal(),
3205 LiteralValue::Number(-2.0)
3206 );
3207 assert_eq!(
3208 even.dispatch(
3209 &[ArgumentHandle::new(&two, &ctx)],
3210 &ctx.function_context(None),
3211 )
3212 .unwrap()
3213 .into_literal(),
3214 LiteralValue::Number(2.0)
3215 );
3216
3217 assert_eq!(
3218 odd.dispatch(
3219 &[ArgumentHandle::new(&one_half, &ctx)],
3220 &ctx.function_context(None),
3221 )
3222 .unwrap()
3223 .into_literal(),
3224 LiteralValue::Number(3.0)
3225 );
3226 assert_eq!(
3227 odd.dispatch(
3228 &[ArgumentHandle::new(&two, &ctx)],
3229 &ctx.function_context(None),
3230 )
3231 .unwrap()
3232 .into_literal(),
3233 LiteralValue::Number(3.0)
3234 );
3235 assert_eq!(
3236 odd.dispatch(
3237 &[ArgumentHandle::new(&neg_one, &ctx)],
3238 &ctx.function_context(None),
3239 )
3240 .unwrap()
3241 .into_literal(),
3242 LiteralValue::Number(-1.0)
3243 );
3244 assert_eq!(
3245 odd.dispatch(
3246 &[ArgumentHandle::new(&zero, &ctx)],
3247 &ctx.function_context(None),
3248 )
3249 .unwrap()
3250 .into_literal(),
3251 LiteralValue::Number(1.0)
3252 );
3253 }
3254
3255 #[test]
3256 fn sqrtpi_multinomial_and_seriessum_examples() {
3257 let wb = TestWorkbook::new()
3258 .with_function(std::sync::Arc::new(SqrtPiFn))
3259 .with_function(std::sync::Arc::new(MultinomialFn))
3260 .with_function(std::sync::Arc::new(SeriesSumFn));
3261 let ctx = interp(&wb);
3262
3263 let sqrtpi = ctx.context.get_function("", "SQRTPI").unwrap();
3264 let one = lit(LiteralValue::Int(1));
3265 match sqrtpi
3266 .dispatch(
3267 &[ArgumentHandle::new(&one, &ctx)],
3268 &ctx.function_context(None),
3269 )
3270 .unwrap()
3271 .into_literal()
3272 {
3273 LiteralValue::Number(v) => assert!((v - std::f64::consts::PI.sqrt()).abs() < 1e-12),
3274 other => panic!("expected numeric SQRTPI, got {other:?}"),
3275 }
3276
3277 let multinomial = ctx.context.get_function("", "MULTINOMIAL").unwrap();
3278 let two = lit(LiteralValue::Int(2));
3279 let three = lit(LiteralValue::Int(3));
3280 let four = lit(LiteralValue::Int(4));
3281 assert_eq!(
3282 multinomial
3283 .dispatch(
3284 &[
3285 ArgumentHandle::new(&two, &ctx),
3286 ArgumentHandle::new(&three, &ctx),
3287 ArgumentHandle::new(&four, &ctx),
3288 ],
3289 &ctx.function_context(None),
3290 )
3291 .unwrap()
3292 .into_literal(),
3293 LiteralValue::Number(1260.0)
3294 );
3295
3296 let seriessum = ctx.context.get_function("", "SERIESSUM").unwrap();
3297 let x = lit(LiteralValue::Int(2));
3298 let n0 = lit(LiteralValue::Int(0));
3299 let m1 = lit(LiteralValue::Int(1));
3300 let coeffs = ASTNode::new(
3301 ASTNodeType::Literal(LiteralValue::Array(vec![vec![
3302 LiteralValue::Int(1),
3303 LiteralValue::Int(2),
3304 LiteralValue::Int(3),
3305 ]])),
3306 None,
3307 );
3308 assert_eq!(
3309 seriessum
3310 .dispatch(
3311 &[
3312 ArgumentHandle::new(&x, &ctx),
3313 ArgumentHandle::new(&n0, &ctx),
3314 ArgumentHandle::new(&m1, &ctx),
3315 ArgumentHandle::new(&coeffs, &ctx),
3316 ],
3317 &ctx.function_context(None),
3318 )
3319 .unwrap()
3320 .into_literal(),
3321 LiteralValue::Number(17.0)
3322 );
3323 }
3324
3325 #[test]
3326 fn sumsq_basic() {
3327 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumsqFn));
3328 let ctx = interp(&wb);
3329 let f = ctx.context.get_function("", "SUMSQ").unwrap();
3330 let a = lit(LiteralValue::Int(3));
3331 let b = lit(LiteralValue::Int(4));
3332 assert_eq!(
3333 f.dispatch(
3334 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
3335 &ctx.function_context(None)
3336 )
3337 .unwrap()
3338 .into_literal(),
3339 LiteralValue::Number(25.0)
3340 );
3341 }
3342
3343 #[test]
3344 fn mround_sign_and_midpoint() {
3345 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(MroundFn));
3346 let ctx = interp(&wb);
3347 let f = ctx.context.get_function("", "MROUND").unwrap();
3348
3349 let n = lit(LiteralValue::Number(1.3));
3350 let m = lit(LiteralValue::Number(0.2));
3351 match f
3352 .dispatch(
3353 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&m, &ctx)],
3354 &ctx.function_context(None),
3355 )
3356 .unwrap()
3357 .into_literal()
3358 {
3359 LiteralValue::Number(v) => assert!((v - 1.4).abs() < 1e-12),
3360 other => panic!("expected numeric result, got {other:?}"),
3361 }
3362
3363 let bad_m = lit(LiteralValue::Number(-2.0));
3364 let five = lit(LiteralValue::Number(5.0));
3365 match f
3366 .dispatch(
3367 &[
3368 ArgumentHandle::new(&five, &ctx),
3369 ArgumentHandle::new(&bad_m, &ctx),
3370 ],
3371 &ctx.function_context(None),
3372 )
3373 .unwrap()
3374 .into_literal()
3375 {
3376 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
3377 other => panic!("expected #NUM!, got {other:?}"),
3378 }
3379 }
3380
3381 #[test]
3382 fn roman_and_arabic_examples() {
3383 let wb = TestWorkbook::new()
3384 .with_function(std::sync::Arc::new(RomanFn))
3385 .with_function(std::sync::Arc::new(ArabicFn));
3386 let ctx = interp(&wb);
3387
3388 let roman = ctx.context.get_function("", "ROMAN").unwrap();
3389 let n499 = lit(LiteralValue::Int(499));
3390 let out = roman
3391 .dispatch(
3392 &[ArgumentHandle::new(&n499, &ctx)],
3393 &ctx.function_context(None),
3394 )
3395 .unwrap()
3396 .into_literal();
3397 assert_eq!(out, LiteralValue::Text("CDXCIX".to_string()));
3398
3399 let form4 = lit(LiteralValue::Int(4));
3400 let out_form4 = roman
3401 .dispatch(
3402 &[
3403 ArgumentHandle::new(&n499, &ctx),
3404 ArgumentHandle::new(&form4, &ctx),
3405 ],
3406 &ctx.function_context(None),
3407 )
3408 .unwrap()
3409 .into_literal();
3410 assert_eq!(out_form4, LiteralValue::Text("ID".to_string()));
3411
3412 let arabic = ctx.context.get_function("", "ARABIC").unwrap();
3413 let roman_text = lit(LiteralValue::Text("CDXCIX".to_string()));
3414 let out_arabic = arabic
3415 .dispatch(
3416 &[ArgumentHandle::new(&roman_text, &ctx)],
3417 &ctx.function_context(None),
3418 )
3419 .unwrap()
3420 .into_literal();
3421 assert_eq!(out_arabic, LiteralValue::Number(499.0));
3422 }
3423}