From 11337938c1ab6145559f272aa17f9b5ce55fe466 Mon Sep 17 00:00:00 2001 From: Gian Hancock Date: Thu, 9 Jan 2025 21:51:51 +1100 Subject: [PATCH 1/5] Add additional PRODUCT function tests --- base/src/test/test_fn_product.rs | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/base/src/test/test_fn_product.rs b/base/src/test/test_fn_product.rs index 20dfc3d9..3a27df16 100644 --- a/base/src/test/test_fn_product.rs +++ b/base/src/test/test_fn_product.rs @@ -13,3 +13,112 @@ fn test_fn_product_arguments() { // Error (Incorrect number of arguments) assert_eq!(model._get_text("A1"), *"#ERROR!"); } + +#[test] +fn test_fn_product_text_converted_to_number() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT("1")"#); + model._set("A2", r#"=PRODUCT("1e2")"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"1"); + assert_eq!(model._get_text("A2"), *"100"); +} + +#[test] +fn test_fn_product_invalid_text() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT("a")"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"#VALUE!"); +} + +#[test] +fn test_fn_product_text_in_range_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(B1:D1)"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_product_text_in_reference_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(B1)"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_product_text_in_indirect_reference_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(INDIRECT("B1"))"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_product_text_in_indirect_reference() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(INDIRECT("B1"))"#); + model._set("B1", r#"100"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"100"); +} + +#[test] +fn test_fn_product_invalid_text_in_range() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(B1:D1)"#); + model._set("B1", "a"); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_product_invalid_text_in_reference() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(B1)"#); + model._set("B1", r#"a"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_product_boolean_values_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=PRODUCT(TRUE)"#); + model._set("A2", r#"=PRODUCT(FALSE)"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"1"); + assert_eq!(model._get_text("A2"), *"0"); +} From 0ebac5446de30e0155f05bfe002accec571d4bea Mon Sep 17 00:00:00 2001 From: Gian Hancock Date: Thu, 9 Jan 2025 21:52:06 +1100 Subject: [PATCH 2/5] Add additional SUM function tests --- base/src/test/test_fn_sum.rs | 109 +++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/base/src/test/test_fn_sum.rs b/base/src/test/test_fn_sum.rs index fa680d0a..5516f1c2 100644 --- a/base/src/test/test_fn_sum.rs +++ b/base/src/test/test_fn_sum.rs @@ -17,3 +17,112 @@ fn test_fn_sum_arguments() { assert_eq!(model._get_text("A3"), *"1"); assert_eq!(model._get_text("A4"), *"4"); } + +#[test] +fn test_fn_sum_text_converted_to_number() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM("1")"#); + model._set("A2", r#"=SUM("1e2")"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"1"); + assert_eq!(model._get_text("A2"), *"100"); +} + +#[test] +fn test_fn_sum_invalid_text() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM("a")"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"#VALUE!"); +} + +#[test] +fn test_fn_sum_text_in_range_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(B1:D1)"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_sum_text_in_reference_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(B1)"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_sum_text_in_indirect_reference_not_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(INDIRECT("B1"))"#); + model._set("B1", r#"="100""#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_sum_text_in_indirect_reference() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(INDIRECT("B1"))"#); + model._set("B1", r#"100"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"100"); +} + +#[test] +fn test_fn_sum_invalid_text_in_range() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(B1:D1)"#); + model._set("B1", "a"); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_sum_invalid_text_in_reference() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(B1)"#); + model._set("B1", r#"a"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"0"); +} + +#[test] +fn test_fn_sum_boolean_values_converted() { + let mut model = new_empty_model(); + + model._set("A1", r#"=SUM(TRUE)"#); + model._set("A2", r#"=SUM(FALSE)"#); + + model.evaluate(); + + assert_eq!(model._get_text("A1"), *"1"); + assert_eq!(model._get_text("A2"), *"0"); +} From fc135324429d13c3014dfe981357fcbe298a791d Mon Sep 17 00:00:00 2001 From: Gian Hancock Date: Thu, 9 Jan 2025 21:52:44 +1100 Subject: [PATCH 3/5] Add tests xlsx compatibility tests for SUM and PRODUCT function --- xlsx/tests/calc_tests/PRODUCT_SUM.xlsx | Bin 0 -> 11161 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 xlsx/tests/calc_tests/PRODUCT_SUM.xlsx diff --git a/xlsx/tests/calc_tests/PRODUCT_SUM.xlsx b/xlsx/tests/calc_tests/PRODUCT_SUM.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ade60b9486a7cdc195c323d073e470240e8f2d93 GIT binary patch literal 11161 zcmeHtg;yNg^7Y^voZuliB)Cg(cY=lBHo*o44GzI25Nsd>3$6npXmEFT8C-)C-1$iE zd%t^c?)(0N_j;}Fp6TvgYkE%YQ~Okvnj!)s0RS0*3IG780BOu1unimlfP(}8-~&+M z^(4V|&Oke7BTaXEppyZsn=R;RCL%mzIspFR`TuSI#s2^l(xTGFjv0I)S1%d=+`8OB z47C_$(u_;b@#HD(J}HZGCO;Vrw))0QA|EQTlk<}7`o8JgTl#VXvoaRM@xcU;euNIP zwqVX)q4`1I=1m%qI7q9NAA=5tP&RXNWnDJs*&I>Ek^ucMB5MDkELg^P`GwAGfxe|w z_EhPLVRxktToXm){x@e{|44Tb&$ROqYW#F!qVV4LV?Ww=;t2!Te#NhP0?%a>al;_s z;;Z#oN(%H|B@P{%D89fJw>-(ZtS+}1R&gu?=MHUcDpOHaN6Ul)NbAsfQee%v(c~)ecv76+Lk-1 zO5kJ$k43-bx$cR{yj?x1KF&;Q- z
    gv9bO<{*Rmg#f1FJt3zXzRsZ1TO-T3o#C$Z4grcjsTpg9B&pY`!?8?Y2TC({j z267xt(f|Zm-v*zXUfBGL$n9>bvlVXWdprUmnkv`g;Ez`hE-26F9TQ|6pv&#JZzoPC z&OXU1xHGoFUd%q-gj# zK9)*u!oT%mQAz1mr{>1H;2e_eqDw#Z)ve(E(UCtW=&; zv+79N=*+Mi;-X15@<_Ka^+>4{Ko!O$EX}pE^G~0&Y;GEt4&=&hrR3O3+sp*m>*ArE zNE>pSiWQAIEj?@fJliSvdIHd1%ZPC~>bI>MfA!vWfdBQ!rL!k>vuSNy{-QF&I&Jc$ z&W@T|*(qpMF+>2{vuxFyuI}~=Nz}7E4u0$&4gqggO{^&B#rHU-nO-h(PV-Y zmXMYFN>*UektK_4G{F%`;>l7A&TC$YUc5kDNbFIPoS<1HBE^%NE3E;S>GBl6)=IkD zIu`z*R(Nl}FT=6p3^opm<|UQ&Dcka8`0LU=>gpRx-D|MgVqo$bG_$AbsY|O>TP%{r zX#~WojIBc?1_w2!`NPRqU++9qM_tZ)@h)1e77i-G29(?Dp%{4CZ=#l`+K!JFh>mT# zrzqTmcl~6_BpukE3Qzk$HTGA#Ua@sjy|tJorZFcOoWqK2b~2;EdXr!wkYGb~F2vs^ z>(eXu{5XJ};6&3^42eOJ&s-F0yjCrJCbLRN;S;>=YaLv9&}-s?v_Ry{ut1v~JbRas z&sCMYjVxmy)aXUNU)vEv9VejHJcy`J{P{ZIF^rseHDcfyF_Eqg0t5(y)! zS=*0NTSVd&zi8VK+)js31`3*Eik$s2Bx{2&69#^B1sKF_#U)p)-L;+GF33ja-xOC$ zmA4SlC0tUM5ov5PnZFwvV$7VTcffx)K|y+w#E%>fnzHZ2?;{qf0`{lW5Y`xSN42tw zZksLNQ@(oh3h(ZaP@UPSbv-=p1g+6Pu@)^1AbyGkw|H(Y=}Jyb#wf@Uth0i~*=oN= zy#J!g0DH(HhU_`Vsd@Z$vZ|quSvgEO43v?mMd4~@RQbc?jK5}!0n7bL&GXK&WW{It z798xl(SNcy76r14_`q+`2PTsOP~jd}{71C=E0_Nr`QRQ@>BF=C-5WHv&$^u* zQ~EsUE?}h5fzw+p*x}8HdZKNz&pUevzmh46p7+{;jk;h@bF^Iags=AzG(n_+Z;Ft zb5<>c%0YOBzaZrx+HA{aCQHOr*)c8`dvu8BspuF%h`OIBr21)iM%3WFOU-^ z*MT)g_%dcipD}sg`U_MhusT*-Gh%u?Tb%CdA1dbn-?wy^Pj8C;M<05#n>GqV0RXIN z0RZBMC;k-)oGgJrXD7B_Cyt*XAtlZLoXk!Te88|Ls?*5O8JLX_qzR?a%`s4#pQ<#& zY6*}0wCANcc6W`HE&p6PDO2Gc_VmGVT0j(0HA+?wR?4fY=Zv4*Az}=+#kDTtzFuE4 zu^|^vYjrJQ;^Lp1$6)K%uJ0RM$QI!d{R0PyDjsDFksOyH(cHA34KFL1rQ*UF%_m(h z_~pySj4+3-3UEodoXB<0^I*cb6d-0@HC|{g!B)ZR*-b?i@e8w(SJ`=&f(|#%R7D@QeG>9VeFW9&p&z2 zuqv|j$XnCGf$~D&Ju~!Nht!E+Ib^Zau)ABvo+>%rD7O%}4k^w>1`&~GuRk|d@AcW; zmo1h=iTVzGNM;^-lV^0jiGQQD@hthaXW~vxh)SNM^>*8v>u*KbzV z@7y{dZpN5DblYBhMv!Q`v35Oo@wv4Q1>4;OU_s-!Ej~6R9{FJ$za%t{n*ZUbAZJ_O zQ_Gx`{4!SJON+T&fcl-Mp7KRMX++37YPG=2N8yQ#o3q4VPJK4Gl$EQa(s+(slgoL_ zI6p;kwi2q4_hRMCjmnk9!=2oEe9W(nGs%98-FD4O9+W4t4qG9Y4>_twk|=|V5Sog8jHCVuIrqY`^qngYGx z*MI58i8@3|>h^>N9qiAm1s{<*i}v9lctu0pt8-ZDs!_BmYG!v@#I{Uk4iY)eb&djZ zo0(iXN2Iz~C%_%=#1frq+J|7Z6o$GIzHNWW5T!%6a>q<9PlK}CHm8`<XsSea{etLOUcAKo(|MgHv9Z$#Um==w> zHuj*xF*}D>D7SRlf9xVssN1jQOKCHGi}j94xL$;_W8X*F^zx-uZ?TaZpEnn%NF*lOx62?&d695@q1;mEk&mdX%xd97eeF`Sz$9eJ|z$?33B_#m>)mQti3jIPsmE+_%bcG)Aq_XqZolYO9IGU?*l=8 zY?-J!!WT$=)!(G?Zp^I@O3fU~X107v!;B7{V|qM0J-{{yud>Q~vO}$}HJ+-d)Ew{F z(wesB?;^Yjvo*;Pq-HwgMr8=9b<#rnrp~H!9;aWryXh6gqF&3kOh~vaF;`Q8sLwcPX+GwuR*U@F0($<_wO+#?`esS zp40nQ6EIyG#UB;{)dUNKh*IAb4fM_3#@;_DyZifJ(=PWw3wkR%4&ty8+BKXu!B);z zM;uB<6AsK4Z#=d1HNpMDvo&5Y7MG+vPSp$5ZS7El$EqlfXH2{D_QCZ!Hoqrje7my|5aS>hS?-A)?+FSE5MoNxv%8+pcF8aX=Yg2jR-J2J~s5J=)^utJR z46rvo9rvNts6R2)d3KSpbGb28v{0wlfl`B`;c2euXmnkIg3w$}^1`Y!2g+s1efu`iOfh|TG^1_|-I}*CyQ!*8;5+xV$^0l- z?9<)VKGz4KER(iYGPPjv?vkg^^uaiDJsj|21FKPFyRtPVh%Y^KWX+3c9#jb%Aa4j_ z<`;5q6&lE0B;7cKM;a-OWEJl4XRtPcc3hAUnE@=HwuRHn6>F3=bA_%L7JieGe5MgR z^+ds;l@9aT1V`-q#I5}`nE3-XDyzJWVYAderj>}?AfmF?lqW#90dH-cZ?WF(?)3fU zz59<2BDS=942lt8Hm6p?9FuQ&@feG3!4p*xP2usc{MM8=+jO?$XcsRYgu!@xQ|34u=Cn2 z$9`8`poQZ(*$rw@xAYN++y#vybH`c3uYj`pR@(X+3cOnG>PNk&}e*gX@LEDlNxAl-N4c|Y_Yuk8uvjBMP ze-eON)=TApG>HJ}Dr{5@4dj%c;;Cag3RQS+KGYq1u+A`gz{jz#p^b zD<Ho$$~2%!o}l z?$)2NwNJkhHYqlgvwa~qEOIzQ|HZ6nps#6bjNTPQ=)z0Ip)AjN`qJ_*8M<7B~rX%CNeaen%lHy zK@ZU(T=hX9Rh{9lDAWwLt2z=N087r78YF4&1iwpYKnoi+=M75=kh0;rAjPY7I`h$S zCTE`2z3tQz$^}^KeQEaO^RGTFnaa0_NT)&JxzfL8aysp-n-w^i7`u+>>BR*UcNnO; za!)lx<$Y$wsN_4WkZ$Ft!+4$?_#>X_&>Rum=I)$uy)Q~=&v)vQXI~(#nrvE5icVU5 z7IIjrLOtb(pxFD#pYA)}q#Nq{K}oKZw+%=qk;|MaRBqWm=dB>L5H>ElGQxY_0y;wV zM_(g2-zP)kE*WyeQQIx&PEU@(ZGLq0VdiUsrOv(uYZ47hK2~eZJ5?WhOvT?6?w*f^ zRyn`LU=mS$JZKbo0Sl<4HS(()%DkjYDvNiEplT;A7V~sfp(Ce6{8Y3_FxXc02^!jD zBczdbTP#^ZO=*@O!@sEBJM$tS`4LdDYtGhIt+wwo31+QL>z4XK=|TNoqtBPggy&~n zDqpR1t)#)p`=G1kx0iTDrbLxGz?y51!97qc1!AF7vfFt>lT>kHe`YSp!G=j{#qrDJ zSx+dcLY=Bp)O=NP;20jb?lfTTG=D;wT6h%=0~MBVf4o}tP16tcZtk6sYWgh!&(9xAzkh~&3IzTNkrPy+QYB+X8b+M z`=rZ}>IesWr)^FPS?c4b$;<76lx;?&^$cuP#$Af--b`ZkrOjrWm@$2ut)#B#K?)uf z5b7UrWLx|6JpR?JeUP^Q+lIxfNJDQNUSz_id19^Y%332UK95R;FC+O#Su#u5*UOt? zs?rX_-Hl$vZB{sXn~S1}KT^R@R|fp1Qc0qU=Db$bcoz5;#SoY8z)zcF#GaEQot6?| zJqs`yhK{$Hl#kj?c*KiOlLre=S0t}4Y(A-FUrHAx|Iig#I!X>G**_k={4spyBx^=(iguMO2Q{0YL^cZ0QVsW{OFHB@8s+b0y_O% z?$>MCs>0ZD?nJEaTaM17Kk^#r(TPLw(88!_6Dz*htyDbr=^;lMRlYtDVz5fa;wDzQ z9-ZAhb{d*@YLv>0T>9>}9nM5zcHiC>+N-p#RAP3s&gzm&tQFjyVozrkQC-oR*VBdl z5?Gb8V8_UV`qZ3oHIid}&ZP(>iBzDXDNmO|pnzd&Tr_2;Wvy`3Ylt`Z=mmHMy_I$! zXM@`FQRvgFM%GlNuvp?M)09uP?}K8xH$K`FaKCm)pS>`P&FK969l+y3lwBzjMx&Ff zm4ogC|E=U1dcsJav&DWt-3%OB^kVQ@NC^@7`oecU>PQ1+%EWkVhL;)H1!}q0R;DlD zGpi7fJqw-yt)s-27oLX|9(?z+FTI#n&Qx;9iGkZncPy616BmjqAI(QMz%QQ2I%Nqp z_JXA%O%5w82w@<@>txyVI74DBfem*S%?70fIY&|IR$PTo={hePd;M4#iDOY`N7Ps} zrqg}At64Hq@C;R6#Y`R`9aPfmiK3zq&G9}Zc)3MxxUFLJ9B83Ht%89NW?i)~?VF`= zVZdaTo*c($VO~tTtQ|ie=>xZA4msAlPHK8AJFVbU6Y zSQ!Jk#~=@m>nWxF2n`-&)@M$&_iEH(9s5M&wke8od`&S{cRMGBeYNV|VbZxHG9%If z3t;ICP?l}-jurFl_I|8XUp{=E;=Fb_=q8an=*)?p%6$^BZeITlbIBl?dVL60qWt9{6tzIiD46a0%KmL`rs za}8%lD?5u{R0)K%*gd36f)9|+u>7$kJ#~P2R(O^P7%*Eu@<~FgBY=M!Ju-7o+?uiX zbx+?rq}+@rw=vYdcV&sfH`*)$elC$C{<+m?bm=zb;1luT`Ljz{INoN?Lf@n%&Itm! zcXyLMaL^rZ(3;`<$n^)VjcppnEjwitL8*mEtBm`!xZ1;~a(uP`0W5T~&B}b-^}%U{dps=a9sx&tEs--$ zUu-BlpBzsO40Rb)Qmj_bV{5tuch4t=(os9hb6gA2uSoYQa$9q(u1i@MW^NGW_Lnjd zjnr8>a zWTQUi`%oVmT78OIOCK6ly_7ENzS$F(V-ZpVsjNOrXTnGMM3n>$J zEG=SQT-Q`+TQGW2r=Kv~0GgRpAdzNzmt*5;FC)@yTUz3R(~;?ot_w@b3{U)IFE%Ii zYhyfCkqFVuyMyAlv{aGwHa5<5u&aBc&epX4Kh|5n&xc<&dX&5##+m~8p~2&iu{JXS znMqojSlRuY5$!02{}}q%C zr;_FRWO`?BbGRIvFUanMr^$#}z^ResJXLi3X76-;Kc<{ruc`X>yTL${{t|3pZ=m+8 zw`B3&i}!?H`R+nPU7LmI$~(wpk5@-7FV?+NkcGOYw$y@F*LFoJ1_vaK!qy$N9(@HY zLdke1=DJY^Yo`${ZimVR9eQ8H%x@F8VTH;27JQBKEm$cCE?tl&&2kz=!wOkUDuas&KA5~ox{c%RYO6w zQX_lV2~oGyB@qG%5>ZhL)E_;3=*RlM-T7$Y^=51|y!X54XB5D9(ZR$|Y2$)F*4c83 ze&J+fYO!)JKXNW}-o;9OCMk#T(m3Qf?P5|NSB|k2E%SFme+z_zmsaF>V&+NJI6BK< z6q~gkYF1v_v%zOfQss7%8it(d(h^VGcG^a|GKvv6+D#fj@`pq0s-oa>s)U zN5sBVZF@=(w5WW8{pf(D0+wPm%leYb+deFehjbBj&ej}>RIKjS)2eP_+v_mK6N_?l zE}G@63|*u}CqwJQN@zNOU`#(>`Ga9E&@B>vCx4}46y4Do@cwbfx^(9@#0o)GX397P zK;{-(##H1~;ZmZ)Q3b+?o$VHa5KYa>ZQqQ5?3HIe*Z~*<^-a(|IBqlA`I!jgjNtan zyn~7=O~CQDsrezUGk?e;_VuJ7wz{t$W}?X>*jKw&>H22IYtg{}?OYqPGuJBf&1lEr zrP)oDhu3~#uta5@>AUcVatKMfO7e=K8sUqJnm2WrQGsv62rKQ@&l?^$gRz?GH)}WF z@_6tcHlrwBlQOKkIC{QfeHjyN{y3JOwmeozU1D_RrosEJ>z3D2G`b7to@!7rHd_Nb z^+PCwSj=+y(956G!9O!3Ch}-FVGnn3AIfexe|0|-d;9-+--FxzYfFh0|5--}IzYIh z#GEG+m{-H}gPO{{Iw>`Qi>`p~hjf~HDY)7tnx?M@h&yx!pokWjx zsD-U(5+EUx*$|z@yG_p<_z)t+qE-Ij5TpC>Zbin(OFfw!-`w#Xk}M)rN)e*1o@txG z_03qIbhLerWQF$t2qLvu=AHeB1>aLtR-a)16qRTE;}!v9Zo8H$<)h*(ii<-BS0zm7 zL^Y(oJoqBvDz99?6qJAAQ@RwN@;-d4%GjtZD~mNgrP|siGK2?Zr_&9QZV`F=Nq=cY zqytIiw$(&HP9lc0ubb_w%nMr_5<1v zAJ$6)3og3FyRtisf4eUnJj;WA`Oj^P{~oS?pZ`NsqnhI14g9^w>fgYh=j;bg{H5dS zci``RCx1eF9-{7VT`0eU|6cC?6AA!Cq5lH^e~RC~+xflZ`KP5Z%>Vll|0sa|Zsqqp z>7Q2C9+cw4tNfld{oTOt>#siz43Pga@aJmmcj)i3^iSvy%0HmLi`3sO{4RF>v@lQg z%fjEJ&+q8J2jD-k06+*00Pv4+{5$;b6X;*z$n<}K|7$u`Q$%|3ji1eY7=Xryh~L5Z H^V|Oe71r%l literal 0 HcmV?d00001 From f1447546696c2c60f2e44dd36c8ea1dab8e9b105 Mon Sep 17 00:00:00 2001 From: Gian Hancock Date: Thu, 9 Jan 2025 21:53:24 +1100 Subject: [PATCH 4/5] Fix SUM and PRODUCT function excel compatibility These functions convert their arguments to numbers except when they belong to a cell reference or range. --- base/src/cast.rs | 2 +- base/src/functions/mathematical.rs | 107 +++++++++++++++++++++++++---- 2 files changed, 96 insertions(+), 13 deletions(-) diff --git a/base/src/cast.rs b/base/src/cast.rs index 9d0e2e46..6c533ba2 100644 --- a/base/src/cast.rs +++ b/base/src/cast.rs @@ -15,7 +15,7 @@ impl Model { self.cast_to_number(result, cell) } - fn cast_to_number( + pub(crate) fn cast_to_number( &mut self, result: CalcResult, cell: CellReferenceIndex, diff --git a/base/src/functions/mathematical.rs b/base/src/functions/mathematical.rs index 12402c2d..fc33a06c 100644 --- a/base/src/functions/mathematical.rs +++ b/base/src/functions/mathematical.rs @@ -103,6 +103,18 @@ impl Model { CalcResult::Number(result) } + /// SUM(number1, [number2], ...) + /// + /// The SUM function adds values. You can add individual values, cell references or ranges or a mix of all three. + /// + /// # Details + /// Generally each argument is evaluated and the result is converted to a number. However there are some special + /// cases: + /// - **Args evaluating to CalcResult::Range**: The sum of all the numbers in the range is calculated. No + /// conversions are performed, non-numeric values are ignored and errors are propagated. + /// - **Node::ReferenceKind args**: Similar to ranges, conversions are not performed on the evaulated value of the + /// referenced cell and errors are propagated. + /// - **Args evaluating to CalcResult::Error**: Errors are propagated. pub(crate) fn fn_sum(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.is_empty() { return CalcResult::new_args_number_error(cell); @@ -110,8 +122,31 @@ impl Model { let mut result = 0.0; for arg in args { + // Handle reference special case. Behaviour of a reference is equivalent to a range of one cell. + let arg = match arg { + Node::ReferenceKind { + column, + absolute_column, + row, + absolute_row, + sheet_index, + sheet_name, + } => &Node::RangeKind { + sheet_name: sheet_name.clone(), + sheet_index: *sheet_index, + absolute_row1: *absolute_row, + absolute_column1: *absolute_column, + row1: *row, + column1: *column, + absolute_row2: *absolute_row, + absolute_column2: *absolute_column, + row2: *row, + column2: *column, + }, + _ => arg, + }; + match self.evaluate_node_in_context(arg, cell) { - CalcResult::Number(value) => result += value, CalcResult::Range { left, right } => { if left.sheet != right.sheet { return CalcResult::new_error( @@ -170,26 +205,66 @@ impl Model { } } error @ CalcResult::Error { .. } => return error, - _ => { - // We ignore booleans and strings + calc_result => { + let cast_result = self.cast_to_number(calc_result, cell); + match cast_result { + Ok(f) => result += f, + Err(s) => return s, + } } }; } CalcResult::Number(result) } + /// PRODUCT(number1, [number2], ...) + /// + /// The PRODUCT function multiplies values. You can add individual values, cell references or ranges or a mix of all + /// three. + /// + /// # Details + /// Generally each argument is evaluated and the result is converted to a number. However there are some special + /// cases: + /// - **Args evaluating to CalcResult::Range**: The product of all the numbers in the range is calculated. No + /// conversions are performed, non-numeric values are ignored and errors are propagated. + /// - **Node::ReferenceKind args**: Similar to ranges, conversions are not performed on the evaulated value of the + /// referenced cell and errors are propagated. + /// - **Args evaluating to CalcResult::Error**: Errors are propagated. pub(crate) fn fn_product(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.is_empty() { return CalcResult::new_args_number_error(cell); } let mut result = 1.0; let mut seen_value = false; + for arg in args { - match self.evaluate_node_in_context(arg, cell) { - CalcResult::Number(value) => { - seen_value = true; - result *= value; - } + // Handle reference special case. Behaviour of a reference is equivalent to a range of one cell. + let arg = match arg { + Node::ReferenceKind { + column, + absolute_column, + row, + absolute_row, + sheet_index, + sheet_name, + } => &Node::RangeKind { + sheet_name: sheet_name.clone(), + sheet_index: *sheet_index, + absolute_row1: *absolute_row, + absolute_column1: *absolute_column, + row1: *row, + column1: *column, + absolute_row2: *absolute_row, + absolute_column2: *absolute_column, + row2: *row, + column2: *column, + }, + _ => arg, + }; + + let cell_value = self.evaluate_node_in_context(arg, cell); + + match cell_value { CalcResult::Range { left, right } => { if left.sheet != right.sheet { return CalcResult::new_error( @@ -198,6 +273,8 @@ impl Model { "Ranges are in different sheets".to_string(), ); } + + // TODO: We should do this for all functions that run through ranges. See fn_sum for more details let row1 = left.row; let mut row2 = right.row; let column1 = left.column; @@ -228,11 +305,12 @@ impl Model { } for row in row1..row2 + 1 { for column in column1..(column2 + 1) { - match self.evaluate_cell(CellReferenceIndex { + let cell_value = self.evaluate_cell(CellReferenceIndex { sheet: left.sheet, row, column, - }) { + }); + match cell_value { CalcResult::Number(value) => { seen_value = true; result *= value; @@ -246,8 +324,13 @@ impl Model { } } error @ CalcResult::Error { .. } => return error, - _ => { - // We ignore booleans and strings + calc_result => { + seen_value = true; + let cast_result = self.cast_to_number(calc_result, cell); + match cast_result { + Ok(f) => result *= f, + Err(s) => return s, + } } }; } From 8174d06008b41c6add61ce3a22b823e4a591a849 Mon Sep 17 00:00:00 2001 From: Gian Hancock Date: Thu, 9 Jan 2025 21:56:20 +1100 Subject: [PATCH 5/5] Update comments in PRODUCT_SUM xlsx test --- xlsx/tests/calc_tests/PRODUCT_SUM.xlsx | Bin 11161 -> 11170 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/xlsx/tests/calc_tests/PRODUCT_SUM.xlsx b/xlsx/tests/calc_tests/PRODUCT_SUM.xlsx index ade60b9486a7cdc195c323d073e470240e8f2d93..0f360fa0ef78665d31283bc210c8c3ed5cbc654c 100644 GIT binary patch delta 4169 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 delta 4191 zcmZ8kWl$7c)Lv>~0e1oEj#X+&DN!0}kd%<_1_=cLmtGXfrIZkmlqDBX>F#b=Iz+m= zKHhJ>dEc4u&YU~XJm<{$ai2Noo|CQrMZatkh=+cpRsRJ!3*^FARni=#fMy~e;LE_v z1tx@9l+)wl$Gv_uYG%QIU*7!eWmtLX)R7?sC}`Im%ysr3C3n!^X5}@Ib26CbHzfth zH3+|GMJbF%q-U~Qrvsa)}J?M1h?_8+ybRZLB>P3kHtyStj%yTze39?$ONbAm$!U%O-+ z2|-*_^l>>nAm7r%NbpXk6RHPq7GM#t$G^E6jKfy^C1ro|%HzR2)sa)zB!h4Gc924a z+#5b-iK!1Lo!ymAeZCHsw{}z1Y}Pabv*4&EPb)UCZIa#7Bqx@$XJW02A9|Fyk3#s# zk9FTk#^|0o2T+(pNHmz&)jCg3!YS`ZUPnD z!y5eQcWc@atjMRDO#`?p#ow+%C^7WZD{f2l5)sc+yC%6g|S z;j%ep!~U9@5>20EwqyT=Tj1j{eiZ|La-MYWc-Tw*tlFk8?Kj15Q=ym^Ua3v1r8_2l zTYa+IeF|-uXUp0rB{##%VDmU~CfO2;8C5N@yx5^Ql_rQUsBZ2}pz zi=*QfII&K=N_}Z+V1>r0e{{}D(PMaLWma`yaVqwEgB|R9sp)qs?NPG)Vd};${M}-J z@}N-k-k?xgK)vBynu8xhF(cEmXH-b4J7by~6D+yetmyCH62TD@j^kU9z6-mKXKeR4 z1bM(jfleco#|GJMXN+L*0RTtj1~CiV#4VMdJbaIRN6Mgqy(2USCrlT`V3cd3HaA&i z1#bQn^<~Fjcl7oOoTJSB;%l}_AY^LqC?h0>>L>p9Zg84@H8;n%wghQ*m*N_48MuFG zHYDMkxyHyIBO~*zX%w?|^)9mBn{ELcH#l^Fs`5eBAk9$)9@}->>ClpzRXQmg%WUrJ z#e7hKV%#w7=jHyp0v1B2SAHA zNXxa2!tzN~KE_?N)}S>|LK6Xtg|VxP`zrex8O|MhR2mA(cJ(eTxZT?+M>ZEUy(zJF zBQ`hs7Ct@n+hb)xde z`Ov9bBB3R4rNY63M{=zts5#;ueB+1T5lj>zh*+7O#a}hcDW!K3GhhMu`wH~N)_H=o zu*ikJqN9D=D}-Y09kJsaun(G^V{knr;d|o+F}1{GUs$)P6vYL-v>bE4F57Cepg;Qo z=27red9^5)3#5GHu@xfRn8A+d8?daTX%~}>eE7WYjtVUh1P1^BcX$7Lr5UQaVfcw} zxxe^L^Q!W6D?38rSkCzfY`?76?*_H0Fu8Q`teD#yj_ztDqZl6qR=d350=uLnm*KaD zpF6#-cNGgIF;eis=v3ICZNAyn`on9zKU}Fd-Q%~KlDBhJ`Epm+72PEZJy#I`Cud@XFQ(F>T$1?p%3%=Ekrst{%-n&8 zijqrRcMa@os#d&gX|uIbi1w}DYxVO!+RunUR?X0h2cJ?n)@P{Q1daKy(v~j|%Mu0h zEH38kkwL0(8NL!0M5Hu&sX@J}c&J0@wJ6NcJe%%d^rmx8?r81gu)pAynuJLD+&bb+ zl1?15FRdgy)>H0MG-@r9K~jZwBh0~(mE@OhDcOEoU(e> z`ReQ}J$=UW>iPcfOQU+fUpDAW(y3AR+9sz--|08-0$JFBv|7bjM?x<3no68;z;F1k z9b`Li4(WvM!%rzYP20iFOEPJz76|SVY=It1#)*JxbNe&vI-Oe5DbMb7#t9&C3LkP2iAVh(5lO|L!Y(>BLae5w(_&fkym( zSsDs{E@rIdBSCFxS3Fq;rH z_T&uT*=KrU@eG&ce2tuRtOG1H*|?hkZvGMRj^b_0XBhre?7-{!X!1Mq{si;w{o1Og zB)&Au-zdtg-HLFm${>c0)!c<{9UD{>gGM!6Wz1d09b7Yj>7xa@1C#8Pt3B-V|6*ib zKg)D2-r&`h5}du&Dwf52VA14mJOm^re**0wN3#QXfQPtB!JW&;f(^pG@$m`{r@}D&63C`aEV0%^b2;p$oHkd$BR3cqwLF8kHA==YU4f(;-HnF=(jzq4>%mk;BXWy7ezwrxiD;x_#yQ&$P)5 zJddCtSWWz1rAJP#$g{k%so>G`DEPCkpytxDrpL{WTNarr4R_6)sYn9Y9(Lm!e34@c z!izr~-sqZJdIL%6B`<7)(l^+!3lA<(l0Db!`h>W#X$!Z?Jh#4DNNw%aYoDiEeBJY* z)mWQFbO)@Be)~u82RVv~9`5`jzwDC=b(yiq4`)TbB43X9;gS~_8a5u#=}DQBko`3A5xR$#|ltj2m{ z>8ff?i5|@@8LPqG66+Wji(GM5&V3;Q_OKdHJ4DesSddx!+(-_L)(*Q5P=Be(LOa;0DvtL!ps0~ zcD?V!;d^+e;9#(vpMg!j1DSmi5ymBmeuBc`5D?t@7zpb|u9^p2b@vA1rV&n6qkhO0Whxcq#S-U#BQpEorOvYKXmfiYA51I8AeVm^>Wd!q9U^*WQaF3aro;hNrVU6D;$_x9(itf*-6$EkgP(9n~66 zSkw_Dfz=PPk`JZQ&Fu7OIO!4Mz?*5!NSXIMw7fR*=1H3tPMqk02%^dx62V21RMUZb z#c!c3QIDORydGgL@60+{GWzcRnW5mNf4bHD?`32F0stiU->Q!@pOuBPm7KkWgDbC( zi?egWoJK1@aoAp*4}#+i#U{0)xRuvV_7VUg@wO-)%hS;Vig%tz zQur76J{#;@|3Rp}1*D@~8NN7Q3rGV#>zv%s3|m>%zI1D(yri3M+ECJ$RszlJ>6Mp)?XmHR z!rm%at{frs@B&0qwy(YD*i3N1d8sgIo(7=viLc-+@~reO(GaM1CW@cwdX_*nIis|B zJq&ktSD%h{1+a%2TY#cHHaT2_EF?&VNxP>5QBrE-SYoZ3LAYzM=M5Jtz>Rh5HS5xEh2M+qH{q*Z(Xy|3d-&<|YQ@D`Q^t!y z(eY~9vLn;i^#Qk?HzM{@v7ID$ECZ_XIXaN^=m>V{xFz(UmOmC6^!O#t|Lx``oZ471 z$TChI`hRx`C;&ix|Kk6hQDWpSF)cETlLUD|PD1pbV-+dMC5_dOgmW>|{o4}p008p; zLhjK4vWAPD?*G@?e<5?oYc4jrf8