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
2584#[derive(Debug)]
2587pub struct BaseFn;
2588impl Function for BaseFn {
2621 func_caps!(PURE);
2622 fn name(&self) -> &'static str {
2623 "BASE"
2624 }
2625 fn min_args(&self) -> usize {
2626 2
2627 }
2628 fn variadic(&self) -> bool {
2629 true
2630 }
2631 fn arg_schema(&self) -> &'static [ArgSchema] {
2632 use std::sync::LazyLock;
2633 static THREE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| {
2634 vec![
2635 ArgSchema::number_lenient_scalar(),
2636 ArgSchema::number_lenient_scalar(),
2637 ArgSchema::number_lenient_scalar(),
2638 ]
2639 });
2640 &THREE[..]
2641 }
2642 fn eval<'a, 'b, 'c>(
2643 &self,
2644 args: &'c [ArgumentHandle<'a, 'b>],
2645 _: &dyn FunctionContext<'b>,
2646 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2647 if args.len() < 2 || args.len() > 3 {
2648 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2649 ExcelError::new_value(),
2650 )));
2651 }
2652 let number = match args[0].value()?.into_literal() {
2653 LiteralValue::Error(e) => {
2654 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2655 }
2656 other => coerce_num(&other)?.trunc() as i64,
2657 };
2658 let radix = match args[1].value()?.into_literal() {
2659 LiteralValue::Error(e) => {
2660 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2661 }
2662 other => coerce_num(&other)?.trunc() as i64,
2663 };
2664 let min_len = if args.len() == 3 {
2665 match args[2].value()?.into_literal() {
2666 LiteralValue::Error(e) => {
2667 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2668 }
2669 other => coerce_num(&other)?.trunc() as usize,
2670 }
2671 } else {
2672 0
2673 };
2674 if !(2..=36).contains(&radix) || number < 0 {
2675 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2676 ExcelError::new_num(),
2677 )));
2678 }
2679 let mut digits = Vec::new();
2680 let mut n = number as u64;
2681 if n == 0 {
2682 digits.push('0');
2683 } else {
2684 while n > 0 {
2685 let d = (n % radix as u64) as u32;
2686 digits.push(
2687 char::from_digit(d, radix as u32)
2688 .unwrap()
2689 .to_ascii_uppercase(),
2690 );
2691 n /= radix as u64;
2692 }
2693 digits.reverse();
2694 }
2695 while digits.len() < min_len {
2696 digits.insert(0, '0');
2697 }
2698 let text: String = digits.into_iter().collect();
2699 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(text)))
2700 }
2701}
2702
2703#[derive(Debug)]
2704pub struct DecimalFn;
2705impl Function for DecimalFn {
2738 func_caps!(PURE);
2739 fn name(&self) -> &'static str {
2740 "DECIMAL"
2741 }
2742 fn min_args(&self) -> usize {
2743 2
2744 }
2745 fn arg_schema(&self) -> &'static [ArgSchema] {
2746 use std::sync::LazyLock;
2747 static SCHEMA: LazyLock<Vec<ArgSchema>> =
2748 LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::number_lenient_scalar()]);
2749 &SCHEMA[..]
2750 }
2751 fn eval<'a, 'b, 'c>(
2752 &self,
2753 args: &'c [ArgumentHandle<'a, 'b>],
2754 _: &dyn FunctionContext<'b>,
2755 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2756 if args.len() != 2 {
2757 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2758 ExcelError::new_value(),
2759 )));
2760 }
2761 let text = match args[0].value()?.into_literal() {
2762 LiteralValue::Text(s) => s,
2763 LiteralValue::Number(n) => format!("{}", n.trunc() as i64),
2764 LiteralValue::Int(i) => i.to_string(),
2765 LiteralValue::Error(e) => {
2766 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2767 }
2768 _ => {
2769 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2770 ExcelError::new_value(),
2771 )));
2772 }
2773 };
2774 let radix = match args[1].value()?.into_literal() {
2775 LiteralValue::Error(e) => {
2776 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2777 }
2778 other => coerce_num(&other)?.trunc() as u32,
2779 };
2780 if !(2..=36).contains(&radix) {
2781 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2782 ExcelError::new_num(),
2783 )));
2784 }
2785 let trimmed = text.trim();
2786 match i64::from_str_radix(trimmed, radix) {
2787 Ok(v) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2788 v as f64,
2789 ))),
2790 Err(_) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2791 ExcelError::new_num(),
2792 ))),
2793 }
2794 }
2795}
2796
2797#[derive(Debug)]
2798pub struct CeilingPreciseFn;
2799impl Function for CeilingPreciseFn {
2833 func_caps!(PURE);
2834 fn name(&self) -> &'static str {
2835 "CEILING.PRECISE"
2836 }
2837 fn min_args(&self) -> usize {
2838 1
2839 }
2840 fn variadic(&self) -> bool {
2841 true
2842 }
2843 fn arg_schema(&self) -> &'static [ArgSchema] {
2844 &ARG_NUM_LENIENT_TWO[..]
2845 }
2846 fn eval<'a, 'b, 'c>(
2847 &self,
2848 args: &'c [ArgumentHandle<'a, 'b>],
2849 _: &dyn FunctionContext<'b>,
2850 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2851 if args.is_empty() || args.len() > 2 {
2852 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2853 ExcelError::new_value(),
2854 )));
2855 }
2856 let n = match args[0].value()?.into_literal() {
2857 LiteralValue::Error(e) => {
2858 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2859 }
2860 other => coerce_num(&other)?,
2861 };
2862 let sig = if args.len() == 2 {
2863 match args[1].value()?.into_literal() {
2864 LiteralValue::Error(e) => {
2865 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2866 }
2867 other => {
2868 let v = coerce_num(&other)?;
2869 if v == 0.0 {
2870 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2871 }
2872 v.abs()
2873 }
2874 }
2875 } else {
2876 1.0
2877 };
2878 let result = (n / sig).ceil() * sig;
2879 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2880 result,
2881 )))
2882 }
2883}
2884
2885#[derive(Debug)]
2886pub struct FloorPreciseFn;
2887impl Function for FloorPreciseFn {
2920 func_caps!(PURE);
2921 fn name(&self) -> &'static str {
2922 "FLOOR.PRECISE"
2923 }
2924 fn min_args(&self) -> usize {
2925 1
2926 }
2927 fn variadic(&self) -> bool {
2928 true
2929 }
2930 fn arg_schema(&self) -> &'static [ArgSchema] {
2931 &ARG_NUM_LENIENT_TWO[..]
2932 }
2933 fn eval<'a, 'b, 'c>(
2934 &self,
2935 args: &'c [ArgumentHandle<'a, 'b>],
2936 _: &dyn FunctionContext<'b>,
2937 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2938 if args.is_empty() || args.len() > 2 {
2939 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2940 ExcelError::new_value(),
2941 )));
2942 }
2943 let n = match args[0].value()?.into_literal() {
2944 LiteralValue::Error(e) => {
2945 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2946 }
2947 other => coerce_num(&other)?,
2948 };
2949 let sig = if args.len() == 2 {
2950 match args[1].value()?.into_literal() {
2951 LiteralValue::Error(e) => {
2952 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2953 }
2954 other => {
2955 let v = coerce_num(&other)?;
2956 if v == 0.0 {
2957 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
2958 }
2959 v.abs()
2960 }
2961 }
2962 } else {
2963 1.0
2964 };
2965 let result = (n / sig).floor() * sig;
2966 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2967 result,
2968 )))
2969 }
2970}
2971
2972#[derive(Debug)]
2973pub struct IsoCeilingFn;
2974impl Function for IsoCeilingFn {
3007 func_caps!(PURE);
3008 fn name(&self) -> &'static str {
3009 "ISO.CEILING"
3010 }
3011 fn min_args(&self) -> usize {
3012 1
3013 }
3014 fn variadic(&self) -> bool {
3015 true
3016 }
3017 fn arg_schema(&self) -> &'static [ArgSchema] {
3018 &ARG_NUM_LENIENT_TWO[..]
3019 }
3020 fn eval<'a, 'b, 'c>(
3021 &self,
3022 args: &'c [ArgumentHandle<'a, 'b>],
3023 _: &dyn FunctionContext<'b>,
3024 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3025 if args.is_empty() || args.len() > 2 {
3026 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3027 ExcelError::new_value(),
3028 )));
3029 }
3030 let n = match args[0].value()?.into_literal() {
3031 LiteralValue::Error(e) => {
3032 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
3033 }
3034 other => coerce_num(&other)?,
3035 };
3036 let sig = if args.len() == 2 {
3037 match args[1].value()?.into_literal() {
3038 LiteralValue::Error(e) => {
3039 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
3040 }
3041 other => {
3042 let v = coerce_num(&other)?;
3043 if v == 0.0 {
3044 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(0.0)));
3045 }
3046 v.abs()
3047 }
3048 }
3049 } else {
3050 1.0
3051 };
3052 let result = (n / sig).ceil() * sig;
3053 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3054 result,
3055 )))
3056 }
3057}
3058
3059fn collect_nums_from_arg<'a, 'b>(
3062 arg: &'a crate::traits::ArgumentHandle<'a, 'b>,
3063) -> Result<Vec<f64>, ExcelError> {
3064 let mut out = Vec::new();
3065 if let Ok(view) = arg.range_view() {
3066 view.for_each_cell(&mut |cell| {
3067 match cell {
3068 LiteralValue::Error(e) => return Err(e.clone()),
3069 LiteralValue::Number(n) => out.push(*n),
3070 LiteralValue::Int(i) => out.push(*i as f64),
3071 LiteralValue::Boolean(b) => out.push(if *b { 1.0 } else { 0.0 }),
3072 _ => out.push(0.0),
3073 }
3074 Ok(())
3075 })?;
3076 } else {
3077 match arg.value()?.into_literal() {
3078 LiteralValue::Error(e) => return Err(e),
3079 LiteralValue::Array(rows) => {
3080 for row in rows {
3081 for cell in row {
3082 match cell {
3083 LiteralValue::Error(e) => return Err(e),
3084 LiteralValue::Number(n) => out.push(n),
3085 LiteralValue::Int(i) => out.push(i as f64),
3086 LiteralValue::Boolean(b) => out.push(if b { 1.0 } else { 0.0 }),
3087 _ => out.push(0.0),
3088 }
3089 }
3090 }
3091 }
3092 other => {
3093 out.push(coerce_num(&other)?);
3094 }
3095 }
3096 }
3097 Ok(out)
3098}
3099
3100#[derive(Debug)]
3101pub struct SumX2MY2Fn;
3102impl Function for SumX2MY2Fn {
3136 func_caps!(PURE);
3137 fn name(&self) -> &'static str {
3138 "SUMX2MY2"
3139 }
3140 fn min_args(&self) -> usize {
3141 2
3142 }
3143 fn arg_schema(&self) -> &'static [ArgSchema] {
3144 &ARG_RANGE_NUM_LENIENT_ONE[..]
3145 }
3146 fn eval<'a, 'b, 'c>(
3147 &self,
3148 args: &'c [ArgumentHandle<'a, 'b>],
3149 _: &dyn FunctionContext<'b>,
3150 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3151 if args.len() != 2 {
3152 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3153 ExcelError::new_value(),
3154 )));
3155 }
3156 let xs = collect_nums_from_arg(&args[0])?;
3157 let ys = collect_nums_from_arg(&args[1])?;
3158 if xs.len() != ys.len() {
3159 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3160 ExcelError::new_na(),
3161 )));
3162 }
3163 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * x - y * y).sum();
3164 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3165 total,
3166 )))
3167 }
3168}
3169
3170#[derive(Debug)]
3171pub struct SumX2PY2Fn;
3172impl Function for SumX2PY2Fn {
3206 func_caps!(PURE);
3207 fn name(&self) -> &'static str {
3208 "SUMX2PY2"
3209 }
3210 fn min_args(&self) -> usize {
3211 2
3212 }
3213 fn arg_schema(&self) -> &'static [ArgSchema] {
3214 &ARG_RANGE_NUM_LENIENT_ONE[..]
3215 }
3216 fn eval<'a, 'b, 'c>(
3217 &self,
3218 args: &'c [ArgumentHandle<'a, 'b>],
3219 _: &dyn FunctionContext<'b>,
3220 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3221 if args.len() != 2 {
3222 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3223 ExcelError::new_value(),
3224 )));
3225 }
3226 let xs = collect_nums_from_arg(&args[0])?;
3227 let ys = collect_nums_from_arg(&args[1])?;
3228 if xs.len() != ys.len() {
3229 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3230 ExcelError::new_na(),
3231 )));
3232 }
3233 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * x + y * y).sum();
3234 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3235 total,
3236 )))
3237 }
3238}
3239
3240#[derive(Debug)]
3241pub struct SumXMY2Fn;
3242impl Function for SumXMY2Fn {
3276 func_caps!(PURE);
3277 fn name(&self) -> &'static str {
3278 "SUMXMY2"
3279 }
3280 fn min_args(&self) -> usize {
3281 2
3282 }
3283 fn arg_schema(&self) -> &'static [ArgSchema] {
3284 &ARG_RANGE_NUM_LENIENT_ONE[..]
3285 }
3286 fn eval<'a, 'b, 'c>(
3287 &self,
3288 args: &'c [ArgumentHandle<'a, 'b>],
3289 _: &dyn FunctionContext<'b>,
3290 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
3291 if args.len() != 2 {
3292 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3293 ExcelError::new_value(),
3294 )));
3295 }
3296 let xs = collect_nums_from_arg(&args[0])?;
3297 let ys = collect_nums_from_arg(&args[1])?;
3298 if xs.len() != ys.len() {
3299 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
3300 ExcelError::new_na(),
3301 )));
3302 }
3303 let total: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| (x - y).powi(2)).sum();
3304 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
3305 total,
3306 )))
3307 }
3308}
3309
3310pub fn register_builtins() {
3311 use std::sync::Arc;
3312 crate::function_registry::register_function(Arc::new(AbsFn));
3313 crate::function_registry::register_function(Arc::new(SignFn));
3314 crate::function_registry::register_function(Arc::new(IntFn));
3315 crate::function_registry::register_function(Arc::new(TruncFn));
3316 crate::function_registry::register_function(Arc::new(RoundFn));
3317 crate::function_registry::register_function(Arc::new(RoundDownFn));
3318 crate::function_registry::register_function(Arc::new(RoundUpFn));
3319 crate::function_registry::register_function(Arc::new(ModFn));
3320 crate::function_registry::register_function(Arc::new(CeilingFn));
3321 crate::function_registry::register_function(Arc::new(CeilingMathFn));
3322 crate::function_registry::register_function(Arc::new(CeilingPreciseFn));
3323 crate::function_registry::register_function(Arc::new(IsoCeilingFn));
3324 crate::function_registry::register_function(Arc::new(FloorFn));
3325 crate::function_registry::register_function(Arc::new(FloorMathFn));
3326 crate::function_registry::register_function(Arc::new(FloorPreciseFn));
3327 crate::function_registry::register_function(Arc::new(SqrtFn));
3328 crate::function_registry::register_function(Arc::new(PowerFn));
3329 crate::function_registry::register_function(Arc::new(ExpFn));
3330 crate::function_registry::register_function(Arc::new(LnFn));
3331 crate::function_registry::register_function(Arc::new(LogFn));
3332 crate::function_registry::register_function(Arc::new(Log10Fn));
3333 crate::function_registry::register_function(Arc::new(QuotientFn));
3334 crate::function_registry::register_function(Arc::new(EvenFn));
3335 crate::function_registry::register_function(Arc::new(OddFn));
3336 crate::function_registry::register_function(Arc::new(SqrtPiFn));
3337 crate::function_registry::register_function(Arc::new(MultinomialFn));
3338 crate::function_registry::register_function(Arc::new(SeriesSumFn));
3339 crate::function_registry::register_function(Arc::new(SumsqFn));
3340 crate::function_registry::register_function(Arc::new(MroundFn));
3341 crate::function_registry::register_function(Arc::new(RomanFn));
3342 crate::function_registry::register_function(Arc::new(ArabicFn));
3343 crate::function_registry::register_function(Arc::new(BaseFn));
3344 crate::function_registry::register_function(Arc::new(DecimalFn));
3345 crate::function_registry::register_function(Arc::new(SumX2MY2Fn));
3346 crate::function_registry::register_function(Arc::new(SumX2PY2Fn));
3347 crate::function_registry::register_function(Arc::new(SumXMY2Fn));
3348}
3349
3350#[cfg(test)]
3351mod tests_numeric {
3352 use super::*;
3353 use crate::test_workbook::TestWorkbook;
3354 use crate::traits::ArgumentHandle;
3355 use formualizer_common::LiteralValue;
3356 use formualizer_parse::parser::{ASTNode, ASTNodeType};
3357
3358 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
3359 wb.interpreter()
3360 }
3361 fn lit(v: LiteralValue) -> ASTNode {
3362 ASTNode::new(ASTNodeType::Literal(v), None)
3363 }
3364
3365 #[test]
3367 fn abs_basic() {
3368 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
3369 let ctx = interp(&wb);
3370 let n = lit(LiteralValue::Number(-5.5));
3371 let f = ctx.context.get_function("", "ABS").unwrap();
3372 assert_eq!(
3373 f.dispatch(
3374 &[ArgumentHandle::new(&n, &ctx)],
3375 &ctx.function_context(None)
3376 )
3377 .unwrap()
3378 .into_literal(),
3379 LiteralValue::Number(5.5)
3380 );
3381 }
3382 #[test]
3383 fn abs_error_passthrough() {
3384 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
3385 let ctx = interp(&wb);
3386 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
3387 "#VALUE!",
3388 )));
3389 let f = ctx.context.get_function("", "ABS").unwrap();
3390 match f
3391 .dispatch(
3392 &[ArgumentHandle::new(&e, &ctx)],
3393 &ctx.function_context(None),
3394 )
3395 .unwrap()
3396 .into_literal()
3397 {
3398 LiteralValue::Error(er) => assert_eq!(er, "#VALUE!"),
3399 _ => panic!(),
3400 }
3401 }
3402
3403 #[test]
3405 fn sign_neg_zero_pos() {
3406 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
3407 let ctx = interp(&wb);
3408 let f = ctx.context.get_function("", "SIGN").unwrap();
3409 let neg = lit(LiteralValue::Number(-3.2));
3410 let zero = lit(LiteralValue::Int(0));
3411 let pos = lit(LiteralValue::Int(9));
3412 assert_eq!(
3413 f.dispatch(
3414 &[ArgumentHandle::new(&neg, &ctx)],
3415 &ctx.function_context(None)
3416 )
3417 .unwrap()
3418 .into_literal(),
3419 LiteralValue::Number(-1.0)
3420 );
3421 assert_eq!(
3422 f.dispatch(
3423 &[ArgumentHandle::new(&zero, &ctx)],
3424 &ctx.function_context(None)
3425 )
3426 .unwrap()
3427 .into_literal(),
3428 LiteralValue::Number(0.0)
3429 );
3430 assert_eq!(
3431 f.dispatch(
3432 &[ArgumentHandle::new(&pos, &ctx)],
3433 &ctx.function_context(None)
3434 )
3435 .unwrap()
3436 .into_literal(),
3437 LiteralValue::Number(1.0)
3438 );
3439 }
3440 #[test]
3441 fn sign_error_passthrough() {
3442 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SignFn));
3443 let ctx = interp(&wb);
3444 let e = lit(LiteralValue::Error(ExcelError::from_error_string(
3445 "#DIV/0!",
3446 )));
3447 let f = ctx.context.get_function("", "SIGN").unwrap();
3448 match f
3449 .dispatch(
3450 &[ArgumentHandle::new(&e, &ctx)],
3451 &ctx.function_context(None),
3452 )
3453 .unwrap()
3454 .into_literal()
3455 {
3456 LiteralValue::Error(er) => assert_eq!(er, "#DIV/0!"),
3457 _ => panic!(),
3458 }
3459 }
3460
3461 #[test]
3463 fn int_floor_negative() {
3464 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
3465 let ctx = interp(&wb);
3466 let f = ctx.context.get_function("", "INT").unwrap();
3467 let n = lit(LiteralValue::Number(-3.2));
3468 assert_eq!(
3469 f.dispatch(
3470 &[ArgumentHandle::new(&n, &ctx)],
3471 &ctx.function_context(None)
3472 )
3473 .unwrap()
3474 .into_literal(),
3475 LiteralValue::Number(-4.0)
3476 );
3477 }
3478 #[test]
3479 fn int_floor_positive() {
3480 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IntFn));
3481 let ctx = interp(&wb);
3482 let f = ctx.context.get_function("", "INT").unwrap();
3483 let n = lit(LiteralValue::Number(3.7));
3484 assert_eq!(
3485 f.dispatch(
3486 &[ArgumentHandle::new(&n, &ctx)],
3487 &ctx.function_context(None)
3488 )
3489 .unwrap()
3490 .into_literal(),
3491 LiteralValue::Number(3.0)
3492 );
3493 }
3494
3495 #[test]
3497 fn trunc_digits_positive_and_negative() {
3498 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
3499 let ctx = interp(&wb);
3500 let f = ctx.context.get_function("", "TRUNC").unwrap();
3501 let n = lit(LiteralValue::Number(12.3456));
3502 let d2 = lit(LiteralValue::Int(2));
3503 let dneg1 = lit(LiteralValue::Int(-1));
3504 assert_eq!(
3505 f.dispatch(
3506 &[
3507 ArgumentHandle::new(&n, &ctx),
3508 ArgumentHandle::new(&d2, &ctx)
3509 ],
3510 &ctx.function_context(None)
3511 )
3512 .unwrap()
3513 .into_literal(),
3514 LiteralValue::Number(12.34)
3515 );
3516 assert_eq!(
3517 f.dispatch(
3518 &[
3519 ArgumentHandle::new(&n, &ctx),
3520 ArgumentHandle::new(&dneg1, &ctx)
3521 ],
3522 &ctx.function_context(None)
3523 )
3524 .unwrap()
3525 .into_literal(),
3526 LiteralValue::Number(10.0)
3527 );
3528 }
3529 #[test]
3530 fn trunc_default_zero_digits() {
3531 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TruncFn));
3532 let ctx = interp(&wb);
3533 let f = ctx.context.get_function("", "TRUNC").unwrap();
3534 let n = lit(LiteralValue::Number(-12.999));
3535 assert_eq!(
3536 f.dispatch(
3537 &[ArgumentHandle::new(&n, &ctx)],
3538 &ctx.function_context(None)
3539 )
3540 .unwrap()
3541 .into_literal(),
3542 LiteralValue::Number(-12.0)
3543 );
3544 }
3545
3546 #[test]
3548 fn round_half_away_positive_and_negative() {
3549 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
3550 let ctx = interp(&wb);
3551 let f = ctx.context.get_function("", "ROUND").unwrap();
3552 let p = lit(LiteralValue::Number(2.5));
3553 let n = lit(LiteralValue::Number(-2.5));
3554 let d0 = lit(LiteralValue::Int(0));
3555 assert_eq!(
3556 f.dispatch(
3557 &[
3558 ArgumentHandle::new(&p, &ctx),
3559 ArgumentHandle::new(&d0, &ctx)
3560 ],
3561 &ctx.function_context(None)
3562 )
3563 .unwrap()
3564 .into_literal(),
3565 LiteralValue::Number(3.0)
3566 );
3567 assert_eq!(
3568 f.dispatch(
3569 &[
3570 ArgumentHandle::new(&n, &ctx),
3571 ArgumentHandle::new(&d0, &ctx)
3572 ],
3573 &ctx.function_context(None)
3574 )
3575 .unwrap()
3576 .into_literal(),
3577 LiteralValue::Number(-3.0)
3578 );
3579 }
3580 #[test]
3581 fn round_digits_positive() {
3582 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
3583 let ctx = interp(&wb);
3584 let f = ctx.context.get_function("", "ROUND").unwrap();
3585 let n = lit(LiteralValue::Number(1.2345));
3586 let d = lit(LiteralValue::Int(3));
3587 assert_eq!(
3588 f.dispatch(
3589 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3590 &ctx.function_context(None)
3591 )
3592 .unwrap()
3593 .into_literal(),
3594 LiteralValue::Number(1.235)
3595 );
3596 }
3597
3598 #[test]
3600 fn rounddown_truncates() {
3601 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
3602 let ctx = interp(&wb);
3603 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
3604 let n = lit(LiteralValue::Number(1.299));
3605 let d = lit(LiteralValue::Int(2));
3606 assert_eq!(
3607 f.dispatch(
3608 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3609 &ctx.function_context(None)
3610 )
3611 .unwrap()
3612 .into_literal(),
3613 LiteralValue::Number(1.29)
3614 );
3615 }
3616 #[test]
3617 fn rounddown_negative_number() {
3618 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
3619 let ctx = interp(&wb);
3620 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
3621 let n = lit(LiteralValue::Number(-1.299));
3622 let d = lit(LiteralValue::Int(2));
3623 assert_eq!(
3624 f.dispatch(
3625 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3626 &ctx.function_context(None)
3627 )
3628 .unwrap()
3629 .into_literal(),
3630 LiteralValue::Number(-1.29)
3631 );
3632 }
3633
3634 #[test]
3636 fn roundup_away_from_zero() {
3637 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
3638 let ctx = interp(&wb);
3639 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
3640 let n = lit(LiteralValue::Number(1.001));
3641 let d = lit(LiteralValue::Int(2));
3642 assert_eq!(
3643 f.dispatch(
3644 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3645 &ctx.function_context(None)
3646 )
3647 .unwrap()
3648 .into_literal(),
3649 LiteralValue::Number(1.01)
3650 );
3651 }
3652 #[test]
3653 fn roundup_negative() {
3654 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundUpFn));
3655 let ctx = interp(&wb);
3656 let f = ctx.context.get_function("", "ROUNDUP").unwrap();
3657 let n = lit(LiteralValue::Number(-1.001));
3658 let d = lit(LiteralValue::Int(2));
3659 assert_eq!(
3660 f.dispatch(
3661 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&d, &ctx)],
3662 &ctx.function_context(None)
3663 )
3664 .unwrap()
3665 .into_literal(),
3666 LiteralValue::Number(-1.01)
3667 );
3668 }
3669
3670 #[test]
3672 fn mod_positive_negative_cases() {
3673 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
3674 let ctx = interp(&wb);
3675 let f = ctx.context.get_function("", "MOD").unwrap();
3676 let a = lit(LiteralValue::Int(-3));
3677 let b = lit(LiteralValue::Int(2));
3678 let out = f
3679 .dispatch(
3680 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
3681 &ctx.function_context(None),
3682 )
3683 .unwrap();
3684 assert_eq!(out, LiteralValue::Number(1.0));
3685 let a2 = lit(LiteralValue::Int(3));
3686 let b2 = lit(LiteralValue::Int(-2));
3687 let out2 = f
3688 .dispatch(
3689 &[
3690 ArgumentHandle::new(&a2, &ctx),
3691 ArgumentHandle::new(&b2, &ctx),
3692 ],
3693 &ctx.function_context(None),
3694 )
3695 .unwrap();
3696 assert_eq!(out2, LiteralValue::Number(-1.0));
3697 }
3698 #[test]
3699 fn mod_div_by_zero_error() {
3700 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
3701 let ctx = interp(&wb);
3702 let f = ctx.context.get_function("", "MOD").unwrap();
3703 let a = lit(LiteralValue::Int(5));
3704 let zero = lit(LiteralValue::Int(0));
3705 match f
3706 .dispatch(
3707 &[
3708 ArgumentHandle::new(&a, &ctx),
3709 ArgumentHandle::new(&zero, &ctx),
3710 ],
3711 &ctx.function_context(None),
3712 )
3713 .unwrap()
3714 .into_literal()
3715 {
3716 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
3717 _ => panic!(),
3718 }
3719 }
3720
3721 #[test]
3723 fn sqrt_basic_and_domain() {
3724 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SqrtFn));
3725 let ctx = interp(&wb);
3726 let f = ctx.context.get_function("", "SQRT").unwrap();
3727 let n = lit(LiteralValue::Number(9.0));
3728 let out = f
3729 .dispatch(
3730 &[ArgumentHandle::new(&n, &ctx)],
3731 &ctx.function_context(None),
3732 )
3733 .unwrap();
3734 assert_eq!(out, LiteralValue::Number(3.0));
3735 let neg = lit(LiteralValue::Number(-1.0));
3736 let out2 = f
3737 .dispatch(
3738 &[ArgumentHandle::new(&neg, &ctx)],
3739 &ctx.function_context(None),
3740 )
3741 .unwrap();
3742 assert!(matches!(out2.into_literal(), LiteralValue::Error(_)));
3743 }
3744
3745 #[test]
3746 fn power_fractional_negative_domain() {
3747 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(PowerFn));
3748 let ctx = interp(&wb);
3749 let f = ctx.context.get_function("", "POWER").unwrap();
3750 let a = lit(LiteralValue::Number(-4.0));
3751 let half = lit(LiteralValue::Number(0.5));
3752 let out = f
3753 .dispatch(
3754 &[
3755 ArgumentHandle::new(&a, &ctx),
3756 ArgumentHandle::new(&half, &ctx),
3757 ],
3758 &ctx.function_context(None),
3759 )
3760 .unwrap();
3761 assert!(matches!(out.into_literal(), LiteralValue::Error(_))); }
3763
3764 #[test]
3765 fn log_variants() {
3766 let wb = TestWorkbook::new()
3767 .with_function(std::sync::Arc::new(LogFn))
3768 .with_function(std::sync::Arc::new(Log10Fn))
3769 .with_function(std::sync::Arc::new(LnFn));
3770 let ctx = interp(&wb);
3771 let logf = ctx.context.get_function("", "LOG").unwrap();
3772 let log10f = ctx.context.get_function("", "LOG10").unwrap();
3773 let lnf = ctx.context.get_function("", "LN").unwrap();
3774 let n = lit(LiteralValue::Number(100.0));
3775 let base = lit(LiteralValue::Number(10.0));
3776 assert_eq!(
3777 logf.dispatch(
3778 &[
3779 ArgumentHandle::new(&n, &ctx),
3780 ArgumentHandle::new(&base, &ctx)
3781 ],
3782 &ctx.function_context(None)
3783 )
3784 .unwrap()
3785 .into_literal(),
3786 LiteralValue::Number(2.0)
3787 );
3788 assert_eq!(
3789 log10f
3790 .dispatch(
3791 &[ArgumentHandle::new(&n, &ctx)],
3792 &ctx.function_context(None)
3793 )
3794 .unwrap()
3795 .into_literal(),
3796 LiteralValue::Number(2.0)
3797 );
3798 assert_eq!(
3799 lnf.dispatch(
3800 &[ArgumentHandle::new(&n, &ctx)],
3801 &ctx.function_context(None)
3802 )
3803 .unwrap()
3804 .into_literal(),
3805 LiteralValue::Number(100.0f64.ln())
3806 );
3807 }
3808 #[test]
3809 fn ceiling_floor_basic() {
3810 let wb = TestWorkbook::new()
3811 .with_function(std::sync::Arc::new(CeilingFn))
3812 .with_function(std::sync::Arc::new(FloorFn))
3813 .with_function(std::sync::Arc::new(CeilingMathFn))
3814 .with_function(std::sync::Arc::new(FloorMathFn));
3815 let ctx = interp(&wb);
3816 let c = ctx.context.get_function("", "CEILING").unwrap();
3817 let f = ctx.context.get_function("", "FLOOR").unwrap();
3818 let n = lit(LiteralValue::Number(5.1));
3819 let sig = lit(LiteralValue::Number(2.0));
3820 assert_eq!(
3821 c.dispatch(
3822 &[
3823 ArgumentHandle::new(&n, &ctx),
3824 ArgumentHandle::new(&sig, &ctx)
3825 ],
3826 &ctx.function_context(None)
3827 )
3828 .unwrap()
3829 .into_literal(),
3830 LiteralValue::Number(6.0)
3831 );
3832 assert_eq!(
3833 f.dispatch(
3834 &[
3835 ArgumentHandle::new(&n, &ctx),
3836 ArgumentHandle::new(&sig, &ctx)
3837 ],
3838 &ctx.function_context(None)
3839 )
3840 .unwrap()
3841 .into_literal(),
3842 LiteralValue::Number(4.0)
3843 );
3844 }
3845
3846 #[test]
3847 fn quotient_basic_and_div_zero() {
3848 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(QuotientFn));
3849 let ctx = interp(&wb);
3850 let f = ctx.context.get_function("", "QUOTIENT").unwrap();
3851
3852 let ten = lit(LiteralValue::Int(10));
3853 let three = lit(LiteralValue::Int(3));
3854 assert_eq!(
3855 f.dispatch(
3856 &[
3857 ArgumentHandle::new(&ten, &ctx),
3858 ArgumentHandle::new(&three, &ctx),
3859 ],
3860 &ctx.function_context(None),
3861 )
3862 .unwrap()
3863 .into_literal(),
3864 LiteralValue::Number(3.0)
3865 );
3866
3867 let neg_ten = lit(LiteralValue::Int(-10));
3868 assert_eq!(
3869 f.dispatch(
3870 &[
3871 ArgumentHandle::new(&neg_ten, &ctx),
3872 ArgumentHandle::new(&three, &ctx),
3873 ],
3874 &ctx.function_context(None),
3875 )
3876 .unwrap()
3877 .into_literal(),
3878 LiteralValue::Number(-3.0)
3879 );
3880
3881 let zero = lit(LiteralValue::Int(0));
3882 match f
3883 .dispatch(
3884 &[
3885 ArgumentHandle::new(&ten, &ctx),
3886 ArgumentHandle::new(&zero, &ctx),
3887 ],
3888 &ctx.function_context(None),
3889 )
3890 .unwrap()
3891 .into_literal()
3892 {
3893 LiteralValue::Error(e) => assert_eq!(e, "#DIV/0!"),
3894 other => panic!("expected #DIV/0!, got {other:?}"),
3895 }
3896 }
3897
3898 #[test]
3899 fn even_odd_examples() {
3900 let wb = TestWorkbook::new()
3901 .with_function(std::sync::Arc::new(EvenFn))
3902 .with_function(std::sync::Arc::new(OddFn));
3903 let ctx = interp(&wb);
3904
3905 let even = ctx.context.get_function("", "EVEN").unwrap();
3906 let odd = ctx.context.get_function("", "ODD").unwrap();
3907
3908 let one_half = lit(LiteralValue::Number(1.5));
3909 let three = lit(LiteralValue::Int(3));
3910 let neg_one = lit(LiteralValue::Int(-1));
3911 let two = lit(LiteralValue::Int(2));
3912 let zero = lit(LiteralValue::Int(0));
3913
3914 assert_eq!(
3915 even.dispatch(
3916 &[ArgumentHandle::new(&one_half, &ctx)],
3917 &ctx.function_context(None),
3918 )
3919 .unwrap()
3920 .into_literal(),
3921 LiteralValue::Number(2.0)
3922 );
3923 assert_eq!(
3924 even.dispatch(
3925 &[ArgumentHandle::new(&three, &ctx)],
3926 &ctx.function_context(None),
3927 )
3928 .unwrap()
3929 .into_literal(),
3930 LiteralValue::Number(4.0)
3931 );
3932 assert_eq!(
3933 even.dispatch(
3934 &[ArgumentHandle::new(&neg_one, &ctx)],
3935 &ctx.function_context(None),
3936 )
3937 .unwrap()
3938 .into_literal(),
3939 LiteralValue::Number(-2.0)
3940 );
3941 assert_eq!(
3942 even.dispatch(
3943 &[ArgumentHandle::new(&two, &ctx)],
3944 &ctx.function_context(None),
3945 )
3946 .unwrap()
3947 .into_literal(),
3948 LiteralValue::Number(2.0)
3949 );
3950
3951 assert_eq!(
3952 odd.dispatch(
3953 &[ArgumentHandle::new(&one_half, &ctx)],
3954 &ctx.function_context(None),
3955 )
3956 .unwrap()
3957 .into_literal(),
3958 LiteralValue::Number(3.0)
3959 );
3960 assert_eq!(
3961 odd.dispatch(
3962 &[ArgumentHandle::new(&two, &ctx)],
3963 &ctx.function_context(None),
3964 )
3965 .unwrap()
3966 .into_literal(),
3967 LiteralValue::Number(3.0)
3968 );
3969 assert_eq!(
3970 odd.dispatch(
3971 &[ArgumentHandle::new(&neg_one, &ctx)],
3972 &ctx.function_context(None),
3973 )
3974 .unwrap()
3975 .into_literal(),
3976 LiteralValue::Number(-1.0)
3977 );
3978 assert_eq!(
3979 odd.dispatch(
3980 &[ArgumentHandle::new(&zero, &ctx)],
3981 &ctx.function_context(None),
3982 )
3983 .unwrap()
3984 .into_literal(),
3985 LiteralValue::Number(1.0)
3986 );
3987 }
3988
3989 #[test]
3990 fn sqrtpi_multinomial_and_seriessum_examples() {
3991 let wb = TestWorkbook::new()
3992 .with_function(std::sync::Arc::new(SqrtPiFn))
3993 .with_function(std::sync::Arc::new(MultinomialFn))
3994 .with_function(std::sync::Arc::new(SeriesSumFn));
3995 let ctx = interp(&wb);
3996
3997 let sqrtpi = ctx.context.get_function("", "SQRTPI").unwrap();
3998 let one = lit(LiteralValue::Int(1));
3999 match sqrtpi
4000 .dispatch(
4001 &[ArgumentHandle::new(&one, &ctx)],
4002 &ctx.function_context(None),
4003 )
4004 .unwrap()
4005 .into_literal()
4006 {
4007 LiteralValue::Number(v) => assert!((v - std::f64::consts::PI.sqrt()).abs() < 1e-12),
4008 other => panic!("expected numeric SQRTPI, got {other:?}"),
4009 }
4010
4011 let multinomial = ctx.context.get_function("", "MULTINOMIAL").unwrap();
4012 let two = lit(LiteralValue::Int(2));
4013 let three = lit(LiteralValue::Int(3));
4014 let four = lit(LiteralValue::Int(4));
4015 assert_eq!(
4016 multinomial
4017 .dispatch(
4018 &[
4019 ArgumentHandle::new(&two, &ctx),
4020 ArgumentHandle::new(&three, &ctx),
4021 ArgumentHandle::new(&four, &ctx),
4022 ],
4023 &ctx.function_context(None),
4024 )
4025 .unwrap()
4026 .into_literal(),
4027 LiteralValue::Number(1260.0)
4028 );
4029
4030 let seriessum = ctx.context.get_function("", "SERIESSUM").unwrap();
4031 let x = lit(LiteralValue::Int(2));
4032 let n0 = lit(LiteralValue::Int(0));
4033 let m1 = lit(LiteralValue::Int(1));
4034 let coeffs = ASTNode::new(
4035 ASTNodeType::Literal(LiteralValue::Array(vec![vec![
4036 LiteralValue::Int(1),
4037 LiteralValue::Int(2),
4038 LiteralValue::Int(3),
4039 ]])),
4040 None,
4041 );
4042 assert_eq!(
4043 seriessum
4044 .dispatch(
4045 &[
4046 ArgumentHandle::new(&x, &ctx),
4047 ArgumentHandle::new(&n0, &ctx),
4048 ArgumentHandle::new(&m1, &ctx),
4049 ArgumentHandle::new(&coeffs, &ctx),
4050 ],
4051 &ctx.function_context(None),
4052 )
4053 .unwrap()
4054 .into_literal(),
4055 LiteralValue::Number(17.0)
4056 );
4057 }
4058
4059 #[test]
4060 fn sumsq_basic() {
4061 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumsqFn));
4062 let ctx = interp(&wb);
4063 let f = ctx.context.get_function("", "SUMSQ").unwrap();
4064 let a = lit(LiteralValue::Int(3));
4065 let b = lit(LiteralValue::Int(4));
4066 assert_eq!(
4067 f.dispatch(
4068 &[ArgumentHandle::new(&a, &ctx), ArgumentHandle::new(&b, &ctx)],
4069 &ctx.function_context(None)
4070 )
4071 .unwrap()
4072 .into_literal(),
4073 LiteralValue::Number(25.0)
4074 );
4075 }
4076
4077 #[test]
4078 fn mround_sign_and_midpoint() {
4079 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(MroundFn));
4080 let ctx = interp(&wb);
4081 let f = ctx.context.get_function("", "MROUND").unwrap();
4082
4083 let n = lit(LiteralValue::Number(1.3));
4084 let m = lit(LiteralValue::Number(0.2));
4085 match f
4086 .dispatch(
4087 &[ArgumentHandle::new(&n, &ctx), ArgumentHandle::new(&m, &ctx)],
4088 &ctx.function_context(None),
4089 )
4090 .unwrap()
4091 .into_literal()
4092 {
4093 LiteralValue::Number(v) => assert!((v - 1.4).abs() < 1e-12),
4094 other => panic!("expected numeric result, got {other:?}"),
4095 }
4096
4097 let bad_m = lit(LiteralValue::Number(-2.0));
4098 let five = lit(LiteralValue::Number(5.0));
4099 match f
4100 .dispatch(
4101 &[
4102 ArgumentHandle::new(&five, &ctx),
4103 ArgumentHandle::new(&bad_m, &ctx),
4104 ],
4105 &ctx.function_context(None),
4106 )
4107 .unwrap()
4108 .into_literal()
4109 {
4110 LiteralValue::Error(e) => assert_eq!(e, "#NUM!"),
4111 other => panic!("expected #NUM!, got {other:?}"),
4112 }
4113 }
4114
4115 #[test]
4116 fn roman_and_arabic_examples() {
4117 let wb = TestWorkbook::new()
4118 .with_function(std::sync::Arc::new(RomanFn))
4119 .with_function(std::sync::Arc::new(ArabicFn));
4120 let ctx = interp(&wb);
4121
4122 let roman = ctx.context.get_function("", "ROMAN").unwrap();
4123 let n499 = lit(LiteralValue::Int(499));
4124 let out = roman
4125 .dispatch(
4126 &[ArgumentHandle::new(&n499, &ctx)],
4127 &ctx.function_context(None),
4128 )
4129 .unwrap()
4130 .into_literal();
4131 assert_eq!(out, LiteralValue::Text("CDXCIX".to_string()));
4132
4133 let form4 = lit(LiteralValue::Int(4));
4134 let out_form4 = roman
4135 .dispatch(
4136 &[
4137 ArgumentHandle::new(&n499, &ctx),
4138 ArgumentHandle::new(&form4, &ctx),
4139 ],
4140 &ctx.function_context(None),
4141 )
4142 .unwrap()
4143 .into_literal();
4144 assert_eq!(out_form4, LiteralValue::Text("ID".to_string()));
4145
4146 let arabic = ctx.context.get_function("", "ARABIC").unwrap();
4147 let roman_text = lit(LiteralValue::Text("CDXCIX".to_string()));
4148 let out_arabic = arabic
4149 .dispatch(
4150 &[ArgumentHandle::new(&roman_text, &ctx)],
4151 &ctx.function_context(None),
4152 )
4153 .unwrap()
4154 .into_literal();
4155 assert_eq!(out_arabic, LiteralValue::Number(499.0));
4156 }
4157
4158 #[test]
4161 fn round_one_arg_returns_error_not_panic() {
4162 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundFn));
4163 let ctx = interp(&wb);
4164 let f = ctx.context.get_function("", "ROUND").unwrap();
4165 let n = lit(LiteralValue::Number(2.5));
4166 let result = f
4167 .dispatch(
4168 &[ArgumentHandle::new(&n, &ctx)],
4169 &ctx.function_context(None),
4170 )
4171 .unwrap()
4172 .into_literal();
4173 assert!(
4174 matches!(result, LiteralValue::Error(_)),
4175 "Expected an error, got {result:?}"
4176 );
4177 }
4178
4179 #[test]
4180 fn rounddown_one_arg_returns_error_not_panic() {
4181 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RoundDownFn));
4182 let ctx = interp(&wb);
4183 let f = ctx.context.get_function("", "ROUNDDOWN").unwrap();
4184 let n = lit(LiteralValue::Number(1.9));
4185 let result = f
4186 .dispatch(
4187 &[ArgumentHandle::new(&n, &ctx)],
4188 &ctx.function_context(None),
4189 )
4190 .unwrap()
4191 .into_literal();
4192 assert!(
4193 matches!(result, LiteralValue::Error(_)),
4194 "Expected an error, got {result:?}"
4195 );
4196 }
4197
4198 #[test]
4199 fn abs_zero_args_returns_error_not_panic() {
4200 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(AbsFn));
4201 let ctx = interp(&wb);
4202 let f = ctx.context.get_function("", "ABS").unwrap();
4203 let result = f
4204 .dispatch(&[], &ctx.function_context(None))
4205 .unwrap()
4206 .into_literal();
4207 assert!(
4208 matches!(result, LiteralValue::Error(_)),
4209 "Expected an error, got {result:?}"
4210 );
4211 }
4212
4213 #[test]
4214 fn mod_one_arg_returns_error_not_panic() {
4215 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(ModFn));
4216 let ctx = interp(&wb);
4217 let f = ctx.context.get_function("", "MOD").unwrap();
4218 let n = lit(LiteralValue::Number(10.0));
4219 let result = f
4220 .dispatch(
4221 &[ArgumentHandle::new(&n, &ctx)],
4222 &ctx.function_context(None),
4223 )
4224 .unwrap()
4225 .into_literal();
4226 assert!(
4227 matches!(result, LiteralValue::Error(_)),
4228 "Expected an error, got {result:?}"
4229 );
4230 }
4231}