From 67357cf271a3bb6a81d50af9470b53154236a3f8 Mon Sep 17 00:00:00 2001 From: me Date: Thu, 29 Jan 2026 23:20:12 +0500 Subject: [PATCH] initial commit --- artifacts/FastReverseTest.jar | Bin 0 -> 65002 bytes artifacts/Md2HtmlTest.jar | Bin 0 -> 48576 bytes artifacts/ReverseTest.jar | Bin 0 -> 46528 bytes artifacts/SumTest.jar | Bin 0 -> 51276 bytes artifacts/WordStatTest.jar | Bin 0 -> 45846 bytes artifacts/WsppTest.jar | Bin 0 -> 51442 bytes java/RunMe.java | 550 ++++++++++++++++++ java/base/Asserts.java | 84 +++ java/base/BaseChecker.java | 20 + java/base/Either.java | 95 +++ java/base/ExtendedRandom.java | 89 +++ java/base/Functional.java | 92 +++ java/base/Log.java | 56 ++ java/base/MainChecker.java | 28 + java/base/Named.java | 15 + java/base/Pair.java | 44 ++ java/base/Runner.java | 185 ++++++ java/base/Selector.java | 143 +++++ java/base/TestCounter.java | 184 ++++++ java/base/Tester.java | 18 + java/base/Unit.java | 15 + java/base/package-info.java | 7 + java/expression/BigDecimalListExpression.java | 202 +++++++ java/expression/BigIntegerListExpression.java | 192 ++++++ java/expression/Expression.java | 112 ++++ java/expression/ExpressionTest.java | 26 + java/expression/ExpressionTester.java | 288 +++++++++ java/expression/ListExpression.java | 211 +++++++ java/expression/ToMiniString.java | 10 + java/expression/TripleExpression.java | 190 ++++++ java/expression/common/Expr.java | 32 + java/expression/common/ExpressionKind.java | 94 +++ java/expression/common/Generator.java | 173 ++++++ java/expression/common/Node.java | 106 ++++ java/expression/common/NodeRenderer.java | 96 +++ .../common/NodeRendererBuilder.java | 145 +++++ java/expression/common/Reason.java | 60 ++ java/expression/common/Renderer.java | 60 ++ java/expression/common/TestGenerator.java | 56 ++ .../common/TestGeneratorBuilder.java | 145 +++++ java/expression/common/Type.java | 51 ++ java/expression/common/package-info.java | 7 + .../expression/exceptions/ExceptionsTest.java | 30 + .../exceptions/ExceptionsTestSet.java | 162 ++++++ .../exceptions/ExceptionsTester.java | 100 ++++ java/expression/exceptions/ListParser.java | 13 + java/expression/exceptions/package-info.java | 8 + java/expression/package-info.java | 7 + java/expression/parser/ListParser.java | 13 + java/expression/parser/Operations.java | 148 +++++ java/expression/parser/ParserTest.java | 31 + java/expression/parser/ParserTestSet.java | 236 ++++++++ java/expression/parser/ParserTester.java | 76 +++ java/expression/parser/package-info.java | 7 + java/markup/MarkupListTest.java | 248 ++++++++ java/markup/MarkupTest.java | 97 +++ java/markup/MarkupTester.java | 71 +++ java/markup/package-info.java | 7 + java/md2html/Md2HtmlTest.java | 71 +++ java/md2html/Md2HtmlTester.java | 355 +++++++++++ java/md2html/package-info.java | 8 + java/reverse/FastReverseTest.java | 87 +++ java/reverse/ReverseTest.java | 165 ++++++ java/reverse/ReverseTester.java | 299 ++++++++++ java/reverse/package-info.java | 7 + java/sum/SumTest.java | 178 ++++++ java/sum/SumTester.java | 189 ++++++ java/sum/package-info.java | 7 + java/wordStat/WordStatChecker.java | 127 ++++ java/wordStat/WordStatTest.java | 70 +++ java/wordStat/WordStatTester.java | 100 ++++ java/wordStat/package-info.java | 7 + java/wspp/WsppTest.java | 51 ++ java/wspp/WsppTester.java | 139 +++++ java/wspp/package-info.java | 7 + test-rules.md | 113 ++++ 76 files changed, 7115 insertions(+) create mode 100644 artifacts/FastReverseTest.jar create mode 100644 artifacts/Md2HtmlTest.jar create mode 100644 artifacts/ReverseTest.jar create mode 100644 artifacts/SumTest.jar create mode 100644 artifacts/WordStatTest.jar create mode 100644 artifacts/WsppTest.jar create mode 100644 java/RunMe.java create mode 100644 java/base/Asserts.java create mode 100644 java/base/BaseChecker.java create mode 100644 java/base/Either.java create mode 100644 java/base/ExtendedRandom.java create mode 100644 java/base/Functional.java create mode 100644 java/base/Log.java create mode 100644 java/base/MainChecker.java create mode 100644 java/base/Named.java create mode 100644 java/base/Pair.java create mode 100644 java/base/Runner.java create mode 100644 java/base/Selector.java create mode 100644 java/base/TestCounter.java create mode 100644 java/base/Tester.java create mode 100644 java/base/Unit.java create mode 100644 java/base/package-info.java create mode 100644 java/expression/BigDecimalListExpression.java create mode 100644 java/expression/BigIntegerListExpression.java create mode 100644 java/expression/Expression.java create mode 100644 java/expression/ExpressionTest.java create mode 100644 java/expression/ExpressionTester.java create mode 100644 java/expression/ListExpression.java create mode 100644 java/expression/ToMiniString.java create mode 100644 java/expression/TripleExpression.java create mode 100644 java/expression/common/Expr.java create mode 100644 java/expression/common/ExpressionKind.java create mode 100644 java/expression/common/Generator.java create mode 100644 java/expression/common/Node.java create mode 100644 java/expression/common/NodeRenderer.java create mode 100644 java/expression/common/NodeRendererBuilder.java create mode 100644 java/expression/common/Reason.java create mode 100644 java/expression/common/Renderer.java create mode 100644 java/expression/common/TestGenerator.java create mode 100644 java/expression/common/TestGeneratorBuilder.java create mode 100644 java/expression/common/Type.java create mode 100644 java/expression/common/package-info.java create mode 100644 java/expression/exceptions/ExceptionsTest.java create mode 100644 java/expression/exceptions/ExceptionsTestSet.java create mode 100644 java/expression/exceptions/ExceptionsTester.java create mode 100644 java/expression/exceptions/ListParser.java create mode 100644 java/expression/exceptions/package-info.java create mode 100644 java/expression/package-info.java create mode 100644 java/expression/parser/ListParser.java create mode 100644 java/expression/parser/Operations.java create mode 100644 java/expression/parser/ParserTest.java create mode 100644 java/expression/parser/ParserTestSet.java create mode 100644 java/expression/parser/ParserTester.java create mode 100644 java/expression/parser/package-info.java create mode 100644 java/markup/MarkupListTest.java create mode 100644 java/markup/MarkupTest.java create mode 100644 java/markup/MarkupTester.java create mode 100644 java/markup/package-info.java create mode 100644 java/md2html/Md2HtmlTest.java create mode 100644 java/md2html/Md2HtmlTester.java create mode 100644 java/md2html/package-info.java create mode 100644 java/reverse/FastReverseTest.java create mode 100644 java/reverse/ReverseTest.java create mode 100644 java/reverse/ReverseTester.java create mode 100644 java/reverse/package-info.java create mode 100644 java/sum/SumTest.java create mode 100644 java/sum/SumTester.java create mode 100644 java/sum/package-info.java create mode 100644 java/wordStat/WordStatChecker.java create mode 100644 java/wordStat/WordStatTest.java create mode 100644 java/wordStat/WordStatTester.java create mode 100644 java/wordStat/package-info.java create mode 100644 java/wspp/WsppTest.java create mode 100644 java/wspp/WsppTester.java create mode 100644 java/wspp/package-info.java create mode 100644 test-rules.md diff --git a/artifacts/FastReverseTest.jar b/artifacts/FastReverseTest.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d138477e39efaa9c5df351d93f8af4221a1c5a GIT binary patch literal 65002 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 literal 0 HcmV?d00001 diff --git a/artifacts/ReverseTest.jar b/artifacts/ReverseTest.jar new file mode 100644 index 0000000000000000000000000000000000000000..86ec21b1005a0f234ab1f3017aa7163ade94075c GIT binary patch literal 46528 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 literal 0 HcmV?d00001 diff --git a/artifacts/SumTest.jar b/artifacts/SumTest.jar new file mode 100644 index 0000000000000000000000000000000000000000..1a0bb8df1267ce0165a5c4df3b48cfe5ac0d7f3c GIT binary patch literal 51276 zcmZsC18`>D)@E$m>DX4swr$(y8+2^jw(X>2+qUg=I6ZUkcWdrHbL#9FDR+rRk`rCubU!7#CS~kDMljQ5WILloMpmOmNIO3TN;?bxO1XEsa{~U)FoA$9Z2o&j;D3i{WawRZ#F&x1>=K zE#?E-06A+(1}P|e6&f1Z*^kZjxXxY^Pp~cboa}bKdGUGKeBiy#Zv6&4as9e_bw}cR zRRv>J$=4G6RQycaPFZF-uzeSc!J!ftbS+J1%BBbZ(wI(qY?l)nrR@u?k~_XZ?FDBi#{vPQ&SX zAD_-txJE}D=}DuAxX6$xWfDY(QH9scwA6)o2P3oAcZZIC{+B5=4jb(V40vRbz%(Jo*bExKEu3f2ag zD!&!gZ;ThYoDeLe8_c*pMzO496XZx?fp_uZyxlU@2)$5h;AE1W%#m*9EY8Mu7cIF0 zh@7a0PU(i`6`)|ABdhV!61j7dBeD<-6o!c4h1*JzmX-1t4zLcyQWTwJsDwom5!{c&9 zH;0T==?nv)g+{_6l2(ULt59ZbkHqFf9E9Ef5MS5145WoP%8c5?(Hq1KcXW1&3c2i+ zg-AM#*2d1V0ppxuSevqHq?tivxYd>ghj6>MdEF}Qz{V@v^XY(g_+|^}R)x0knFi6d zYBYtCxM^UX+Qt`;u<}qUH5o3}O;s^z6&d`V%g)jTEje8_q`_>VsM7IbwJ~uiK)e8F z;^uK8&YCi1F5=jvb;H)PKV{vft@4u^D7|Ck81>kcMuJHhqz(#*n5m0N3W20(SPnG8 zN6%HA^&o)+cSOm>G#@I2+2=sqDi}gg9Fg0H6}pP-=SVKl971ipeZ-T;UNBnwK`@dU ze4gm60&B?E1f)N-qT;PPz;QdZM$r$w!Blc8Peb9fpvqp|^10|zW`C-T-~UAgzccKv z!CJZn&CAA_wxoPm!=$DtN$Z7Uu9CHMtINY%=}TUnL~k_fZXqB@$>|;dn={M_zccXS z39%ZoU>kMT-lV*)+wL%dWNh2YCIxVnWHG*+l8we%6Vw&+lz@Rh(^T#P1Ol7)o7^sw zPav)%3=bq3ZoIWA4ywoz8+RhLH=gi*GfHogWaFb4Xk)L{lU-A5GNWyhhhMCe@{(%X zV-XI)@ZIQMsx$rB5tloWV>UIrc^z~yT#-H*bwfaKb5S(x`^A}KsAfv#T;^Qo^R?W; zhgED{wVyW#XhWkoQWMccUf2p^%4Sn4v4z?0a`qEAKJH%(iqCNACS`WruBWFf(x^O{ zQt-akFxR>CT;riJ(h*C@!N*()g<(rao=ukmaCGnfN}ec*(o zj8sVu98B^?1F)^tQdDnU{>bqNx-aq4mRb(zr8ZTKZMuxe!p_l(BD0_m^6w_y%Umum zoB>~l+gSRfN5v;!;(7ns2OIwiw>+J92B!wWqvP2`it0ZIw+>Rsvg%EgGa^o}u*m=% zd{{tZX@jnIYlJ_f1zqA#t{(U6q&@3b+;7+*pe{l!}b?tf;_**!Nu%k(+Fzqs!zS4Sk}4-5pKet?5H`ZMn@ z$2UN&ui724`S2Tw?t4F;f$Na6_fsl){6f=9ctwosmrn8;+sNd8adNyijp?S$z62oh z9r3RQsTZx7{T#f3kG%ju3sW-ad16Egy`=o)`&b_$E^S5;4JgwZy9puARiPo)mf^z& zfb$6_%y=_(sC-47cJ!j@PI=-Px-^G{%q?xsYb1v%5_PaMv$M}dXF77fBP+QUo^~ij z)zF|gp{cJ`5@T%N@zB;XT3W3>4;LJGrfpQsNhPZsRS`5Pc;ZCN8Y%9r!nh<2 z?Ica9vo09bS7zeQ?7BpQQv2=R8}arMjNL-*{ec?m1$>t3u{4p!B!tp2kzU*0sDZ+D z9Xt<5KrN=6N1Nd#q0Rt1l+)1Pgtu~svyo~R-K94^FUAnE^BZ+vc%bkAZ4HIIbx^bJ z{0((wCZ8(4jR0%w5rTj-&~(MYsci}013y-0_+;8RAFnfNL@Ib7y1CPmW(;R}#$jh7 zOghD}Iau)8sTsVtKU1eWW9-MPbNjqV#T{fM`H$gkj|Z_s~e(!WTMD&6y(7BCQy7zhv$#y=!T=r5fTwlFoeHg)ACa8ua!K7@{E@F-8A?hN7l5)eGl}P9ljZLi#a9s;KtC%?I4dst zp2ZZO;6J4YsBNCkd3yT1gWd&W1BE7awIElEGop-o<+bq(oFH{aO z>1G=J5gmmvAc8?YN5_X;%|e}>Uw0!inzzxn7;B<_S#GN@Qy|AEe6G8ZK;6vB-PoY3Rts zCcJKjPJZ+qBohEi(19`&qQ&Y$@vwpURW)U>3AQkcp{tGp3)?B2p3x$|MoOs~k{}b> zLCPA@ER_f^q`N-eJ?9N+P0%IeN1pVXddld^A28#lm<9zN{_tHt!p73ZG$a<~w@zCU z<`Y^-_W1t*qefd4+%@u#zvi^CcGgo#FF)kG-1FQNpFF4(`vAX+yw1%q zDkR8ob{A`~t)*$2L1jyv!+l;b4_I6)aK+D86sa!QONTnGjRzr%Q2we5gAxhZQ-cio zsw$vbh7i{0sVD6F5dwmR!4#kv0=>~~M`hKav@)JjV0~t*Zg49-GICBepwpGjF;^4u zBjOIoh`45tOSmW>sh>H?D!L*4I&5h-1A3oaRd@ILPLlPsvF=MQQ88BIa3ej+;FCD@BGwBXHNsj@WZqyUE z_9JXIM`?+)(oSK~mt9}O+I~cAWe|5ZLuwSmnIK$4k<5DKVo*!IOU0ZtrdWq_Oj?j( zs#D)>XW#o}?Fq|9y8$jh>Hs}T>PGR1x!h=Oy5vQHfhXDKj+(9`?@c@hj29au;w&cl zI{l@oc3e%aW|j}3_T59=rR#${e;9A_%qpgL$+=5)gk`MaH~FG{2`uu@w>;=Z5^zb*>iY0vgr}eTaRQ za}ca85K^^iZt@s%0~v7h_H%WclUxf}9S2BUN1zw>4@&0q8;o|-+badmMhkhw%5&uC zWzPJ~|1kX~AeRm~|Kcq8|ARB;e{lBx7iVZ+cCGbxH1fcNVFCB6plSQ4>u8@BFarwL0(>b88C5IuH{*~}L(?Ke#3*7a zLU1Sp0@gsn@@h+}fMg;h`6Q!LVLvQT6bPn4BP*zgODC7rMtl>JsCgdT8Q9(@9 zdt7#mQJj0O+Dr3G9eVYu!t(ZB5W*6CT=Q~RW?cO(?IN2-NX|v1e70l-ZP9m(*Ai`! z;U!mBt0fgH768=&o+Qauw%F9<)1806 z5=YvnapG_2H~B5z2zS2wEr7eA@x~xC6i%}`Vi+Nr6Nx__t5!G{9}#rueB3x0jIr`=5f zx7bz{WhOO0B9ptPKg9ym)+|kiA@Y8U(G4y@BukwZFi|HF`KY#n?yQ?gCtc4dsNMu6 z=1zf&wY$Q)wMqPGoJM-X+d!ZkBw);%{sxos5prvu0o^7)Ll6O+!Xzp`R$rJl3>V#m zI!F~ka%ee>Ja~v%$MTqyfNwFX?|3i1g~gDo4NYpvdt~f9gQxj`@C~AQChc7H2{{6q zdqAWa>2c^t`Qbclow!fB<0vaYGld&!)(`(+X{0h()JQOZ+5i?hi$1WD^JYCWdZNTn zv4fJlYzwl(9*Yc;s}+%JP1&=H$sbrn!F6RC(ssO%a`*y2M#QjItWUI4fw;fWN|eMk z+X=-m`hrs11#beCI|7HW1@{&7O7#2aGRv?E-JuoOVHOn5GtOGWeRyRxk7<+KozH*VKr-C*73QI7@Q*shGnMy-^A0?jDVOdLlsO(6Mz*c8`^de@$V`>e+f zFJ|}eS#BH{vsl z(`BseJ_Vj+&{W8L3M?+ON*~5deP{Z@cJo&F&L#GY#LW`hVzUsboxQ#>N>R_NpHG)i z&O#@mHbxdxHxmIRQ>3`6hWe#Y6J;Ch#Vu{iuNiXOf#jb}?ozVf5*|N5q#vPcSC|tg zmRur40tGRZXGRxeV!FWWO{4e!+PR?v@~l`iDK4TkU8gWJ$GV}ks@AIqa-jg`{&FVw zp=?IJ$x#dE`GbceLI$$w^J+^|7Z!lbsNpk!B3IjWzlPHB?t94cGkM{|J)^2$x^!Un zBHS1oU0ZJTr4SIL)sHAAvZ$W>mEc}O( z$JT$binS-(-|B$-!xPe7{235*6T1gVxlcq8|D1MMUpSGj)x*$q4$T*M8Pug`9AAYpOBT0NH-|k()nui5y7)@37Ho&HcpYc zq`5>&O4!bSA106RtoYr(SGLh#*Ch6T4wHwAshx?biISn6iM{Q=4pp+c%cB zaL72y_BTW(S>ijjqzEtjWdKSEdYJEBv!3mJ2so*kkLLZzZvWvD5cHH}BQv(_JsQ;Q%1^J$t>M z3daG;LY&8pliaj~@!G7Kgf?p%0F5&i?Bk}&4l6q|$_KV5kKK?F@Y*043ApBNCmG8# zJT~-h{j(j4i%+p2)`I6ea?;)Xl8R=HOk4WNsk%ejRB>{K??Tr*&b3IK#STXOeh^xd zvJ7U5ys>($Kx+l2&cm>N#+?xUp1;LIpM%n;rj93it2!6ozWL|&97X!sFp$jdId z8WkJoLKs{yunpZdD|n37Zy#N$)r<0E1Uvp}oEao%L!s+w5(E>TKZi-lKE7;qfWu7& z=IPTWJFtGZe097Wj5Y2fXqben%Toks-x#8YknYe8r{I(#A>`Hbx8peyiVgbYL2@p& zA}Shgi)4Cg>kmed@PL4%%2&zig1fJ)<|_bW@{XGD$=Z|DFB3UvIzrnJ(79nz6}Dfq zLuA$*-+>+a|4gHcC4Yrw#qIL=bNbsZ83yP{2G)NwOr`d~*>h$v< z39|n%L=r}`k&P}RmtU(%M@Nvx3K5fJho|vk>(K%4A>Xf+W@Hq~9isogQc(E42J-0EEG-0NKIocxna$+%p*{BBX?-0fWM-0ocO z-1kl7&E)ORBkY;-*}@siS>u`W*~1y;0_HqJvA$eyf^Ra*=}1Cr!ogVh;niXG;nrdI;pZ4?A_axKY)%dji>KTD=AoyQ*=u-=fca`P zW_d#jpbOoP|Bk&IBWwAO7P^vaF%7|)YQa{+sIlzmX<{=0XT^Pc_bF?riqvL60&m~U zAB8^cUkzd*xZ>q;*cFj(*citPc(`KP})yyvl%iKR}4_0<`cd_E`m7y_zg4xXaaW`1d$%9D3A5B({K zp-g0}RuSV0o?d^RUNa4fbZ(=Tjd^^YpsDuKlXTA)m+C2iDa_Sp*{(y5eWuz_-OgUFTunXF`5|=&3*G8iYK7o?G*o++ z1KYcoTJus~P^zNx8OBt#8f~*R&@$np0{wX0mbUTTl4m+7*Px(mL?ax+ghyye!-rg= ze0ZV_efRB%u!;mMWtb)x!0tqKHbkmKUqtqS&}*)tNQ#dFO|0el<3K@_)cyJEd#8KN zUMq>^Vj2ylBNEcp%IFS5;q>~BqC;hJoEP(}zs|)>(aTVy-y`UJT=3<^iMGG=4!eVr zPn-o(Zua9()*o0_{%jV0;Rk2Vhlj^Ikz7;Oni96Rfcz8i4BJxPn470q|38`b{jRE` zy&^O1l!YfCKkJbQ7WYgu=$eY|VD!KT!wQ-G|7flsq;)$$mwSENst}~bRvd#>pq6pd z#Tt%br8tK$_wAxPMM8HHL3Z;+>cy_yavA!?3}+2#{o#r3`BC*j9DO=4`YcMJQqwyr zAB6a3|2z5jImd2|We*sKzBY^C-C#29CmwJ+Yp9oIiWhbdX}`N-(C@h^zPz75bsRcJ zAs`RrhkGAvt`Raj8`4pIe)^A~Sol%kN<~_VA}E`tl+T;(*IYL5^~g9JJ$9b6x8Pw7 zEY~zVO@mJlb0~I-{Y(`w_@<)heC*?2A+0kWFAPq>K%cUbYKraez(M@Y;6Yg24U^yz zIGZg*ZQMt_0m8Q~(1|dwA5pto8RVrHw0&o<(pDGz8EMpa^^sv~&SB_8JGOIauaoe* z2JBgf>ej2;;6>X>Q;uqaYVRT-F}8<@$NVF5)l7qe7*r}Rd8R+D2K7|UvCYX)*6qNv zO&1>ek_K!mOPMx@whYo9W_INkqenIf&#G1zKv2;QFxl^ zLA2k1+QDSEgie64nTTHzOxCVIOcrS9b#|VB6s3=A8}liDlDX=#R%n|*u&_{%Otpl`2%Q^ z?`30sBdh+x>MhRB-9LH!hBRavAiOjGK#%-_{clga`!GO9KRi{tqn@bG0*e zv9z}{wD~tB(uDTVUP}IoHPLSQFq&Wv&mIhk`#G4B{gX*j!e(S>?f0OjZB})0*XT@4 z7;Sq(*04gJa-OXeM_!Wvrj`Zl7Ns<7y(MU)TggJYn;Dn2x#9D46H9R;Gk(cq)_YA( zP7%dg@eXP3j?-Vu_v5AaBnxo8z~RT`hx{AiP3muG6SRy`A)H$1l>DJ;4W9!waSKv@ z%S4nVsNze5I0gI1>jw9*m11-TPymFLArigfK1;er7eJkv!2FK}bD!{hdXlLevRaEf z+=3(g?G?Q&Dhsq}1)$dVl*X1KOuCNsmnlTX z>||WTA?v*fwJel*!U{!6!a;p_nkTg#{K)*|?_#}12eI<-D#kh#x*&l?J8~Nv1`aVA z7vwF>-^D>4n^^U_k!n@U4xDPJ)bsV3bj>ir%@zaeKsAhY2v9l}ks+PV3 zwPr+kPnm)EOV~zX5jdBENXYbsg*(lDL-~34VZ8Owhr46CK$02uEMO3yKz}a!7TEy0zaJHe|G8V^^3XOIkGq}U} zhK%Gyg}DxzohMazCWZ+4!0|qy`F?kLTAMUETowitZg(&y2{51a_b`6jX`?Ueyb#a( zH~|ctf~&K>4XaerjRYZtQe&g*8ej4O{K@JlPjAx>0)DzM`g+aqvzYVBOrz7kknLv8M4i!O19I_g-IQjUnVo_5% z_m1xk^hcnvot2a>a3>_9!{>3~y0AH%JQ|hy=iOq9eJQHOL?Q{_@3x? zE$oUDslHH;tr0wNSO9u`m&Xo!~&$dnp( z=}|O?y2u@Q0QQy*`{2rNrUeAKMzZAH17xwJZwn^BQZchzbUiB(7^FPDGW_*N>~9YX z$NQl1H3=f!lJ{jaV=)Jy_FBOPIl9j@%lJ5B?Xh3+KWcuiJvJp9yj;>{qK%W^Xn8~S zV*b9teFgRAuwOW0F0hSFvbD+_kCNo0oHUaY`Xu>q1o9(p>|m?yV#xSaHIOTL{h2d( z7gyTQ`&%F4R=wb|SP3CcRXt8|+(Se0Z-_In7k>8!?t}NsU)fg9Kt0PD$UM@K2LE7c z4EiZPDm`7Y^8Onv7u=0(Bt7nJPotlUk;QdfDq5;#m)G?tHrs)*nH0Xhi?QgjOH?R3 z9Hz-5-*%HRwzbryCj+rL;dFnsX}1u>33hh&G>+a`d9$BOll`yIYMRm(KVE6q7y`uv zHj?@=uo?a#uA5WBD?0MWF!7CFt>){;n3kQh=I?9;eFac@d0LcCzT1ByGU>GiVBc+# ze`HSLABjKLzN`W^(RNf1HdbktOGa+YE1~7mrnxh*peswUs!)!Jj*KOs zW$QdHLc$RJ)aoiRu-5lAA&(E2)ikbmiBcXjAdWu-qYRF3v~dGBlherZm|Qf`A}3yp z0++fBuh^EK89a*H&&J_u{-b6OFZYciH!{Zpr%BcU1P;C82{9`W{;6qA{S46dV?2M$ zz@S%~P>!yYSN0_kSXJ+LOaB;2RT{RQ^x(mDv5vUkj1woX+zrSMuH08MVjfuf0gqgx zkEJ|q5kK!M&m`D7#0^hdx5upPJ%5MrhpBjFCe{i6;3BTnK9-I>NR()YJE_KsZMVlE-JI=!l+2HnAG{rFOgb-sTWPSYG|#qN4fE2?|57uE!NCNHE}CG# zqd>8gk-k2fQ)<;%j(UX7(O?FqBHE+dZKAsvBKhk=`#2;{vk=Aj z2D>fkV-k%;L=5FxTdtg@`Ja@!X-9e(OsZ|YTSIrj?mvW)hoM2~;o+;C3|kv*sZ*(^ z-Q$&x)`G};0gY|;IXVov&9THoSso!BL5kp40nb4IJ1jw*rT<<;`$yq!yU))Da zYsztSFqQuOWAjPU(k)2V#ycCGr#Fq$g45hoiOKqN$Ai#f(@wbDr`NeJrBJmG_~zwb z7CPTK=y;OTMFq>-X?UvV&Laiz%uy5h*<&UlR7|BW+UW~pcP>ynm9+&N_f%=yCbN{f zg#Fb|Y%U~(#$&9fNm+)KuT32@8Ss)AHx~M|$W?Aai*h(FVF(Y}GTU~{7X0{7bN+8Q z-rPZ{ZM;-h3lO@YsY0*x97V-M#}t>9;F zUVvYVt^I&T{R%W8T-cfWZL5w%bD>J3a89H-Rl!}jhI8vyuq zA#LGDUm;z9-nmgYV#FXX#8sX@(w6us?Zx7s>yd$*)ICD>Q>*j|Bn}=*ctq@`NVm{a zCbc?mPGYpG(WFNT*{M^l@-fOTK<;8Hg`;l9ORm; z;JlH*N6t6zUp)r9zHh|&)e`Gs_I?IinZm zi~{oa*S(aZz+PN87oDk;;*oSwvOf_CfneAsICOor`fifTK=ug#RA=4{zKoAL=as(p zh2A_gOR+5<7wLEA<LKLz`5yN9u8I2$ZTQMvf3vO9E>eGA`!F30S#c3N zo&%mV^nu2^*UrGOSH@CI%Crr%Y;RNRn; zg>ob9yzApf&@FJnk*&1oSav=I3FWc5uEI0oWl@eDv08K{~?K}>johNsLU zKevwD{%h-@P3CbDQtahTe<)Uw+m@^AXG9vzyIL1*NSG&Ynkj!`?r= z%)xn|*I&57m4XAB;EYW0E3g${8E&;Ye+YR#RL0*-d#%}Y!gFw*dSbDiYFL;A-7EXS2P}@B`XG2|pO3^WlEp02(3*nk zjI-#7$s&}&95LbJp_S0KG~d^74t{*Fx&kjeNJQdhp|5yYi~6Rm2H6YJwJ;NIVEtB= z`Gb)f>88+#kc^-uN5eX$S!#@Ug4Vp6`u6H`l8HnHC>2c=P*v-58%UVap&PB+aq?wv}&9W?| z-&3UlP4`?v_gJFZsLOSn?o`zV>gS-|^x$8E-3#HR4xw+hfth8t<6eSGet*EjYe>5t}} z|IB+FsO|f?A%u6(f$d=PEpkj2JvfWdg{82bT|$ptseNG#HUBD;?XOHM14P|&;B6vZ z?;Qn<9|r)T(nn4nQA!zDaqL5;sy@f(Sya5$5glynK!|rIJ?Ylt8E_kCN=nnhZ8Z0b zQD{*$wVdmC>7i&|%v*G(t8K#AP7i3#uAs#P6zjtV6>FZVtsbWNwSMZOe(Dp(6Y;$g z04W0#)DZPyvX_!=!S={`d%*H}?0Ixsc;GoPVa4%X!1ODEDPJnfGYdBNPX!1pK(94| z>|&{IUHBWBdjm3ka&vt5y2Q_dsN0xl8l#}r4c)!1m&+ac(o(7D5HG96)z_}#CzrE`X-ulc`XO{|K5oAU3BHcA`28=w znfOZGWuGQ_?U%W_9B@KdT`rTLJ?dXD&F$7Ofe2DPX87K-i(C+FsM02}uPu~+28@ih zp5{?h$;NugT!O^VNcj+~T|-q0l83DJNVnFQL;2CoU!DEpr^(e%gH^%HFr-z3D}0-U z5tmqx(PlGM5oeM3zG=VSh`^``goD4ad33N=T&Yh~`ptD@vzlY6L1O0|d-$g}<|x{) zz_JhdJv;ojWfVV6IOR*vqI@GRTBxLMb9uUhl~@Zmrsu1CJ2d&X1X}00Pno7i-OFA& zPjx@cH@p$MtnY_bm&l++;xCad1@NM36&hP0 zYs$s-$WRj~lEn0%>3NU7^)G;{p_87+V#M`g6`#5r-B09CY@dcLb2AAc6phtp7;96r z>9zNbov&v;z;P_(%QfiFm2m6r;o!sP?h++KVpM$slh!cMweO%Dr!A8D^Y2&9 zVN1r$;cP`8*8CKDxeH8=SEAb@QT@#NUGT! z3&W+tw-h>6T?`SSQ%Imjew?k7ETzm*5l!l$zu!{M%}+I%Df!x{2#BZ-f;AuWK-+Oi z3f5ust}+y=k0P6|_CTtH(beu4u zi#y7kO7vw%JY%IFxG8n2%ub>upLw$hp+Yc`9<)Um^7JGu8g0)tsCZNglwIXi+6~P> zX^v?hXsX{(1cy*iJ3sFQ8hA(zNoEynPv3jm4l*lmrY~o6c1&<@y4=HwtJC>J6X>UZ zTSkq0h$Ve!hP2?|(Tw*-v5{3Ul^Efns(fiqOgoL=Yrro&TvUhv%`i!1KT`Zkq$a=P z0p8G(L;ZafKZheXl{O5^JYDXg7!_skG)MgB3Z%J?)u{L^pDaIf-Ghj`a4m7$jnS~U zjcLeMm*+h)fu%IDO3LU@9ioz_6~0bLNl^|{yK>#w)!_i)bv|<}m9#t}0;-wIl~YPw zX;{TrqaBVa0UY-V&ljAw+Hp^s_H?hc?08}#{RF&ZXzB5Y$Fyq#Q|~4}efSvXM#p8o zESxNY%FFLt;TReQnf({DO|rlUsgo7n51Qgz-08Jxhcd5qSAhDK>)Rs{tpB1ow~>6z zmN2(NA=KPyXVRvrQ>TFX_4?$j78F(PEY|rMp+$`A%ckH6ikpmjgJtOj24>9y%hOs| zCo3z5NF#2+bnx~OOPB02e1K&Y06M$gxfLmeep4)sn2w0~ny$#w@H-l~{Be=!Qx1p@ ztsye26UnVML7Gye9S9$+Gn&>dPBu*9>PIA!7nMV}iGKY5ehK5V$S!3RvFn+|X*xHZvIy4V4 zNaub*Nur8o)rimP9%A>Crwz(2miCUe5n?zE3XRqSyCNbbIfmEwhHrL0`Xs(UlYitW zZstfTIMQgBq-wG0kw+-O>1;u)=@)Jni!jTc`^{y^j_gtEw8-l?^w_cOoHae4N2P z;;lIL%m&6Pe8a1ukNix|1JB9w%gj#W4v?OaasZ+)Ffg`Y5SG#Vczi-R)tlaNpceZi zjIf2~0vq?pt~_a^VHgx_oXO^|$sfsR(o;4xq13>jT-k7UA7mpnwpMR?DdJm6$f8k1 z9z}~dwskMXKFv1fWC25-#I0CnsM)O9$YVxX<5+P_VHk3vF117L#u2%LG;RZ=N`Coq#mv0HT_u$p(E|xq7)eS~<)f4| z%>_nQA6w24mla}4PK_u71~iY_pLp9VLE7Z)c>cyWDxFp;ZfJ>JEmkO73aQsfT$(1g z*!ox)fD1iqr6>^RiQr;6gKL;}`muBgm@zXemFAt#I@|NEOD>eE(!aR|WaQbPRt>N^ z38aJ_`ZV$ocf|F}5FLGMher$Vq$8&-&e3R-`TXNDxCIZ> zbbO;0SVSDYvhq%XV24Prfn$cq(u^IE2}?!^Tu^q)ED_PkGzrZ{5#A|-I(Ltp2@NNS z1uuIwg|#72aw_|FZ@p(SdC%|@=x;y&0!kSZ2``>KqyBjPGi5#Vn*h*C;LQ(*^Q!~6NVm|~nvaJ=rABe;dg#uNpB&{s#HWwhb@nFTzxbD-;Ki z_()_Wuc##Pt#48jEp3DH$@9x1wug3OheNE81*(7Lg>i+!H4iAM+40j-;RrvK6_~n) zNZeFK2@jYb7HyBUV}zBR(6VVJRK|c=S`k4;GgifRXl}C9=X22xqX5Gbrxe5+#tSAa zB&)mwQ!R8!FEv{x@RMJ>o1W9g3~vgxg&h&AIeY;rB1o2JO}c7Q8KJAOyv`TG;U`lq zT5IoM6WP}w*JE6aA}f(65`z!ZQH6vQa(BL}~w7sWeWh3mg@*zkcue_cD*Ym_Ts?qVGnLQjUdP zF*Bojg{hw?y$A|DX`8;F9&Lj|FM8?fq$Xzjbal-6x>w3mdW5mdXxBMjF>Pl&-dY*K zGfdQ%x52`@w4p;c5KSr>_>zJ1`f2Vf_oz@V*}JqqHjUlQoYj%isRe*fZHPIUrih8-eu)c%{v zvSR7ibs4}|;!^9MFE;(k73 zj5wEmOtz*lI=piHeunCZTypiJa@}LoLm$0B2 zj}g}thZJve$|EfD#=D^RFGO%}(tTrZq1KJ6qX@3#oVX1@bTn~fks_6(TnVJWR*~~# zE6Xw+`2sM2)RmOXJQJ{R7Opc~IGhz3pGvpVVj+}r9Nn(oS*&6uFlTx_HWToQrw{d- zfH|+v#M)UVdo{J*L2(n}{N0O7d!JYwsf(D{1HG^9O|c6==HC?`_ibF%l3PlaTthJd zr}qOoF;0(&xv<1;wJgn&wURP>mca$vqN2*AGpzjp%G-(F4YJTAzf?pmawbn~eA5oTLMMg>6-=3B=|hlubXqH>^z0C7&ADX|W%?I|mVDsSje?8!y$EF^Ph5iX_;&KQ?qD$^+6*M#Jrc#>AyV+<$@x%G zIhMBied~Mx+uo~i5{T`%;1y}QRGgR@17uL2R3@E1_@Odn&?Q!`sKTFOeqy2B$Qwh}p2F&x#>G2x;GlY5$E5nn~k1=SOc?M?|}Yc%e$nr5571JX<+_5C_qO zDWet~Q?s|nYva8)uflBh1Yi>sJwgl@nNC>J+zW{vA_u774&g%tUp^o?f$NX*fqTm~ z!%GiSsrO1yF&DogJoc8e0L!aC{(ct((%sZeXqk5H*q{8aCX&Z_tKWog@z|6u|eEi^m| zI>F=Pn7G2{BfETd(ET1wdD0Ep1|KB#loiZnvbJcEH=Q?OPK?8g*aQ)38trCg*~AQc z!)ueLs!iYx7K?Lb?2)yudW*hA8T>Ma`Zo@~{UY&|=m9f>(|vLWqStb)H-!6|^mBV~ z#l#WF{s3=#NIpc~#|6S~I5e&T;J*ONMu3dFy~MQ48-;Xg`N$<@y8Z#j#w zy{)aOoy))e{!L^RI^_lgkg{NjVzMFqVk+ zF8w)hDyC*|FaU-ATkWw^KCdnN7AdQtM`QfRa9If`DTy| z^eO)6)qCP*KbtEGqPYg9U7pU)<5SoDPOa9!G})zBiFx%nU&dW9EM1H11Y*u@)hM6} zC(RPO(LF9z@9^!9*<+t2?=U#dO%4lpf(%QNtxU`Qw&x}Pi%!ft;FDh@*0aAVkgr2x zLq7!AcoGg<+}0)v+BjsDf7p>>>pvy$|0>j~$#|%J{+suT$p8d|^FP*7!O+;+(A@Oj zRqM@K(B3LbOW(Pv>7I#WfrEz5XePh&)+B&9WRRedSu?;`vEa~L6FiAP6VqKVz=yOd znzdHe*VQ^Lo0U2>M4^Erq%l*kTikY*manTnFOQYpdS9kw%$O7NK6@B?2_LRIUpAem zdEeV(Yd-+rBtUX3)6abZ>Cma*96SLPbotUkvU3VH&I=hmbZ&l9yP6o`qDm9srfki; zN4Eu=K{G<&w0(~~3iEbc4ET|2;pffyU)A|jZc4lO)9{sCtivwUD7NQeq{?)BMrl_0 zD5@YU3X~|QtV=FY@mF$W#Pw5j&G>)HHui;u-Dr=xnqS8{cKTUmD3rrlnHHC1K=gh| zE#z7nV#JW3=B!t$N2k{~m|IFaJS~TF1!2lDd!id4hQet7+G#O!J5;r)j zj`vtNmsor@su95PNsIQBB6F0MF9bJFI5a(#JD$zO~$UBl#WIXzSf#(C^>oDA9JY~1eYrCj&vBsIpLJa4!x zi#^gT8L9ley;HZo{czDLCb%Nw&vFT#(oJjD+T*3nZ z#cYX2S!u?i*6qOy^#S5CL)ndQurXy-6xiX-qQYY);ynM0t#@F~B!0KGClh;O z+jwHznb@{%+qP}n#!T|WwllG9CvVqc!wA)lf|bq+(U zQLl0KDD7`;kY8xwm0Y<%pBmz99{N2d8?1N}tZxdk`8dJjaW0Y;!HT}LtG!DDQ9ubH zJWA)KCHZy+(NeVD&y_7_7j<%S!usNR;m#@7();%X?}75r3t;_MOwc2lVnG~@mLs~! zoMCN?T-$t?7#4$sv>dlhR?wzZDd)!Cn9z_SF>}k6;1!9@KnwG)X{S2$@R+sytz@mt zlPJjtkEV>*1q3~Da`q6Oc08O|VxKw>8txJypS9@1Kzs=1ZgF)_BWp%83YDVkdPH!d zj#Y${4K2Frrve@>ON|PQXcR^YdAP;ekfF3BJ{7Emwva_-(b+E|bqCDKSQZLsF+OXh zz|tr@PNe9>vjcQj$^u2(%?f$yxvRl-Fba+zf@qOJHv#&7s=lLr?p)XikE>Wn5W)@i z3>EB|?p=f&vA|k)OBWtI*t)ePJ4Bj@boVg*OH5_8ZthL~|WE>hH(;2>bZLOvpIdtZOd*!aY6$Hf_Y%Ps)Th6NOvqL^1*0vx=iQ1s1`Jq`_2B-R1Y|JI|~(OL)~kEVE&= zgYO;aV8TN=`WRNI&^M+ymkH(&F>)|S=Te?t=#dv9w==Jdg;;W1k#D;D8_j4Ak5GPI zlRDd+ZyX9$u1wmS?o6CBoE7M59m9r3fInc1f~et+>2-Vi+WJ_uvqU8`RCjQO*tQ7*`QP$>!k{ zR0|0>y@qQQ@0d{@YjK;BSdEgW$7h3G9Q57@J817tRSTKc;dH7hE6G%fvt4aKxk9ha z4!3(MF#!zx*I?GlWWs{sdrZjIt{dsUD`{rN1?ko@W0vP2!g>cmj*;UG)j`)rqVnPd zYg49`ePG_T8H!)SHS#Al2k|tTMP^pYVCKY63$ICk>i)GmDq})S+a7m~lvY2K33?l% zkjER|cVIvNO4M{?Y_~#QnI?b>%C7QPTzaWH7JYH{SZ2K-a1ZpF=6OEuGs6 zW(-=uAPrLoHOprpOx4||hL^#4e z>#o-E;^jT={V~y(4 zI6_|O)|xs;-92MgJriJo)LLUoU3);madIuICqy)co~q^8Sw6*r+(p{Eh<{Xs^%wYT z-nxjwR?qCTCW8pK0?qB6Tl1!7@P$26+cNC=DE1JU#!qO8Dl$7Sm8g@6S-n!LTex={DV+LK4!eunE?ypNw z#B)if?){i5wNRTi6)P6(nuatSHTB|){zyC-Bboq<5E`*^FlOt1P2@CH_IWHEytoY- zg|vj}L9s;4gIHpzQ%i_p<)mY@+>^Ea&2VtXXHFKFOfwXg_)z&Q`)nMhT>%%AW2yhMOzrRvp<^t$3gA(uom2zKk{4@B zm0ST$KKHx-GIF?cuF{ONHW?aK?uAjMwq>8o!0rV4fyMznIFCnHYAdj!G?7XU$q=CqI|R~_xt%!7ayOUf~8Vbyg6klHP-bJqD*bVHi*{_iK*(fKGv+n7yiw;rEB zSAGu73wil(ZXWcbAq{<-xX8~ot@GZ?k+{2;*s9&bFu+T=cQWpb-ntRE0W(2!>oAQQ zQ%Lz7QQ={bg37g>qS;rwr6T6rSQkl_QGqg-;BYy(#Zm=xL)>Y;o0wO+`J$KUTncvd41%d^a_5L7Bf^g7Cn@g zg5QnrZQ+GC&qdO(`JafcbmBFWgcSi<yC_)a*~w%Q?w~Egll!ih%L@N`vxgr752K4e6Hk zr<^Ck5IkDnh)}=l#OH|iHcfShCW|x^o=gSD#r_8tnNh920dy@zHI*jJ&HnT>-d>5a zOPzY8IIwdH5Wj(3MnmV&Ayc)cI+?jS=FeQHLQm(dQ(SZ6rv`&6v08MshAdhmM&6_d z?^DozisGL8m`>$4MT2pR{HrIQJ6HC8eX7ME1SnJ#X#BAtia!-ug(###vv-49L(6iG zDb)z0S9w3O<#Rp8PwSzOTaoA}+g(Vm+p-UN3#9G{Mfp-i38Q_f^{yNHWqHd$0332j z6vG0ijZr*d+!!!+r+rD~0kYS5(zNYM-5yY9PeV(|NH^?Tyhx&{Dr2djpw?WZ*aD~f z7t>9#=2h-aBGwuvEBdI74#D*B)~ljP78SiBEW?816h~rPuM-)&ps0fi$I>rY4`HO{ z#p)R4cn7U;t7KJcWLpIqe6z{uN9ro~)w0V~P+U^q#eB6)SfuUa@$rij24iQ9v7#0z zl$_MD1XxrRLTUuJ(Yv_?;T_@j8*d$-kpt>n`nMV7d z`jlZM)Y{I!m%j~MG@*!}Ur{?TI&3lgiJ$JmZZ=N6uQLY2pEUi+f$4_WHq~+DmlVng z8F*O`S)1T?#{;Dfs*fb|!THV%keE?BAt3%P0k3+fzdEw)LN|gWr@9VMaJR-YV4q93 zy}z5FTpqmi`9|89RtL8#DEF=^>DqNu3Xh&)pGJ?w9DzpS_TFZj zK!zs+BJP_H4v>{FCAiOQ@?$RlCEaDg=7&^P=c~a=)8&4je3-0FkS9K8DBIz##bcg6 zqhpIlFl~@&C^~aC&_k$yOp(3Z+fRtd0kc}@*UoW<<3%9z{1Xu}Jtm)Ru5pB`=kV_0 z_EZLT!wVDFOf46Y{Wkv~GbeSRSh7B=HdaG))&4G2bs>0Y!(4VF`?zz5wtGR= z_RF+mKW*o@|2FDCFKQ1+---!p$DiI7W)!;k%_;o zMW;k?H{%oO#sop=kjFU!qHl%GgNB2bMdBjZP-4h6Wt@N38CB%2-($zJH`tsK?J`Dz zyudIz9AJ^Vemh5Xe>sAw7;ptzZs|IgjojsiDwj=goi#V1YX9Jwe$c&A+gy+jx8lh? zm``^BriIH!_jrEYk8oxhZUrkeXeKG-`B6BJX+?b;y3Re%;J8S>2wCi3sf_T;N53;~ zysrX@|H$-06^)fu#>3sV*ju^#^?b#?hIW2DGnmV`@r4dC!o-^2HoUQtKOCh|>`U}q zrj?mfz;H`tN}In!)C>ZTuv|X9#IHwdxxUPv^Kq8b@CH7L-1U8g@pL?cCrhRH#cp1F z+^gZvKx-AeJ63z_QRS+3=0Tp)D-Y)_1KvDxrx{&&|6)E620hr)meA7zf#!qwlV^@$ zt%tun@0H2_=I;Cs#FR~{8(VfSmCPp9I~ozCP4xynm88are}KDK25jv|MfB4$?x>I;}B+zA1 zeqIX!AegH>+g9Iy{exznTA=(KF=xjA@O_WN{@C>YN>S)8MPmGiyB4y|LPhhBU!1r7 z-+3i$`zg+2#Cponj~Hp;{MVyh5)Op#9xiXVqitF1KAmVWg5iVqETkh@v2-tc0;7P$ z>xik2mf6ZbVNOfe^+xamu36q%y~5C2!^MxZfZ38wbC@w*AdCmvg5islFfqc4_;4g?qGtZ zgEw3(&Of>b_5G%VNsYEUEG5LMK$aUX+<@2-XYp(o5+*B=*vm_oWw*EyMo44bD_zdk z)fTPFtWC(`R`zY`b-;s{$xV5-Iz`5NyW{xHhGD_3n)lZ zhP2RBf32__1GD3s0CPc`abp@M@hnnA+(EtRG>e3Z()tQ^|QsGEXhyPo#ziE$gO23)zhH|>--D9N#5s%*^!SI>&S&B{o3YlO$S z&|z&9<}O|7wNNpZsOEg!++}$e>;(z! zjBIJ~*60PA^Phy=7uB-#C6A^rIsefyqbrfuK8Cgr$gusr6dL`&TnX&`v4#z$W%2pi zOi`ILoAqCMOr$&`7WHU?@jJyN40;y85Udi8DkM)B$KXP4u%;F0qnriA&?z7>6W^1(lu?foK~hL9JH{N4EV@s@^ z9ji5iaQT-IujZ2&-IEt*=O~Bp#SW&)cU;<*;BK^ zdJS&PZY;A!mzl%XYIWb#Zf%nT{M4uk4nO+n)&5iQV3Acr`Jc1q8IfsD*Zo1+7torr z=x5b2f`eFTWo^hOB4j741FvK>N|coX>Fp(9+Faf}bdH+k?k`2MnZ_HOuVRehM)Znz zQJN>RmnzR)2Lp(LKg!rr?yqSFcDIfAA4;EOUkgRc`!48mWkPVk07h-B^fdT+EG)jF z{`5-mlp&-cINnSXuxL)tEyh>&a=#<9rOKE+W^OAc?z5PXA;UfAoK2nN>AfKsumPZ1 zRoRAmL{~KPv$jdLMKj4Y!T)3?bku#rbiY|TTe$zIo%m0$xkXjWNl69u)76R7!qc;$ zWYAwSAW%XQnizu!JoqHmSz z)WO_j#x6Ic%xd5D3Qp}tSfj4j>i`k(gjYJ03HaaJ?+?qBm`1HAb0^6g#|Ck@c7Mz3 zvV^zs=gP3C$pRUYw9(}}(B|pH5(dI!d4D_1wkxHiW&ze@2S*qH2h?p##5wItumS8Y z=RhseGSZ`%Xd{ebx7GVY4o6-KFI$x8P8U>2dwC!cp50!(|K+?}ze`MH<7^2(*8wOJ z#|E*tlPRE+g+Nd<->XnFlZ`rP>O9|Mt&;Ye#REFvU2O;d4K?Yah1FFiTShNdVV!FD zfK&z6S;Y@cP-9ymfv=TK6asoIL#j939e{Cc6t2}4D9y^q91Hy)59ltM>$cVn(agf$ zo1BdbdxwRmCISDlmEu)(Ri#<$V1xc;j}klAu6Y-i*EjQfW;xW;$aHCw5%~ZJp|gEB z&!_c?E7^_}n{d^K8Rxq}H6~1RiHA0ssw^k!;Z>QP>beJ}W?l*Zd5`L@MJ z1Qxmn$gp-1KvK^mlbUCCG|%WS=;0Ae@1VAhngcIBbuzf+A2*6&#%|y(<>A6hJK&<9 zr7F{;!Za$)bY9rKRJa5)%=R!H9m%Sd*hFc)aGzuLFov?$L2|6N#7I`xv|Ls(HR;oT zkA)`2V^?18#CC@nQZ-F=kR$^>rrPG_CdKY`=!&Ke!`n?|ICwE!<<-1gx+X7_n4GnT zjzXo|s$YZ&&fTuR>p$U!dUA;n5@B0btjCyRL@qB(oR=wkkrqOtrE~oS9dDF z11&73)kC?How8pnIX^avY*GqNJJYnPL%e%!3O!g!09Ir7(JWLQ7=u3ECs zKERy91QpKJtpx;jfgSMecl{3u{t}6PSqEP{ z_ViN)^apZr?1(=CJ&5Y$|i9&v_WOsn$m#AMU~3DtKG3fA&k$MBpp%W;Ou@`=T;jh~93p z=jMKv@(x+?`=4SQ^0-v53i0EI%Qt9)_ur!{B@=5CBWJt+GYGV|bFy$Y`L7|Ms+Jqd zw=m1v&Xa=z0$PKm1kH(Ll*U@jQK(rWm4yT~fhFP6^A_ATOejoAKdA%Jz|rU_b=@CA z#0>zj=xMZhI-f9l@33^s$Qx#+Tryd(X<_@>t4;L(2Xvkyh{NXrtA}iRx9p_5 zr!xe{#tc@77^h>XZs|vZBb(>cl%EeRtjKx0h_RR_H6#;CP9r)Cm5yCbVLnko&aA{} zbmUVFT7=8hg}EHoWPw$uFyz%$7f4OPir8-grz(1isnFDwjfWN%ZifS<09^@32dN=u zGhhbJc|b{sQY`7LQ3_kNr}I`3Hni>w=`dHYAt5!ROc*emei-7#b_)-nJ2Qd70V;Bm z?$Eo@c3Ks^f3`AK(+Q8`Qu#)92oK1f8|o{;L5`DhD#xmYEw&nm6b=V2WORvkho_bQ zR^$R4g~utk7k(z~DtnD*v6BiL~)=2%OC z)vZXER<;+9*)oqL)H)QWn>eHIt{C3^xA4HtUG4P94rQo|U zrbmy@E?xYcw2gyy7wTWWtD?+JKfZ&#_23Ws`Rzjdyr5hd!VsgMu}&o@&MZ~QQ!tFF z<{T0!?lw;?U<5U+v{_>7Ip*<*XddFN`LFJp z3y?u#47*(1u_-r$VE!(ww^d~kYP(;uL9~jwEOze+x8&iF;%877k>20o>1`>y!nCpQ zV!s(TeG}qOFX|nF1BY|iKzW+XM&(96`7_>aw1W)1aZt_=-7Ue!IDnI|f&@#$PQr4{ zm+e)nkosaUMzK`Y;J@J5-d-)Mb-)SIrUOmE~V1{>SRGx ztzf3lo~u>}C+2dop#xlUGQfvz&d@cKpaA^Jl)6a z_Mg<*+K5m6NZl{l<}jWo1#VDAtXDgh6lPRDsQ_gbK_K=%VS;nn z0Tl2z=%Fnb6Zj!F!Z*&t!O_-%Gl=$_3xmV^gOWLNDk^yc*5}+k3q$BwqhS`=((% z?u$Gv25(uZkwZ~?1pit$MSGN7)uQL#6vru zCw7H9fW$34m*K?Z@Di_EiUoplnB!j&?vduot;6ii_tiJ9fAUA}wOd%|`aMhxgbu{# z%kYEb8C=`&oS>TAsY*5tnoX|^nobWq(029%i}NKM`58uJx?3bj!_|?99Q0@DG}=cAnFvTkq4;*Hp(L&6Zv#*fyPK6(U_btGn|;LGCXO zi)^M&WHHCev+bKC(+T2*r-3AgsGHZDePMX|f>_`f;iBV87SI>oJ@0o2| ze}c|X6)sgHc!Na;5-Zo3Oz6Xuv0wK}V&t}@HAqHF6Ua+&fICBkP3)zmZGP)wAE^D8 zy7>lt8L+y^>OiC6fw5-i(zu>rEkv$2Z^|v4lUbc zU8e#ic&pZSgrafpvs|WtOwVyZF^DA8VIY7V&^{4rd+H1Wu5uU>*95ckk zxl}vacU*+G=X<|R2j9b`8^&w$>d;y5JG4(~&=cf~C(e3Kw$c4=$dJHL7A>M@O3iLX zUK7B~$ZRvHByqFHhX=YcJ?cxGlEIpOlY?-&ARBaBf4}3OJg*;O81g$-YH72`2nq-W z$i+*S(Q>Nnj`ehipZRANf?S%=B2-a~#boUwe3-!^#X?b3fKs9D$h468u%nB+#87Dj zC(h9$B`u?Qm|WcLI`(^iB8Xz(Pf*F&8ftY7!P7}Q0ZMjjg9S62@Uc0+r^`^BmqYXq zdi;GE(VCk1iozug@fLz15^OlUVI-RU8W;;ba-#_c!v(XNXp0GCt~zB;524uvKYOMu zjsgS8VU}GJ4n;BXYh$zB?9HsTtJ+CgsQ&0!u=L)k-(@JcML6qHCvy9Vj1u{^ACCf5 z@pB17Mdz<5&_G2|Wy(D4EY((uDxgrTPk&1_1J#Od2ru%$JlB-J%DsB2d<&Pc?m=tp z4FpdtySaN&EX8%HBUHw?Cb`xn8*{eU*`3y2MXm6}Xyam%w4dbgd>~&h)jT#^lZMe4 zoTHS*-A;{rR%qMVGZIy>SlGvTVe|> zpchtd5fRltg5?{OX9>0sW9nSeb1K#zzqOI*%(%k|;|bcqYQgad%wd#LC2+_NcMZuN z^&1Yp{5=it_>L^I-E(!s<%G&@#1nS}ej2pwY?kTY5uE)q`4|L;YdneB5{@`95@t{~ zHj}mwyG0Y$^q_?T8^bQ|-P>90sgjyvJQ--v1k2a3;LCvJu;8gMx$QJCBR&apy2;v4 zWg)H_Nt%*_GB6H5(Ud<*Adnph9SA($UsyB_-uZh>M zD*Xd0H=cl7mUrys7{_Hy3|)RBT(eX}`rXEXqh)A-$w_%EUJ#lz*N_QN&DmRJN*}(G z8X?Kvpr%(r8;Own?e_XtAwhSgq2fp$99?j0;9Y3h$y7*vUZ;UURlY2?{X642|b$cox z!hRrdSByv9BHn?3{PGxV4?;3_91mJl4~G6p|5N8m3YiI63b491F1$Y6#)vap7QU4I z$dM6^4kiz;{vPQ$MWp>)OFp625}pK0`IwXVTKv#a-%yDT zuEC_k-?oqK8CXeh+ayEs)TX3EgQGlc7`#i)+B(f+PRTgQMeJE)9upt=4yu(a_wroVeb42GKE-_RcnY!?m;c;;oBp zZ1yG6Lnah^I|{8C(_5>NBP(R&o3rsoib`HJ&D*EDlg8+GK7(gUZDKLeadVF1irq1C zUcJs@xK@J)$kl_lky22V(K~E@DJt?Bys{Y0RTZ)g@tfZ^WIyQgN?CR`n{ttt>v+IO zd`k!RiHfn*O3R#A7!M|nny-7QnfH^tgx$%dOLg6H|GTF2MZ==Ud)U^a+O=y>iT7WO z7Mg3ajaH_Rl|Ajbw80$}lZUUPhvm{`tB4e}OY0YU6&sbbF>IOfX|JIuz0E*qqsQms zS;gwIRGr|g_3_);osoXB_R8qcBQ^?X{c%8Ll*)xuXjNklcMgs(Jf5W6rFt$bU#m5R zb=3#n)J`KEK@pP@oNrNuHo56#9PGb3YWF`%tqpHt>Pz)hJ*vHkmog=^hla&rGFDuy z84q&|MUTC>1UiPHCg{_gsDIs-7Uqe1kLP>1;AcL6Ts+NT4%=^E?*xUpg^fsAE9`yO z2;9-K{Bv`!w==G8YMPgNLSaMKgCp2rmw!xM4^cA-FueV7rXGG}oA5T4$>piO&sR9l z2M5700?Vk8zF>j)0DdeIGHtb$Gqf6Nv5LKXLU#%;UzdgqH;jzwS{q(vc1cAfan_vn z>LENNuF=RccCsQ_)=mMqi=mJ!F_q?Kd6BDlgSCJAR7|n%?@mL+932hLu)&fnF?F;k z6LyV>aSV4L{L^nHm?ior;deBQWP^m6H;Z_j#eKW1T|RrNPjPcam@;{zdeI91ds9hM z-N*H0*K3+b0?n1qC>fX!Lk0^o3_Ex%cBFdAtWxk&6_ZL4JBqD(MzWSjE++T$>%q@$ zL3v-PAH?+2+6#a00OnLvK0JCctb-kSM`yFj+2dE7xk7j~yEm4)9uT&lu+3rg{{0vQ zGTH(_k7swxZG&qbY+%CO%CA_u?bo~Z6WsYu3wZuIVP5{_MloVL_~{_;GMfR_>b?FS zLLR-%sr$=NJY#1yVp`=12X%W5wskB`WZ-zqcWcC~tJuELWKd=1#?Una&rRmh6GbGQ z;dJbD!gtvr!#)05FCp}e;kFo;u>AD7ikh{mUhMUzI7p^c+}*g1ITqB;6-5UoFiA;;ueIPB(t+`=piDP3HjEYRH@4EiPt zTg`#ZV8nb@$I&~3b@4u=>7$)YdvQ9z_RR6mp&;?FX+x=fMV*A*0GP;duOoY75X2Pw z1?=>>wr~mdRC{D~k-0%czJciAqV+?EQ(7iq-jLC(2Tw;ZJLsLe)aYZ(dt@2T;Y}=A zw|Eb4RP5X!fl5?2@;l>doBtDFr)+G=+w!7pG1Cm>ioeYJ4(!M!5AOkKN;n_>)J^TZ zEy>7_ow)m7goZN_aQTsWWz_3(SEp^;;=6F(frc-5{khc-Qg=6+l2SViH) z2cKQz4(27*(s4f(@lqozioXRV9J>BNu3qycmHuX$bViu(wbElfMQcUQn zE<74jnq^zEOR7|`(~md8Bz(f}>~T>Cy5gX4-5{J6Ua)2681oi)Vj=2gLNOPA;2TYV zzwg|Mij=Eq#-tDm3J{gOL(d?rRs4gLWMHXN6EH9jI87zayi~lkYdPRI4GPXpBiKA7 zeDfva&Mu=FTZ(FQ>5Q>eV{$18(XvQbR-v~nIBKasxzc~}@VPXebR{u6a|)*lF6|)o zc<KV5G=jWk^ zX#2!V0`gRq^%Pr^zZ`-;TXhr2*mLJK&*zRdc#`+VEmDP1mtL8-LRn+@Pdd)PHS0^! zoLhAx(#~K%13#&tw^IMLHtGy~T8YphLm2kMjIr=_GGU~qBj)nS&+vCSXbYc^(2npg z#~pC*G@PR+ZFPx((86%|NX80QBy!?sWt%9D0k6R+nQ`LJ=|Rf^FiGkIT*{m2(!y*R z56*MbUYeB+PNPKxzxD*}y8(eOd6R)oN#et@WoFobD9}jD_{&tRKVA`+JF~*sp>a%S zL|l_EEJBbc^_g*%M@SFda=a7^cj<**@%Y3*e7rVsa+z5PQRbq8$au&98v335AKK%c4C<8-l6VdK*H4ra!>{i0(t)nx}A`P32w2#*4I}G zuuXz{DfPosYJZwXU84|PccrjlcOmYA#U}|Zy!n5z7JL@He^FJb=C;fPK6%2K^3Wfw z)alP5*oJea&GRri$aQDfjybH^d*)H_hRwa}Z4AjU$ZJuI`kUHb|AB z1Rj!LSvBSi_9al6a2dJssOU5*Z^3_#Skt_E*3PCP>=$g2m`89Z(<%FnDTjDuDqe~8 zM<5!P)!+`soDCN7xjU`&V-0q$3JxK~;gaupC+#xYOGlY&kEw0E`v!)D{ZJ6Qo#n^+-Wre`-lewcZaX zIIkfIX1Qq#9olT|TrwgSPoc!mYBtiJNHACQk|wykZ8?=Gjbm8wBTQ9{t1T>!p5miT zx1zfJ>)8z|F7^6|gK@QrF||j5auc zH@Q{D9hX;Rf6G0h$=z5O!h8@k4D1T)>>S4vJZBacdG1F%p(scC*gwad5S~U61RfYr z?soALkn_f%ruNwf<;rjY@2!Z8QySq4n$(MQV9uJ$_!C{(sSxw4epsF1U;?qC3wnG) z=i==N<9;rh*hf7&{hohfCel0p@E(LP8CTXK%~_`$J_2m?=|pBVParwQ?fPhP)~5}P z41-$bdH+IxGnZdDg1=>)W>^3WGxhYnGxsTUFOAwSGYpEmWn|tN~k> zs={h)E-Wr=YQ~=Y{$=DyvnBob_~0AJ$$0X)e9PH6c^bkeDJ;oVB=DdVE|Sh^q|6%a zVoYYRTsO>0Z^R*<{q2aQB$i*eEUr8DUE!iccOo64CA2Qo0$Olfx^{^dQOr2PQe?-0 z&t5p1JCZ+(kA)ddih6j8J685H2F4{*&{v-tf>(@q6%{9zJcdI_l^83MA_KFYxk9~2 zgKM&slZp;FI`V_`6c!#+I+t}*`m^;u#*={I=+qf*(&&~fdLO2UpM~c~;b6e%-@++x zF4-c*9Q2tRl!cRx_Q7M&T(_hIYiua8d{Kq`LK|EGK%gF0ij{MrEQNxC05}8Whj|SKx!*HymZLmtQGEJ@wDklc?Q1EsYbc_>c z7(i&P@XgAvb`iLGfPMKIKKA#@73gBMLRLFD?AdyxOBhjLgCIeUYD$E@hSrKX><1)4Q3@+Za2tgrd9Itw|4Od!D$>$sB6nCnT@z00se*g2jS0Buy2 zab{4u+m{H79NDuI>+Qj?BI(ImF1EqKh#kRWjtr5IESo{k?|Y7)gZOF4Te^BFdyF;{ zFN;$VM+JGANP8Ym#j5#H4jL-^5j>H{i@9bg5Zf#7XhYheK`I$qh`cfwpj!58bJU%* z6S~qwUZh_iR!KY`zzaN!8%UWS81k150`oc`CkCZt*z^f^FD?CREEvmX8vGhBi%|?W z7ik|y-UBA%QIQtvP=THf>#`=xJyAG=A|XjBbKhVSF5Rgf{Y+GfqOGiDojH5E&jOqj=hEQxITK)TD>(AoN?~SN-^fbC?)+$WU3v?J&`rVWGH!ehZ2W zdJ5ob?!>keG6V_zgmo7>_=i%56u9f1F1pTn$uvX3>Smm#axNu6?)eyGtz`Ddf zlQ8C&5%fRW`ShMXj)*}eNkf}r4)=tbhyrF5o-=Qu(&)fnnJNgR~a>}p0r-*F7U#JxjuQ8 zxj3k*>sh6Ho0#6B`FY}|%V2gD4(8;I^AaJ z<_S*kNnRO^b!J!Qy?v$3)Vz!k=Xp?gA~JQYxZFi4Et<=X1?@g0sHs!jOr(l1l=?*t zTL9&%e4YWisbX(qsOMhbyJlP;+NIQZu2QYT{RyApNRP93_m%md$dW`WATQV-nnhc` z7v57XE*JV4Q#iGvtlPz>RqnZSeCZeIaZ+5A9GhrA6w3H#^Avue0C7-M7X=bK(R^b* z6CJyE!RD815e7w_Vm`TM6>9iAlXTHtzKAqGP+W+%oxQ7=hH&ofR(-6zmm7;Kx$KHA zTiKsjRNGve7h2g}n_F4jo)((hp#E`l^+;W1+(2Mu7L9TUl~|s-J4sgOpZSzORX2zq z!8r`mx-l?s8&801P0ev?K^#^02B`PXtVGSROf{G}@wgT^>k%BkI!~F*Ou~i$=Tde< zlVwUdWwC7FD61WOBeAPz)>gZFrZE#sOC5;j8D;leuENQM*KK}0Cwx%mXeTd)m)4T` z&XJbgVa-1|=1wYeb|j^hWLhv&rZW>Cf-0l(dvu5#b$G+kBK@2|Sj;a=2P<_9-E7jT z&U)46OFgpk&YmjiCM;>>u9kM2>miZ2=A*Oc=%ldE7&_p}8=)XF#qA`-fIBM%x(GBy zNOzrZfgIX{lM5g)J#=E3C3Yw^Y27V^N)bSew@V&b)&>wnyK4_E4i(waAC7`svcN*0 zRCzZ}!FnJ`D)><{{Z(cZosca(Gr?7A97l1PlS%-Wa`w$bb+DMZ81bJW7e3A-zdswY z$rXDpSE;wTEQ(^)!<3lUtV6+J z8$Y0vI0W}i?n9=p>+9&^Nf@_!p^aG{&HuB+i=iaBf4k}(J9KpFojcKDn_ppsU`Vn+ zp+b|!ZvT5P#_>D4Z0{>2+Li`fdJDoP^|WR{PPxMjmAPE@K(S*p8pCJYEM27%oBS>g zn$jvk^knpZ8Z(b<@P~AX9kXC}6Lk_d^Rf~*k~2$-3w!<*DWcH&KBC(bssb6np+bS3gkoI{Vk+ZpssOt1GdE)>Xmaly)cUFN)4+1c0E zDI%2lHGa%fh0`@<$5%D%3%RR|kVEF8tVB&{-bs?BU%b6*iN_|FAdYOtkH97-Dx5q=XWl%e@CJBXD4vEsgWW97xk`|MVrgo;1(NglQGPr5435RYm_&WKi1_{@Aw{+Ix$) zr(@NAE#thH%&^oJ;j(26(Y{3HqtRc0Mk48us8 z&9DrC_xOfWO)3)VbSLu*qUh$_r-QK;LVwdlxL&ry1^xQ53fY(1)nk(3O6%NE%x3=R zk-QuXPE}TA@lDO8n{uO};)Nlu0;A5Ok@XCntlbGW3dOQR#6`@!MUvFr6fALnh`|rC z6Fp3G74rZIlu)UFEfMTkk@rQ3 z0+E>AJ$UCsEB3jo{c=!Lf~-E(1U3$e^>Rr_AccZ( zfCY^=zi6}y1J933KXuh4%a{New?5^XCo!V^m#d@e3@RnUosJsWY)^6PQz`bjqJq>C zt5lcFCkaa9ZRHh+U?x!uSrfsp1I+UcMlP3)A<|K?uMeJA?Gr*(=lji=zrS)B6$I>r zxBMRlCzfk;-7*Z6kFRZFwhKUA^eZ;wU!d@g(`=7GxS<5}J!n&#I%eW7Tc8}}0}Qtx!O zpz?aT`nBjOp-D0QCGzYflg9!<|Aj+cK|h9_JEd*b0ZJSf&S5V~T-M+^y zt6ab()&0b`N+F;|*hbl~TCOLX8T8BZoBmC2e%uf|ns1U-MNhQ2QTe?KLZYyQ9%X}v z?aV~b5Db$(A(`k#$Zt&34VY7*)T4>Rvzrdg`&NA4=a0jar?=beD#62fxFyFqVsI{k1@SZ+bbEf?04?@Mpg1C4PGf8dcO_*Gh5+vBVFjkX-T zr4PglTFgA!29HqfZHoU3X08|&$`=v<;gSL&HnBDC7QeJmff_n?rlH7LsNRu41?=de z=v4oKPZN{pcUf)KhTPSDX#EDW7kdjLdN7jJdH-$}Ct+2BJ(%CKYCvTb*`Ze6|(;sSZ;GOGpu2|3Z0LxX+h=4 z7B9NFOBiSg#0(*Vj&9Zf+v;48C7z!GAiSZj-=YO=l^NumSrd$m<+yflfj`q&gdIeo z(;&e7rEg8@{(PnzyQP@daN*F_)NfRTvDlx*VeN!jxI~v1ZR&%G^Hwli$J8O6(@VVE zp&Ki*g*YRBPU@w7Z{()R<`*J3(@mpV=n_Bf%>RP0LfPSv z%o|uLkDTN&O14${Wu$_ zY9rwWUvFGD_N9^KHwfh;E8-Q5j-O923FrCobfGv$dyZdHSB&$h4H-urW>7Wi1Vt9e zs@x-uslMlRlZHq%v7q_AHc#x%V6N)~pE;Dr{m4WnnnK-d4J>A1{W#D;9$ouD#u&2KE|Lf~5pdv}qE>PUv zoyOhW8*kj*-Q8UpcXxMpZKQ$1t#Nm{afim`)$H#4vpa9zi&J?ri&OXJ7eOXu#TVbb z-aB0`%A-*E~J&g-VBr!hvCxngFBPnwYC@ZpX=^>o&e+X%hc3BCCVy;U=Ik*|N` zqdq(2nk&)HN*pCf6;JCV2@GUXY;C$_eOnjjU1~wQggH#HjBi(qyNO}RlWM_-L!6gu zK_B0gdYFCdkd8Y);6K$4^q(oqo3a6Iowqw#?Z%psS4_0H)i(U)15e9Xm||9#F(vLj zMFoz*SxhQf5!O~8GuDe zT;|=IF9iMFeD>=r$Yrp?;au&j*E?9zf!MdS?-7itpo|(Co`+puj>~MH6OE zn<5!nF9pcQ;6S|A>{27bQFnZvP^&3JNnD}o*s3KuaTX&E3vh3`!F_$9O2coF%rcbG zw-}|^yz<#C-M9u`YLhD0>54U3c!)s`Sp+R9G^t|9Q*MI$X|E6~ZgTY{89@tW2yJg| z+q^i}`BnSs!JeI;5)3O_$CW3rYeI}u$#lE;YUA}T3Qn3UpO3twtKx}&z1YqYf1QE% zN)+#rktVCV{hBK78>K}=%R-g$3v@&NoM`u<+@*U?Y4&iP;P7_;HAL@wzN+?umalyy zuYtH4M-@CC;WNQJS55%w>lpJo{nk1->5@xSPX>VgWEixBn8HJLag zS8M2Gmz>KN?khNe%gQ`Q=)f(i`W8xCQ%O72tv3uKpE)9)-QL4~$$@jv;r%voA2?E5 zBROgtMw(2omluq+eE_r!zUz}8=xK@2SdX>(y6Qt)I&Hx(tr6?2^X==U zBX`HbQguqcI!UM~uh2egL2jD-LERX*cA;dP2i6T9os)TS5mx*)w9$AK%LS#f)G1Dz ztaCaym|q*iqHQXB%|%LUKiHv2v^caQWG}Pkpg^X%FIh=?dGtw%p5;7ai%N4sK;Ms` zHU;?ttaDl!59BVD`C2`i%pz{2Z|*}g80EPARj|f#QB>dk=IxQd&q!jy$vgn9&Nrdv9!Qi?RQo-Yx%PJH%Q%^6EU#yx;x zeGc1+-{tp;OI0nxgw%VzlS-Tt+)e4wL2Y(t|ytw+}oRHXdYkk+b>o5Nr+^6xygpzQ%>==k_zCT+)n9?`k;Z zX6Nqn47=1Ea??Kj&HwwGu%>lj4q5JKxhmV)*sRk zX``5ilF$(yR)8iv(_l&MdsT`4Er|pu0X<(B1x@@49{@ff5)CcX7vIz^5Sfr*m{~Qs zaUlrQ?@2#4_#Phs_JypRERYdV7#thLIO1m~Rg%ozXo^@Ny>8e{uvNBmq7il8Z-w?y zDmMA4v;jG}%ew-60X$dm9_9$c_{-app>tzaj~yu`qa$bQr0(YT$Yb($-T5@0V>o@O zg7A7jrCx=%p06 zn#I>7gcAi_Hz%}aD0`DQZus8^tYYiqPfOe`=On z!n1@L5TZGi2ahh;)vuDKh|5ZzF<$u;C4$%upH#N7YK8V;^Ux%m{EJ}p?ucIC><(c3 zW`kCVad?L@cVM{CBs0GFf)kP>`|;4~rs#fN-bER34G=#%y?g)+pNLTzjZo`GmC)G) z)m?#5fZ)XDTJe*ecIFSl-{{`dBIrL?GItk!ur9sb;czyDdf>c)|1|-g7|ErR90k*l z2+X^s12>>h7Kn?R9g~Wi-5<9Pxq{=rr@uoJr=$U&F+zW+W|vsiF6Pqdb#VIVz>{LY z7lNYe@xjisQe=iaq55)Q3kD)nY=s2($GT+lIGR6B_e?H7j8Fyg<-Fi9%f?n0EvRKO z-}74e60l9)zs5>ZXmnAJJrp$v@`qJVyQ!zmE>#YUYm5JgI(;ZX&05!Hi&5@W+2Ccs z(Z^o3dxc-?pvCs?jrnLgybjn9-#Aq|5>J~-S4A+==F^KvrKfRl_H>-z!(q(JX=*}a znj7uLc#vL9YqteBuG$#8Mg07wjp{oAFASW(FBw^z`B{#zuP+E$n~L^a_;VO07%^!W z94F2adA5$kmlqm_`GkEiW1;v3_E_prj)e?yteB}U6Z`eQ(w2V=-`uu7f+SE`>_Pb7 ze|!FG`2LeR;Dt3z8CtxYN->^o( z@mz+lw^pkXS=46g7GJ>8Xl#33TPm)tUDRIpzPqtp+-fCyy`19edO1)0x&(D&b}*IA zb(!_BHTfX$A%0{9qFGVf5cC;@%1znz3naWqRae(~J9K|$`u4*5vfA0nsrBzWC>QN- z)#rv^+38l?zCaW9_YQ5I(%8Fuc=4i$8PbVp>Cd}A;9eN#uYa#y!92TuZdc3x#GupW zv-WlINp3cBQT{Vdttb>X3jM{syMhObQDf0O_T6(-`~B1U*R8;h!#LL=ao*2p;hngQtVK^6V{{c(mNLtSVUg*hPhfbdd&sNJU6V5*> zIhvH*wb3s>ci+aV(A7S1UhL+*zA1<-m6E>Q#|Gy{9lQ!t{QT-%Vp6-lEV++9PzIx7 zc?B)7KC16d9fn4ia-sw1Ek972cL@DXERr913ZOrsf~ThStdnVcEUOc{kz$ii4p zvMNheX%U39sQ8t+ccKR%TDNe4RCf!-w^|u&AJBkpWRCVAFFgqsov~sZ;w5?+Q5d0V zM5Dv7j?}7X5$a8t27n83O^zHT;S=aee|Q>&2?b27DGN9{) zGf&ivY3Fyfw|k4C$5lIBT7trq|!Rt5u0>%1X0>Cx%jn~}=DX6tsWAKuTk z5#^>6$IsP?Leq_Vhjop}#P!MAnc+O`CJ9gRMU-LuZdhqi=o!@YGV{W8=@Nw|cCo3n z(Q2-#+=8{*X%P-&IY|9s5tGY%$1L&O6o#37dSZjG&S>WxoICrBQ(NR)mt54GjJsV6 zAiBqi^&Nz=xOUX?>O`C_R2NqZE%C@hI+S^1Z)LA{47Q)_Rt%5!G`9`}k3eMH#Vy4& zjB{B5e`mKFn7l=TPurbfvv{2Y&g=WNICbh2d7LB;o(rgk zJCb&17`Rv07=|tZ0L!~(AnJ|3UoXROpXdfY(lofPMs z{Jtvz2zO|Nzhoq}O`-qXxCL>zf!+_6Ay=W)+DADjR6)49V-5&+^MoLxhZHE6Jaqo_?({5MkZEc zFaZZCX{ieNe!53BHQOgwe2)U+5bj>m`l()#1XS=gax6X?cyD@yTl;~jOPiugM-*}^ zyWo$-nC~Tk@GDf2vJJl9U0;a;!pluKFbjhiMap5&>@9{m>OoV@7V}rj0un}XR*hPvJP3!v&dF6DG#!GEtX?6L_p}`WX@fg z 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 literal 0 HcmV?d00001 diff --git a/artifacts/WordStatTest.jar b/artifacts/WordStatTest.jar new file mode 100644 index 0000000000000000000000000000000000000000..a047faae06abeb502319fd6ae1b7b58ec2b21d41 GIT binary patch literal 45846 zcmZsCbC74x@@3n$ZQHhO+jh^VyJy-qrfs`>+O}=mwz0GO-uK16-|mfDf82$%2Bx00BV(0l9@s>H_^=4>S-Mki58>Fuk;b1mo8j5RlTpO`(8X|C;W;S&Do7 zH4pvU(Ehgnn92()NK1&Tsxin*+{;gl%gNF+%)-mk(@afF|50IDVBI-%nGi*BrjwGJ zl2QkX049fre}I$sZq1BTL6uceb<3iuLW`1nfP08)fR9FF(@S(^fsOFDQN4qeVUcxZ z_ipt;WxaRHaz%-ZtU?>Rk5mbeMUj2z&x=CJUF^{7(ECq6fq-o6IR9%%;D7sRV(e

d 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 literal 0 HcmV?d00001 diff --git a/artifacts/WsppTest.jar b/artifacts/WsppTest.jar new file mode 100644 index 0000000000000000000000000000000000000000..12a8e268e27a4341675167036ff46a58aa7f7d1d GIT binary patch literal 51442 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 32)) == target) { + print(4, i, password); + } + } + } + + /* package-private */ static final long PRIME = 2025_2025_07; + + private static void flag5(final byte[] password) { + final long n = 1_000_000_000_000_000L + getInt(password); + + long result = 0; + for (long i = 0; i < n; i++) { + result = (result + i / 3 + i / 5 + i / 7 + i / 2025) % PRIME; + } + + print(5, result, password); + } + + + private static void flag6(final byte[] password) { + /*** + \u002a\u002f\u0077\u0068\u0069\u006c\u0065\u0020\u0028\u0022\u0031\u0022 + \u002e\u006c\u0065\u006e\u0067\u0074\u0068\u0028\u0029\u0020\u003d\u003d + \u0020\u0031\u0029\u003b\u0020\u0020\u006c\u006f\u006e\u0067\u0020\u0009 + \u0020\u0020\u0072\u0065\u0073\u0075\u006c\u0074\u0020\u003d\u0020\u000a + \u0035\u0037\u0034\u0038\u0035\u0037\u0030\u0032\u0034\u0038\u0033\u004c + \u002b\u0070\u0061\u0073\u0073\u0077\u006f\u0072\u0064\u005b\u0035\u005d + \u002b\u0070\u0061\u0073\u0073\u0077\u006f\u0072\u0064\u005b\u0032\u005d + \u003b\u002f\u002a + ***/ + print(6, result, password); + } + + + private static void flag7(final byte[] password) { + // Count the number of occurrences of the most frequent noun at the following page: + // https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html + + // The singular form of the most frequent noun + final String singular = ""; + // The plural form of the most frequent noun + final String plural = ""; + // The total number of occurrences + final int total = 0; + if (total != 0) { + print(7, (singular + ":" + plural + ":" + total).hashCode(), password); + } + } + + + private static void flag8(final byte[] password) { + // Count the number of bluish (#5984A1) pixels of this image: + // https://dev.java/assets/images/java-affinity-logo-icode-lg.png + + final int number = 0; + if (number != 0) { + print(8, number, password); + } + } + + + private static final String PATTERN = "Reading the documentation can be surprisingly helpful!"; + private static final int SMALL_REPEAT_COUNT = 10_000_000; + + private static void flag9(final byte[] password) { + String repeated = ""; + for (int i = 0; i < SMALL_REPEAT_COUNT; i++) { + repeated += PATTERN; + } + + print(9, repeated.hashCode(), password); + } + + + private static final long LARGE_REPEAT_SHIFT = 29; + private static final long LARGE_REPEAT_COUNT = 1L << LARGE_REPEAT_SHIFT; + + private static void flag10(final byte[] password) { + String repeated = ""; + for (long i = 0; i < LARGE_REPEAT_COUNT; i++) { + repeated += PATTERN; + } + + print(10, repeated.hashCode(), password); + } + + + private static void flag11(final byte[] password) { + print(11, 5823470598324780581L, password); + } + + + private static void flag12(final byte[] password) { + final BigInteger year = BigInteger.valueOf(-2025); + final BigInteger term = BigInteger.valueOf(PRIME + Math.abs(getInt(password)) % PRIME); + + final long result = Stream.iterate(BigInteger.ZERO, BigInteger.ONE::add) + .filter(i -> year.multiply(i).add(term).multiply(i).compareTo(BigInteger.TEN) > 0) + .mapToLong(i -> i.longValue() * password[i.intValue() % password.length]) + .sum(); + + print(12, result, password); + } + + + private static final long MAX_DEPTH = 100_000_000L; + + private static void flag13(final byte[] password) { + try { + flag13(password, 0, 0); + } catch (final StackOverflowError e) { + System.err.println("Stack overflow :(("); + } + } + + private static void flag13(final byte[] password, final long depth, final long result) { + if (depth < MAX_DEPTH) { + flag13(password, depth + 1, (result ^ PRIME) | (result << 2) + depth * 17); + } else { + print(13, result, password); + } + } + + + private static void flag14(final byte[] password) { + final Instant today = Instant.parse("2025-09-09T12:00:00Z"); + final BigInteger hours = BigInteger.valueOf(Duration.between(Instant.EPOCH, today).toHours() + password[1] + password[3]); + + final long result = Stream.iterate(BigInteger.ZERO, hours::add) + .reduce(BigInteger.ZERO, BigInteger::add) + .longValue(); + + print(14, result, password); + } + + private static void flag15(final byte[] password) { + // REDACTED + } + + private static void flag16(final byte[] password) { + byte[] a = { + (byte) (password[0] + password[3]), + (byte) (password[1] + password[4]), + (byte) (password[2] + password[5]) + }; + + for (long i = 1_000_000_000_000_000_000L + getInt(password); i >= 0; i--) { + flag16Update(a); + } + + print(16, flag16Result(a), password); + } + + /* package-private */ static void flag16Update(byte[] a) { + a[0] ^= a[1]; + a[1] -= a[2] | a[0]; + a[2] *= a[0]; + } + + /* package-private */ static int flag16Result(byte[] a) { + return (a[0] + " " + a[1] + " " + a[2]).hashCode(); + } + + /** + * Original idea by Alexei Shishkin. + */ + private static void flag17(final byte[] password) { + final int n = Math.abs(getInt(password) % 2025) + 2025; + print(17, calc17(n), password); + } + + /** + * Write me + *

+     *    0: iconst_0
+     *    1: istore_1
+     *    2: iload_1
+     *    3: iload_1
+     *    4: imul
+     *    5: sipush        2025
+     *    8: idiv
+     *    9: iload_0
+     *   10: isub
+     *   11: ifge          20
+     *   14: iinc          1, 1
+     *   17: goto          2
+     *   20: iload_1
+     *   21: ireturn
+     * 
+ */ + private static int calc17(final int n) { + return n; + } + + + private static void flag18(final byte[] password) { + final int n = 2025 + getInt(password) % 2025; + // Find the number of factors of n! modulo PRIME + final int factors = 0; + if (factors != 0) { + print(18, factors, password); + } + } + + + private static void flag19(final byte[] password) { + // Let n = 2025e25 + abs(getInt(password)). + // Consider the sequence of numbers (n + i) ** 2. + // Instead of each number, we write the number that is obtained from it by discarding the last 25 digits. + // How many of the first numbers of the resulting sequence will form an arithmetic progression? + final long result = 0; + if (result != 0) { + print(19, result, password); + } + } + + /** + * Original idea by Dmitrii Liapin. + */ + private static void flag20(final byte[] password) { + final Collection longs = new Random(getInt(password)).longs(2025_000) + .map(n -> n % 1000) + .boxed() + .collect(Collectors.toCollection(LinkedList::new)); + + // Calculate the number of objects (recursively) accessible by "longs" reference. + final int result = 0; + + if (result != 0) { + print(20, result, password); + } + } + + /** + * Original idea and implementation Igor Panasyuk. + */ + private static void flag21(final byte[] password) { + record Pair(int x, int y) { + } + + final List items = new ArrayList<>(Arrays.asList( + Byte.toUnsignedInt(password[0]), + (long) getInt(password), + new Pair(password[1], password[2]), + "Java SE 21 " + Arrays.toString(password) + )); + + for (int round = 0; round < 10; round++) { + for (final Object item : List.copyOf(items)) { + // TODO: complete the switch expression using Java 21 features: + // items.add( + // case Integer i -> square of i as long + // case Long l and l is even -> l ^ 0x21L + // case Long l and l is odd -> -l + // case Pair(int x, int y) -> x << 8 ^ y + // case String s -> s.hashCode() + // default -> 0 + // ); + } + } + + long result = 0; + for (final Object item : items) { + result = result * 17 + item.toString().hashCode(); + } + + print(21, result, password); + } + + + // --------------------------------------------------------------------------------------------------------------- + // You may ignore all code below this line. + // It is not required to get any of the flags. + // --------------------------------------------------------------------------------------------------------------- + + private static void print(final int no, long result, final byte[] password) { + System.out.format("flag %d: https://www.kgeorgiy.info/courses/prog-intro/hw1/%s%n", no, flag(result, password)); + } + + /* package-private */ static String flag(long result, byte[] password) { + final byte[] flag = password.clone(); + for (int i = 0; i < 6; i++) { + flag[i] ^= result; + result >>>= 8; + } + + return flag(flag); + } + + /* package-private */ static String flag(final byte[] data) { + final MessageDigest messageDigest = RunMe.DIGEST.get(); + messageDigest.update(SALT); + messageDigest.update(data); + messageDigest.update(SALT); + final byte[] digest = messageDigest.digest(); + + return IntStream.range(0, 6) + .map(i -> (((digest[i * 2] & 255) << 8) + (digest[i * 2 + 1] & 255)) % KEYWORDS.size()) + .mapToObj(KEYWORDS::get) + .collect(Collectors.joining("-")); + } + + /* package-private */ static byte[] parseArgs(final String[] args) { + if (args.length != 6) { + throw error("Expected 6 command line arguments, found: %d", args.length); + } + + final byte[] bytes = new byte[args.length]; + for (int i = 0; i < args.length; i++) { + final Byte value = VALUES.get(args[i].toLowerCase(Locale.US)); + if (value == null) { + throw error("Expected keyword, found: %s", args[i]); + } + bytes[i] = value; + } + return bytes; + } + + private static AssertionError error(final String format, final Object... args) { + System.err.format(format, args); + System.err.println(); + System.exit(1); + throw new AssertionError(); + } + + /* package-private */ static int getInt(byte[] password) { + return IntStream.range(0, password.length) + .map(i -> password[i]) + .reduce((a, b) -> a * KEYWORDS.size() + b) + .getAsInt(); + } + + private static final ThreadLocal DIGEST = ThreadLocal.withInitial(() -> { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (final NoSuchAlgorithmException e) { + throw new AssertionError("Cannot create SHA-256 digest", e); + } + }); + + public static final byte[] SALT = "fathdufimmonJiajFik3JeccafdaihoFrusthys9".getBytes(StandardCharsets.US_ASCII); + + private static final List KEYWORDS = List.of( + "abstract", + "assert", + "boolean", + "break", + "byte", + "case", + "catch", + "char", + "class", + "const", + "new", + "package", + "private", + "protected", + "public", + "return", + "short", + "static", + "strictfp", + "super", + "for", + "goto", + "if", + "implements", + "import", + "instanceof", + "int", + "interface", + "long", + "native", + "continue", + "default", + "do", + "double", + "else", + "enum", + "extends", + "final", + "finally", + "float", + "switch", + "synchronized", + "this", + "throw", + "throws", + "transient", + "try", + "void", + "volatile", + "while", + "record", + "Error", + "AssertionError", + "OutOfMemoryError", + "StackOverflowError", + "ArrayIndexOutOfBoundsException", + "ArrayStoreException", + "AutoCloseable", + "Character", + "CharSequence", + "ClassCastException", + "Comparable", + "Exception", + "IllegalArgumentException", + "IllegalStateException", + "IndexOutOfBoundsException", + "Integer", + "Iterable", + "Math", + "Module", + "NegativeArraySizeException", + "NullPointerException", + "Number", + "NumberFormatException", + "Object", + "Override", + "RuntimeException", + "StrictMath", + "String", + "StringBuilder", + "StringIndexOutOfBoundsException", + "SuppressWarnings", + "System", + "Thread", + "Throwable", + "ArithmeticException", + "ClassLoader", + "ClassNotFoundException", + "Cloneable", + "Deprecated", + "FunctionalInterface", + "InterruptedException", + "Process", + "ProcessBuilder", + "Runnable", + "SafeVarargs", + "StackTraceElement", + "Runtime", + "ThreadLocal", + "UnsupportedOperationException" + ); + + private static final Map VALUES = IntStream.range(0, KEYWORDS.size()) + .boxed() + .collect(Collectors.toMap(index -> KEYWORDS.get(index).toLowerCase(Locale.US), Integer::byteValue)); +} diff --git a/java/base/Asserts.java b/java/base/Asserts.java new file mode 100644 index 0000000..17ea3e6 --- /dev/null +++ b/java/base/Asserts.java @@ -0,0 +1,84 @@ +package base; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("StaticMethodOnlyUsedInOneClass") +public final class Asserts { + static { + Locale.setDefault(Locale.US); + } + + private Asserts() { + } + + public static void assertEquals(final String message, final Object expected, final Object actual) { + final String reason = String.format("%s:%n expected `%s`,%n actual `%s`", + message, toString(expected), toString(actual)); + assertTrue(reason, Objects.deepEquals(expected, actual)); + } + + public static String toString(final Object value) { + if (value != null && value.getClass().isArray()) { + final String result = Arrays.deepToString(new Object[]{value}); + return result.substring(1, result.length() - 1); + } else { + return Objects.toString(value); + } + } + + public static void assertEquals(final String message, final List expected, final List actual) { + for (int i = 0; i < Math.min(expected.size(), actual.size()); i++) { + assertEquals(message + ":" + (i + 1), expected.get(i), actual.get(i)); + } + assertEquals(message + ": Number of items", expected.size(), actual.size()); + } + + public static void assertTrue(final String message, final boolean value) { + if (!value) { + throw error("%s", message); + } + } + + public static void assertEquals(final String message, final double expected, final double actual, final double precision) { + assertTrue( + String.format("%s: Expected %.12f, found %.12f", message, expected, actual), + isEqual(expected, actual, precision) + ); + } + + public static boolean isEqual(final double expected, final double actual, final double precision) { + final double error = Math.abs(actual - expected); + return error <= precision + || error <= precision * Math.abs(expected) + || !Double.isFinite(expected) + || Math.abs(expected) > 1e100 + || Math.abs(expected) < precision && !Double.isFinite(actual); + } + + public static void assertSame(final String message, final Object expected, final Object actual) { + assertTrue(String.format("%s: expected same objects: %s and %s", message, expected, actual), expected == actual); + } + + public static void checkAssert(final Class c) { + if (!c.desiredAssertionStatus()) { + throw error("You should enable assertions by running 'java -ea %s'", c.getName()); + } + } + + public static AssertionError error(final String format, final Object... args) { + final String message = String.format(format, args); + return args.length > 0 && args[args.length - 1] instanceof Throwable + ? new AssertionError(message, (Throwable) args[args.length - 1]) + : new AssertionError(message); + } + + public static void printStackTrace(final String message) { + new Exception(message).printStackTrace(System.out); + } +} diff --git a/java/base/BaseChecker.java b/java/base/BaseChecker.java new file mode 100644 index 0000000..67bd57c --- /dev/null +++ b/java/base/BaseChecker.java @@ -0,0 +1,20 @@ +package base; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public abstract class BaseChecker { + protected final TestCounter counter; + + protected BaseChecker(final TestCounter counter) { + this.counter = counter; + } + + public ExtendedRandom random() { + return counter.random(); + } + + public int mode() { + return counter.mode(); + } +} diff --git a/java/base/Either.java b/java/base/Either.java new file mode 100644 index 0000000..8a3eca8 --- /dev/null +++ b/java/base/Either.java @@ -0,0 +1,95 @@ +package base; + +import java.util.function.Function; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public interface Either { + Either mapRight(final Function f); + Either flatMapRight(final Function> f); + T either(Function lf, Function rf); + + boolean isRight(); + + L getLeft(); + R getRight(); + + static Either right(final R value) { + return new Either<>() { + @Override + public Either mapRight(final Function f) { + return right(f.apply(value)); + } + + @Override + public Either flatMapRight(final Function> f) { + return f.apply(value); + } + + @Override + public T either(final Function lf, final Function rf) { + return rf.apply(value); + } + + @Override + public boolean isRight() { + return true; + } + + @Override + public L getLeft() { + return null; + } + + @Override + public R getRight() { + return value; + } + + @Override + public String toString() { + return String.format("Right(%s)", value); + } + }; + } + + static Either left(final L value) { + return new Either<>() { + @Override + public Either mapRight(final Function f) { + return left(value); + } + + @Override + public Either flatMapRight(final Function> f) { + return left(value); + } + + @Override + public T either(final Function lf, final Function rf) { + return lf.apply(value); + } + + @Override + public boolean isRight() { + return false; + } + + @Override + public L getLeft() { + return value; + } + + @Override + public R getRight() { + return null; + } + + @Override + public String toString() { + return String.format("Left(%s)", value); + } + }; + } +} diff --git a/java/base/ExtendedRandom.java b/java/base/ExtendedRandom.java new file mode 100644 index 0000000..ac2b059 --- /dev/null +++ b/java/base/ExtendedRandom.java @@ -0,0 +1,89 @@ +package base; + +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ExtendedRandom { + public static final String ENGLISH = "abcdefghijklmnopqrstuvwxyz"; + public static final String RUSSIAN = "абвгдеежзийклмнопрстуфхцчшщъыьэюя"; + public static final String GREEK = "αβγŋδεζηθικλμνξοπρτυφχψω"; + @SuppressWarnings("StaticMethodOnlyUsedInOneClass") + public static final String SPACES = " \t\n\u000B\u2029\f"; + + private final Random random; + + public ExtendedRandom(final Random random) { + this.random = random; + } + + public ExtendedRandom(final Class owner) { + this(new Random(7912736473497634913L + owner.getName().hashCode())); + } + + public String randomString(final String chars) { + return randomChar(chars) + (random.nextBoolean() ? "" : randomString(chars)); + } + + public char randomChar(final String chars) { + return chars.charAt(nextInt(chars.length())); + } + + public String randomString(final String chars, final int length) { + final StringBuilder string = new StringBuilder(); + for (int i = 0; i < length; i++) { + string.append(randomChar(chars)); + } + return string.toString(); + } + + public String randomString(final String chars, final int minLength, final int maxLength) { + return randomString(chars, nextInt(minLength, maxLength)); + } + + public boolean nextBoolean() { + return random.nextBoolean(); + } + + public int nextInt() { + return random.nextInt(); + } + + public int nextInt(final int min, final int max) { + return nextInt(max - min + 1) + min; + } + + public int nextInt(final int n) { + return random.nextInt(n); + } + + @SafeVarargs + public final T randomItem(final T... items) { + return items[nextInt(items.length)]; + } + + public T randomItem(final List items) { + return items.get(nextInt(items.size())); + } + + public Random getRandom() { + return random; + } + + public List random(final int list, final Function generator) { + return Stream.generate(() -> generator.apply(this)).limit(list).toList(); + } + + public double nextDouble() { + return random.nextDouble(); + } + + public void shuffle(final List all) { + Collections.shuffle(all, random); + } +} diff --git a/java/base/Functional.java b/java/base/Functional.java new file mode 100644 index 0000000..ef14dd5 --- /dev/null +++ b/java/base/Functional.java @@ -0,0 +1,92 @@ +package base; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Functional { + private Functional() {} + + public static List map(final Collection items, final Function f) { + return items.stream().map(f).collect(Collectors.toUnmodifiableList()); + } + + public static List map(final List items, final BiFunction f) { + return IntStream.range(0, items.size()) + .mapToObj(i -> f.apply(i, items.get(i))) + .collect(Collectors.toUnmodifiableList()); + } + + public static Map mapValues(final Map map, final Function f) { + return map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> f.apply(e.getValue()))); + } + + @SafeVarargs + public static Map mergeMaps(final Map... maps) { + return Stream.of(maps).flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a)); + } + + @SafeVarargs + public static List concat(final Collection... items) { + final List result = new ArrayList<>(); + for (final Collection item : items) { + result.addAll(item); + } + return result; + } + + public static List append(final Collection collection, final T item) { + final List list = new ArrayList<>(collection); + list.add(item); + return list; + } + + public static List> allValues(final List vals, final int length) { + return Stream.generate(() -> vals) + .limit(length) + .reduce( + List.of(List.of()), + (prev, next) -> next.stream() + .flatMap(value -> prev.stream().map(list -> append(list, value))) + .toList(), + (prev, next) -> next.stream() + .flatMap(suffix -> prev.stream().map(prefix -> concat(prefix, suffix))) + .toList() + ); + } + + public static V get(final Map map, final K key) { + final V result = map.get(key); + if (result == null) { + throw new NullPointerException(key.toString() + " in " + map(map.keySet(), Objects::toString)); + } + return result; + } + + public static void addRange(final List values, final int d, final int c) { + for (int i = -d; i <= d; i++) { + values.add(c + i); + } + } + + public static void forEachPair(final T[] items, final BiConsumer consumer) { + assert items.length % 2 == 0; + IntStream.range(0, items.length / 2).forEach(i -> consumer.accept(items[i * 2], items[i * 2 + 1])); + } + + + public static List> toPairs(final T[] items) { + assert items.length % 2 == 0; + return IntStream.range(0, items.length / 2) + .mapToObj(i -> Pair.of(items[i * 2], items[i * 2 + 1])) + .toList(); + } +} diff --git a/java/base/Log.java b/java/base/Log.java new file mode 100644 index 0000000..00d9141 --- /dev/null +++ b/java/base/Log.java @@ -0,0 +1,56 @@ +package base; + +import java.util.function.Supplier; +import java.util.regex.Pattern; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class Log { + private final Pattern LINES = Pattern.compile("\n"); + private int indent; + + public static Supplier action(final Runnable action) { + return () -> { + action.run(); + return null; + }; + } + + public void scope(final String name, final Runnable action) { + scope(name, action(action)); + } + + public T scope(final String name, final Supplier action) { + println(name); + indent++; + try { + return silentScope(name, action); + } finally { + indent--; + } + } + + public T silentScope(final String ignoredName, final Supplier action) { + return action.get(); + } + + @SuppressWarnings("UseOfSystemOutOrSystemErr") + public void println(final Object value) { + for (final String line : LINES.split(String.valueOf(value))) { + System.out.println(indent() + line); + } + } + + public void format(final String format, final Object... args) { + println(String.format(format,args)); + } + + private String indent() { + return " ".repeat(indent); + } + + protected int getIndent() { + return indent; + } +} diff --git a/java/base/MainChecker.java b/java/base/MainChecker.java new file mode 100644 index 0000000..e526e7e --- /dev/null +++ b/java/base/MainChecker.java @@ -0,0 +1,28 @@ +package base; + +import java.util.List; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("StaticMethodOnlyUsedInOneClass") +public final class MainChecker { + private final Runner runner; + + public MainChecker(final Runner runner) { + this.runner = runner; + } + + public List run(final TestCounter counter, final String... input) { + return runner.run(counter, input); + } + + public List run(final TestCounter counter, final List input) { + return runner.run(counter, input); + } + + public void testEquals(final TestCounter counter, final List input, final List expected) { + runner.testEquals(counter, input, expected); + } + +} diff --git a/java/base/Named.java b/java/base/Named.java new file mode 100644 index 0000000..befb254 --- /dev/null +++ b/java/base/Named.java @@ -0,0 +1,15 @@ +package base; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public record Named(String name, T value) { + public static Named of(final String name, final T f) { + return new Named<>(name, f); + } + + @Override + public String toString() { + return name; + } +} diff --git a/java/base/Pair.java b/java/base/Pair.java new file mode 100644 index 0000000..8c27a31 --- /dev/null +++ b/java/base/Pair.java @@ -0,0 +1,44 @@ +package base; + +import java.util.Map; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings({"StaticMethodOnlyUsedInOneClass", "unused"}) +public record Pair(F first, S second) { + public static Pair of(final F first, final S second) { + return new Pair<>(first, second); + } + + public static Pair of(final Map.Entry e) { + return of(e.getKey(), e.getValue()); + } + + public static UnaryOperator> lift(final UnaryOperator f, final UnaryOperator s) { + return p -> of(f.apply(p.first), s.apply(p.second)); + } + + public static BinaryOperator> lift(final BinaryOperator f, final BinaryOperator s) { + return (p1, p2) -> of(f.apply(p1.first, p2.first), s.apply(p1.second, p2.second)); + } + + public static Function> tee( + final Function f, + final Function s + ) { + return t -> of(f.apply(t), s.apply(t)); + } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } + + public Pair second(final R second) { + return new Pair<>(first, second); + } +} diff --git a/java/base/Runner.java b/java/base/Runner.java new file mode 100644 index 0000000..c0a59bc --- /dev/null +++ b/java/base/Runner.java @@ -0,0 +1,185 @@ +package base; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("unused") +@FunctionalInterface +public interface Runner { + List run(final TestCounter counter, final List input); + + default List run(final TestCounter counter, final String... input) { + return run(counter, List.of(input)); + } + + default void testEquals(final TestCounter counter, final List input, final List expected) { + counter.test(() -> { + final List actual = run(counter, input); + for (int i = 0; i < Math.min(expected.size(), actual.size()); i++) { + final String exp = expected.get(i); + final String act = actual.get(i); + if (!exp.equalsIgnoreCase(act)) { + Asserts.assertEquals("Line " + (i + 1), exp, act); + return; + } + } + Asserts.assertEquals("Number of lines", expected.size(), actual.size()); + }); + } + + static Packages packages(final String... packages) { + return new Packages(List.of(packages)); + } + + @FunctionalInterface + interface CommentRunner { + List run(String comment, TestCounter counter, List input); + } + + final class Packages { + private final List packages; + + private Packages(final List packages) { + this.packages = packages; + } + + public Runner std(final String className) { + final CommentRunner main = main(className); + return (counter, input) -> counter.call("io", () -> { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (final PrintWriter writer = new PrintWriter(baos, false, StandardCharsets.UTF_8)) { + input.forEach(writer::println); + } + + final InputStream oldIn = System.in; + try { + System.setIn(new ByteArrayInputStream(baos.toByteArray())); + return main.run(String.format("[%d input lines]", input.size()), counter, List.of()); + } finally { + System.setIn(oldIn); + } + }); + } + + @SuppressWarnings("ConfusingMainMethod") + public CommentRunner main(final String className) { + final Method method = findMain(className); + + return (comment, counter, input) -> { + counter.format("Running test %02d: java %s %s%n", counter.getTestNo(), method.getDeclaringClass().getName(), comment); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + @SuppressWarnings("UseOfSystemOutOrSystemErr") final PrintStream oldOut = System.out; + try { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8)); + method.invoke(null, new Object[]{input.toArray(String[]::new)}); + final BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(out.toByteArray()), StandardCharsets.UTF_8)); + final List result = new ArrayList<>(); + while (true) { + final String line = reader.readLine(); + if (line == null) { + if (result.isEmpty()) { + result.add(""); + } + return result; + } + result.add(line.trim()); + } + } catch (final InvocationTargetException e) { + final Throwable cause = e.getCause(); + throw Asserts.error("main thrown exception %s: %s", cause.getClass().getSimpleName(), cause); + } catch (final Exception e) { + throw Asserts.error("Cannot invoke main: %s: %s", e.getClass().getSimpleName(), e.getMessage()); + } finally { + System.setOut(oldOut); + } + }; + } + + private Method findMain(final String className) { + try { + final URL url = new File(".").toURI().toURL(); + final List candidates = packages.stream() + .flatMap(pkg -> { + final String prefix = pkg.isEmpty() ? pkg : pkg + "."; + return Stream.of(prefix + className, prefix + "$" + className); + }) + .toList(); + + //noinspection ClassLoaderInstantiation,resource,IOResourceOpenedButNotSafelyClosed + final URLClassLoader classLoader = new URLClassLoader(new URL[]{url}); + for (final String candidate : candidates) { + try { + final Class loaded = classLoader.loadClass(candidate); + if (!Modifier.isPublic(loaded.getModifiers())) { + throw Asserts.error("Class %s is not public", candidate); + } + final Method main = loaded.getMethod("main", String[].class); + if (!Modifier.isPublic(main.getModifiers()) || !Modifier.isStatic(main.getModifiers())) { + throw Asserts.error("Method main of class %s should be public and static", candidate); + } + return main; + } catch (final ClassNotFoundException e) { + // Ignore + } catch (final NoSuchMethodException e) { + throw Asserts.error("Could not find method main(String[]) in class %s", candidate, e); + } + } + throw Asserts.error("Could not find neither of classes %s", candidates); + } catch (final MalformedURLException e) { + throw Asserts.error("Invalid path", e); + } + } + + private static String getClassName(final String pkg, final String className) { + return pkg.isEmpty() ? className : pkg + "." + className; + } + + public Runner args(final String className) { + final CommentRunner main = main(className); +// final AtomicReference prev = new AtomicReference<>(""); + return (counter, input) -> { + final int total = input.stream().mapToInt(String::length).sum() + input.size() * 3; + final String comment = total <= 300 + ? input.stream().collect(Collectors.joining("\" \"", "\"", "\"")) + : input.size() <= 100 + ? String.format("[%d arguments, sizes: %s]", input.size(), input.stream() + .mapToInt(String::length) + .mapToObj(String::valueOf) + .collect(Collectors.joining(", "))) + : String.format("[%d arguments, total size: %d]", input.size(), total); +// assert comment.length() <= 5 || !prev.get().equals(comment) : "Duplicate tests " + comment; +// prev.set(comment); + return main.run(comment, counter, input); + }; + } + + public Runner files(final String className) { + final Runner args = args(className); + return (counter, input) -> counter.call("io", () -> { + final Path inf = counter.getFile("in"); + final Path ouf = counter.getFile("out"); + Files.write(inf, input); + args.run(counter, List.of(inf.toString(), ouf.toString())); + final List output = Files.readAllLines(ouf); + Files.delete(inf); + Files.delete(ouf); + return output; + }); + } + } +} diff --git a/java/base/Selector.java b/java/base/Selector.java new file mode 100644 index 0000000..dc119b9 --- /dev/null +++ b/java/base/Selector.java @@ -0,0 +1,143 @@ +package base; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Selector { + private final Class owner; + private final List modes; + private final Set variantNames = new LinkedHashSet<>(); + private final Map> variants = new LinkedHashMap<>(); + + public Selector(final Class owner, final String... modes) { + this.owner = owner; + this.modes = List.of(modes); + } + + public Selector variant(final String name, final Consumer operations) { + Asserts.assertTrue("Duplicate variant " + name, variants.put(name.toLowerCase(), operations) == null); + variantNames.add(name); + return this; + } + + private static void check(final boolean condition, final String format, final Object... args) { + if (!condition) { + throw new IllegalArgumentException(String.format(format, args)); + } + } + + @SuppressWarnings("UseOfSystemOutOrSystemErr") + public void main(final String... args) { + try { + final String mode; + if (modes.isEmpty()) { + check(args.length >= 1, "At least one argument expected, found %s", args.length); + mode = ""; + } else { + check(args.length >= 2, "At least two arguments expected, found %s", args.length); + mode = args[0]; + } + + final List vars = Arrays.stream(args).skip(modes.isEmpty() ? 0 : 1) + .flatMap(arg -> Arrays.stream(arg.split("[ +]+"))) + .toList(); + + test(mode, vars); + } catch (final IllegalArgumentException e) { + System.err.println("ERROR: " + e.getMessage()); + if (modes.isEmpty()) { + System.err.println("Usage: " + owner.getName() + " VARIANT..."); + } else { + System.err.println("Usage: " + owner.getName() + " MODE VARIANT..."); + System.err.println("Modes: " + String.join(", ", modes)); + } + System.err.println("Variants: " + String.join(", ", variantNames)); + System.exit(1); + } + } + + public void test(final String mode, List vars) { + final int modeNo = modes.isEmpty() ? -1 : modes.indexOf(mode) ; + check(modes.isEmpty() || modeNo >= 0, "Unknown mode '%s'", mode); + if (variantNames.contains("Base") && !vars.contains("Base")) { + vars = new ArrayList<>(vars); + vars.add(0, "Base"); + } + + vars.forEach(variant -> check(variants.containsKey(variant.toLowerCase()), "Unknown variant '%s'", variant)); + + final Map properties = modes.isEmpty() + ? Map.of("variant", String.join("+", vars)) + : Map.of("variant", String.join("+", vars), "mode", mode); + final TestCounter counter = new TestCounter(owner, modeNo, properties); + counter.printHead(); + vars.forEach(variant -> counter.scope("Testing " + variant, () -> variants.get(variant.toLowerCase()).accept(counter))); + counter.printStatus(); + } + + public static Composite composite(final Class owner, final Function factory, final String... modes) { + return new Composite<>(owner, factory, (tester, counter) -> tester.test(), modes); + } + + public static Composite composite(final Class owner, final Function factory, final BiConsumer tester, final String... modes) { + return new Composite<>(owner, factory, tester, modes); + } + + public List getModes() { + return modes.isEmpty() ? List.of("~") : modes; + } + + public List getVariants() { + return List.copyOf(variants.keySet()); + } + + /** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ + public static final class Composite { + private final Selector selector; + private final Function factory; + private final BiConsumer tester; + private List> base; + + private Composite(final Class owner, final Function factory, final BiConsumer tester, final String... modes) { + selector = new Selector(owner, modes); + this.factory = factory; + this.tester = tester; + } + + @SafeVarargs + public final Composite variant(final String name, final Consumer... parts) { + if ("Base".equalsIgnoreCase(name)) { + base = List.of(parts); + return v(name.toLowerCase()); + } else { + return v(name, parts); + } + } + + @SafeVarargs + private Composite v(final String name, final Consumer... parts) { + selector.variant(name, counter -> { + final V variant = factory.apply(counter); + for (final Consumer part : base) { + part.accept(variant); + } + for (final Consumer part : parts) { + part.accept(variant); + } + tester.accept(variant, counter); + }); + return this; + } + + public Selector selector() { + return selector; + } + } +} diff --git a/java/base/TestCounter.java b/java/base/TestCounter.java new file mode 100644 index 0000000..85fb9c9 --- /dev/null +++ b/java/base/TestCounter.java @@ -0,0 +1,184 @@ +package base; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class TestCounter extends Log { + public static final int DENOMINATOR = Integer.getInteger("base.denominator", 1); + public static final int DENOMINATOR2 = (int) Math.round(Math.sqrt(DENOMINATOR)); + + private static final String JAR_EXT = ".jar"; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + + private final Class owner; + private final int mode; + private final Map properties; + private final ExtendedRandom random; + + private final long start = System.currentTimeMillis(); + private int passed; + + public TestCounter(final Class owner, final int mode, final Map properties) { + Locale.setDefault(Locale.US); + Asserts.checkAssert(getClass()); + + this.owner = owner; + this.mode = mode; + this.properties = properties; + random = new ExtendedRandom(owner); + } + + public int mode() { + return mode; + } + + public int getTestNo() { + return passed + 1; + } + + public void test(final Runnable action) { + testV(() -> { + action.run(); + return null; + }); + } + + public void testForEach(final Iterable items, final Consumer action) { + for (final T item : items) { + test(() -> action.accept(item)); + } + } + + public T testV(final Supplier action) { + return silentScope("Test " + getTestNo(), () -> { + final T result = action.get(); + passed++; + return result; + }); + } + + private String getLine() { + return getIndent() == 0 ? "=" : "-"; + } + + public void printHead() { + println("=== " + getTitle()); + } + + public void printStatus() { + format("%s%n%s%n", getLine().repeat(30), getTitle()); + format("%d tests passed in %dms%n", passed, System.currentTimeMillis() - start); + println("Version: " + getVersion(owner)); + println(""); + } + + private String getTitle() { + return String.format("%s %s", owner.getSimpleName(), properties.isEmpty() ? "" : properties); + } + + private static String getVersion(final Class clazz) { + try { + final ClassLoader cl = clazz.getClassLoader(); + final URL url = cl.getResource(clazz.getName().replace('.', '/') + ".class"); + if (url == null) { + return "(no manifest)"; + } + + final String path = url.getPath(); + final int index = path.indexOf(JAR_EXT); + if (index == -1) { + return DATE_FORMAT.format(new Date(new File(path).lastModified())); + } + + final String jarPath = path.substring(0, index + JAR_EXT.length()); + try (final JarFile jarFile = new JarFile(new File(new URI(jarPath)))) { + final JarEntry entry = jarFile.getJarEntry("META-INF/MANIFEST.MF"); + return DATE_FORMAT.format(new Date(entry.getTime())); + } + } catch (final IOException | URISyntaxException e) { + return "error: " + e; + } + } + + public T call(final String message, final SupplierE supplier) { + return get(supplier).either(e -> fail(e, "%s", message), Function.identity()); + } + + public void shouldFail(final String message, @SuppressWarnings("TypeMayBeWeakened") final RunnableE action) { + test(() -> get(action).either(e -> null, v -> fail("%s", message))); + } + + public T fail(final String format, final Object... args) { + return fail(Asserts.error(format, args)); + } + + public T fail(final Throwable throwable) { + return fail(throwable, "%s: %s", throwable.getClass().getSimpleName(), throwable.getMessage()); + } + + public T fail(final Throwable throwable, final String format, final Object... args) { + final String message = String.format(format, args); + println("ERROR: " + message); + throw throwable instanceof Error ? (Error) throwable : new AssertionError(throwable); + } + + public void checkTrue(final boolean condition, final String message, final Object... args) { + if (!condition) { + fail(message, args); + } + } + + public static Either get(final SupplierE supplier) { + return supplier.get(); + } + + public Path getFile(final String suffix) { + return Paths.get(String.format("test%d.%s", getTestNo(), suffix)); + } + + public ExtendedRandom random() { + return random; + } + + @FunctionalInterface + public interface SupplierE extends Supplier> { + T getE() throws Exception; + + @Override + default Either get() { + try { + return Either.right(getE()); + } catch (final Exception e) { + return Either.left(e); + } + } + } + + @FunctionalInterface + public interface RunnableE extends SupplierE { + void run() throws Exception; + + @Override + default Void getE() throws Exception { + run(); + return null; + } + } +} diff --git a/java/base/Tester.java b/java/base/Tester.java new file mode 100644 index 0000000..d30260d --- /dev/null +++ b/java/base/Tester.java @@ -0,0 +1,18 @@ +package base; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public abstract class Tester extends BaseChecker { + protected Tester(final TestCounter counter) { + super(counter); + } + + public abstract void test(); + + public void run(final Class test, final String... args) { + System.out.println("=== Testing " + test.getSimpleName() + " " + String.join(" ", args)); + test(); + counter.printStatus(); + } +} diff --git a/java/base/Unit.java b/java/base/Unit.java new file mode 100644 index 0000000..290febf --- /dev/null +++ b/java/base/Unit.java @@ -0,0 +1,15 @@ +package base; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Unit { + public static final Unit INSTANCE = new Unit(); + + private Unit() { } + + @Override + public String toString() { + return "unit"; + } +} diff --git a/java/base/package-info.java b/java/base/package-info.java new file mode 100644 index 0000000..0055441 --- /dev/null +++ b/java/base/package-info.java @@ -0,0 +1,7 @@ +/** + * Common homeworks test classes + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package base; \ No newline at end of file diff --git a/java/expression/BigDecimalListExpression.java b/java/expression/BigDecimalListExpression.java new file mode 100644 index 0000000..67841c3 --- /dev/null +++ b/java/expression/BigDecimalListExpression.java @@ -0,0 +1,202 @@ +package expression; + +import base.Asserts; +import base.Pair; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.IntStream; + +/** + * One-argument arithmetic expression over {@link BigDecimal}s. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +@SuppressWarnings("ClassReferencesSubclass") +public interface BigDecimalListExpression extends ToMiniString { + BigDecimal evaluateBd(List variables); + + // Tests follow. You may temporarily remove everything til the end. + + Add EXAMPLE = new Add( + new Subtract(new Variable(0), new Const(BigDecimal.ONE)), + new Multiply(new Variable(1), new Const(BigDecimal.TEN)) + ); + + Type TYPE = new Type<>( + v -> new BigDecimal(v + ".000"), + random -> BigDecimal.valueOf(random.getRandom().nextGaussian()), + BigDecimal.class + ); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + BigDecimalListExpression.class, + (r, c) -> IntStream.range(0, c) + .mapToObj(name -> Pair.of("$" + name, new Variable(name))) + .toList(), + (expr, variables, values) -> expr.evaluateBd(values) + ); + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + static ExpressionTester tester(final TestCounter counter) { + Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString()); + Asserts.assertEquals( + EXAMPLE + " at (2, 3)", + BigDecimal.valueOf(31), + EXAMPLE.evaluateBd(List.of(BigDecimal.valueOf(2), BigDecimal.valueOf(3))) + ); + + final Variable vx = new Variable(0); + final Variable vy = new Variable(1); + + return new ExpressionTester<>( + counter, KIND, c -> v -> c, + (op, a, b) -> v -> op.apply(a.evaluateBd(v), b.evaluateBd(v)), + BigDecimal::add, BigDecimal::subtract, BigDecimal::multiply, BigDecimal::divide + ) + .basic("10", "10", v -> v(10), c(10)) + .basic("$x", "$x", BigDecimalListExpression::x, vx) + .basic("$y", "$y", BigDecimalListExpression::y, vy) + .basic("($x + $y)", "$x + $y", v -> x(v).add(y(v)), new Add(vx, vy)) + .basic("($x + 2)", "$x + 2", v -> x(v).add(v(2)), new Add(vx, c(2))) + .basic("(2 - $x)", "2 - $x", v -> v(2).subtract(x(v)), new Subtract(c(2), vx)) + .basic("(3 * $x)", "3 * $x", v -> v(3).multiply(x(v)), new Multiply(c(3), vx)) + .basic("($x + $x)", "$x + $x", v -> x(v).add(x(v)), new Add(vx, vx)) + .basic("($x / -2)", "$x / -2", v -> x(v).divide(v(-2)), new Divide(vx, c(-2))) + .basic("(2 + $x)", "2 + $x", v -> v(2).add(x(v)), new Add(c(2), vx)) + .basic("((1 + 2) + 3)", "1 + 2 + 3", v -> v(6), new Add(new Add(c(1), c(2)), c(3))) + .basic("(1 + (2 * 3))", "1 + 2 * 3", v -> v(7), new Add(c(1), new Multiply(c(2), c(3)))) + .basic("(1 - (2 * 3))", "1 - 2 * 3", v -> v(-5), new Subtract(c(1), new Multiply(c(2), c(3)))) + .basic("(1 + (2 + 3))", "1 + 2 + 3", v -> v(6), new Add(c(1), new Add(c(2), c(3)))) + .basic("((1 - 2) - 3)", "1 - 2 - 3", v -> v(-4), new Subtract(new Subtract(c(1), c(2)), c(3))) + .basic("(1 - (2 - 3))", "1 - (2 - 3)", v -> v(2), new Subtract(c(1), new Subtract(c(2), c(3)))) + .basic("((1 * 2) * 3)", "1 * 2 * 3", v -> v(6), new Multiply(new Multiply(c(1), c(2)), c(3))) + .basic("(1 * (2 * 3))", "1 * 2 * 3", v -> v(6), new Multiply(c(1), new Multiply(c(2), c(3)))) + .basic("((10 / 2) / 3)", "10 / 2 / 3", v -> v(10).divide(v(2)).divide(v(3)), new Divide(new Divide(c(10), c(2)), c(3))) + .basic("(10 / (3 / 2))", "10 / (3 / 2)", v -> v(10).divide(v(3).divide(v(2))), new Divide(c(10), new Divide(c(3), c(2)))) + .basic("(($x * $x) + (($x - 1) / 10))", + "$x * $x + ($x - 1) / 10", + v -> x(v).multiply(x(v)).add(x(v).subtract(v(1)).divide(v(10))), + new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10))) + ) + .basic("($x * -1000000000)", "$x * -1000000000", v -> x(v).multiply(v(-1_000_000_000)), new Multiply(vx, c(-1_000_000_000))) + .basic("($x * -1000000000000000)", "$x * -1000000000000000", v -> x(v).multiply(v(-1_000_000_000_000_000L)), new Multiply(vx, c(-1_000_000_000_000_000L))) + .basic("(10 / $x)", "10 / $x", v -> v(10).divide(x(v)), new Divide(c(10), vx)) + .basic("($x / $x)", "$x / $x", v -> x(v).divide(x(v)), new Divide(vx, vx)) + + .advanced("(2 + 1)", "2 + 1", v -> v(2 + 1), new Add(c(2), c(1))) + .advanced("($x - 1)", "$x - 1", v -> x(v).subtract(v(1)), new Subtract(vx, c(1))) + .advanced("(1 * 2)", "1 * 2", v -> v(1 * 2), new Multiply(c(1), c(2))) + .advanced("($x / 1)", "$x / 1", v -> x(v).divide(v(1)), new Divide(vx, c(1))) + .advanced("(1 + (2 + 1))", "1 + 2 + 1", v -> v(1 + 2 + 1), new Add(c(1), new Add(c(2), c(1)))) + .advanced("($x - ($x - 1))", "$x - ($x - 1)", v -> x(v).subtract(x(v).subtract(v(1))), new Subtract(vx, new Subtract(vx, c(1)))) + .advanced("(2 * ($x / 1))", "2 * ($x / 1)", v -> v(2).multiply(x(v).divide(v(1))), new Multiply(c(2), new Divide(vx, c(1)))) + .advanced("(2 / ($x - 1))", "2 / ($x - 1)", v -> v(2).divide(x(v).subtract(v(1))), new Divide(c(2), new Subtract(vx, c(1)))) + .advanced("((1 * 2) + $x)", "1 * 2 + $x", v -> v(1 * 2).add(x(v)), new Add(new Multiply(c(1), c(2)), vx)) + .advanced("(($x - 1) - 2)", "$x - 1 - 2", v -> x(v).subtract(v(3)), new Subtract(new Subtract(vx, c(1)), c(2))) + .advanced("(($x / 1) * 2)", "$x / 1 * 2", v -> x(v).multiply(v(2)), new Multiply(new Divide(vx, c(1)), c(2))) + .advanced("((2 + 1) / 1)", "(2 + 1) / 1", v -> v(3), new Divide(new Add(c(2), c(1)), c(1))) + .advanced( + "(1 + (1 + (2 + 1)))", + "1 + 1 + 2 + 1", + v -> v(1 + 1 + 2 + 1), + new Add(c(1), new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "($x - ((1 * 2) + $x))", + "$x - (1 * 2 + $x)", + v -> x(v).subtract(v(1 * 2).add(x(v))), + new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx)) + ) + .advanced( + "($x * (2 / ($x - 1)))", + "$x * (2 / ($x - 1))", + v -> x(v).multiply(v(2).divide(x(v).subtract(v(1)))), + new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1)))) + ) + .advanced( + "($x / (1 + (2 + 1)))", + "$x / (1 + 2 + 1)", + v -> x(v).divide(v(1 + 2 + 1)), + new Divide(vx, new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "((1 * 2) + (2 + 1))", + "1 * 2 + 2 + 1", + v -> v(1 * 2 + 2 + 1), + new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1))) + ) + .advanced( + "((2 + 1) - (2 + 1))", + "2 + 1 - (2 + 1)", + v -> v(2 + 1 - (2 + 1)), + new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1))) + ) + .advanced( + "(($x - 1) * ($x / 1))", + "($x - 1) * ($x / 1)", + v -> x(v).subtract(v(1)).multiply(x(v).divide(v(1))), + new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1))) + ) + .advanced( + "(($x - 1) / (1 * 2))", + "($x - 1) / (1 * 2)", + v -> x(v).subtract(v(1)).divide(v(2)), + new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2))) + ) + .advanced( + "((($x - 1) - 2) + $x)", + "$x - 1 - 2 + $x", + v -> x(v).subtract(v(3)).add(x(v)), + new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx) + ) + .advanced( + "(((1 * 2) + $x) - 1)", + "1 * 2 + $x - 1", + v -> v(1).add(x(v)), + new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1)) + ) + .advanced( + "(((2 + 1) / 1) * $x)", + "(2 + 1) / 1 * $x", + v -> v(3).multiply(x(v)), + new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx) + ) + .advanced( + "((2 / ($x - 1)) / 2)", + "2 / ($x - 1) / 2", + v -> v(2).divide(x(v).subtract(v(1))).divide(v(2)), + new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2)) + ); + } + + private static BigDecimal x(final List vars) { + return vars.get(0); + } + + private static BigDecimal y(final List vars) { + return vars.get(1); + } + + private static Const c(final BigDecimal v) { + return TYPE.constant(v); + } + + private static Const c(final long v) { + return TYPE.constant(v(v)); + } + + private static BigDecimal v(final long v) { + return BigDecimal.valueOf(v); + } + + static void main(final String... args) { + TripleExpression.SELECTOR + .variant("BigDecimalList", ExpressionTest.v(BigDecimalListExpression::tester)) + .main(args); + } +} diff --git a/java/expression/BigIntegerListExpression.java b/java/expression/BigIntegerListExpression.java new file mode 100644 index 0000000..4f74521 --- /dev/null +++ b/java/expression/BigIntegerListExpression.java @@ -0,0 +1,192 @@ +package expression; + +import base.Asserts; +import base.Pair; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; + +import java.math.BigInteger; +import java.util.List; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +@SuppressWarnings("ClassReferencesSubclass") +public interface BigIntegerListExpression extends ToMiniString { + BigInteger evaluateBi(List variables); + + // Tests follow. You may temporarily remove everything til the end. + + Add EXAMPLE = new Add( + new Subtract(new Variable(0), new Const(BigInteger.ONE)), + new Multiply(new Variable(1), new Const(BigInteger.TEN)) + ); + + Type TYPE = new Type<>(BigInteger::valueOf, random -> v(random.getRandom().nextLong()), BigInteger.class); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + BigIntegerListExpression.class, + (r, c) -> IntStream.range(0, c) + .mapToObj(name -> Pair.of("$" + name, new Variable(name))) + .toList(), + (expr, variables, values) -> expr.evaluateBi(values) + ); + + @SuppressWarnings("PointlessArithmeticExpression") + static ExpressionTester tester(final TestCounter counter) { + Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString()); + Asserts.assertEquals( + EXAMPLE + " at (2, 3)", + BigInteger.valueOf(31), + EXAMPLE.evaluateBi(List.of(BigInteger.valueOf(2), BigInteger.valueOf(3))) + ); + + final Variable vx = new Variable(0); + final Variable vy = new Variable(1); + + return new ExpressionTester<>( + counter, KIND, c -> v -> c, + (op, a, b) -> v -> op.apply(a.evaluateBi(v), b.evaluateBi(v)), + BigInteger::add, BigInteger::subtract, BigInteger::multiply, BigInteger::divide + ) + .basic("10", "10", v -> v(10), c(10)) + .basic("$x", "$x", BigIntegerListExpression::x, vx) + .basic("$y", "$y", BigIntegerListExpression::y, vy) + .basic("($x + $y)", "$x + $y", v -> x(v).add(y(v)), new Add(vx, vy)) + .basic("($x + 2)", "$x + 2", v -> x(v).add(v(2)), new Add(vx, c(2))) + .basic("(2 - $x)", "2 - $x", v -> v(2).subtract(x(v)), new Subtract(c(2), vx)) + .basic("(3 * $x)", "3 * $x", v -> v(3).multiply(x(v)), new Multiply(c(3), vx)) + .basic("($x + $x)", "$x + $x", v -> x(v).add(x(v)), new Add(vx, vx)) + .basic("($x / -2)", "$x / -2", v -> x(v).divide(v(-2)), new Divide(vx, c(-2))) + .basic("(2 + $x)", "2 + $x", v -> v(2).add(x(v)), new Add(c(2), vx)) + .basic("((1 + 2) + 3)", "1 + 2 + 3", v -> v(6), new Add(new Add(c(1), c(2)), c(3))) + .basic("(1 + (2 * 3))", "1 + 2 * 3", v -> v(7), new Add(c(1), new Multiply(c(2), c(3)))) + .basic("(1 - (2 * 3))", "1 - 2 * 3", v -> v(-5), new Subtract(c(1), new Multiply(c(2), c(3)))) + .basic("(1 + (2 + 3))", "1 + 2 + 3", v -> v(6), new Add(c(1), new Add(c(2), c(3)))) + .basic("((1 - 2) - 3)", "1 - 2 - 3", v -> v(-4), new Subtract(new Subtract(c(1), c(2)), c(3))) + .basic("(1 - (2 - 3))", "1 - (2 - 3)", v -> v(2), new Subtract(c(1), new Subtract(c(2), c(3)))) + .basic("((1 * 2) * 3)", "1 * 2 * 3", v -> v(6), new Multiply(new Multiply(c(1), c(2)), c(3))) + .basic("(1 * (2 * 3))", "1 * 2 * 3", v -> v(6), new Multiply(c(1), new Multiply(c(2), c(3)))) + .basic("((10 / 2) / 3)", "10 / 2 / 3", v -> v(10 / 2 / 3), new Divide(new Divide(c(10), c(2)), c(3))) + .basic("(10 / (3 / 2))", "10 / (3 / 2)", v -> v(10 / (3 / 2)), new Divide(c(10), new Divide(c(3), c(2)))) + .basic("(($x * $x) + (($x - 1) / 10))", + "$x * $x + ($x - 1) / 10", + v -> x(v).multiply(x(v)).add(x(v).subtract(v(1)).divide(v(10))), + new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10))) + ) + .basic("($x * -1000000000)", "$x * -1000000000", v -> x(v).multiply(v(-1_000_000_000)), new Multiply(vx, c(-1_000_000_000))) + .basic("($x * -1000000000000000)", "$x * -1000000000000000", v -> x(v).multiply(v(-1_000_000_000_000_000L)), new Multiply(vx, c(-1_000_000_000_000_000L))) + .basic("(10 / $x)", "10 / $x", v -> v(10).divide(x(v)), new Divide(c(10), vx)) + .basic("($x / $x)", "$x / $x", v -> x(v).divide(x(v)), new Divide(vx, vx)) + + .advanced("(2 + 1)", "2 + 1", v -> v(2 + 1), new Add(c(2), c(1))) + .advanced("($x - 1)", "$x - 1", v -> x(v).subtract(v(1)), new Subtract(vx, c(1))) + .advanced("(1 * 2)", "1 * 2", v -> v(1 * 2), new Multiply(c(1), c(2))) + .advanced("($x / 1)", "$x / 1", v -> x(v).divide(v(1)), new Divide(vx, c(1))) + .advanced("(1 + (2 + 1))", "1 + 2 + 1", v -> v(1 + 2 + 1), new Add(c(1), new Add(c(2), c(1)))) + .advanced("($x - ($x - 1))", "$x - ($x - 1)", v -> x(v).subtract(x(v).subtract(v(1))), new Subtract(vx, new Subtract(vx, c(1)))) + .advanced("(2 * ($x / 1))", "2 * ($x / 1)", v -> v(2).multiply(x(v).divide(v(1))), new Multiply(c(2), new Divide(vx, c(1)))) + .advanced("(2 / ($x - 1))", "2 / ($x - 1)", v -> v(2).divide(x(v).subtract(v(1))), new Divide(c(2), new Subtract(vx, c(1)))) + .advanced("((1 * 2) + $x)", "1 * 2 + $x", v -> v(1 * 2).add(x(v)), new Add(new Multiply(c(1), c(2)), vx)) + .advanced("(($x - 1) - 2)", "$x - 1 - 2", v -> x(v).subtract(v(3)), new Subtract(new Subtract(vx, c(1)), c(2))) + .advanced("(($x / 1) * 2)", "$x / 1 * 2", v -> x(v).multiply(v(2)), new Multiply(new Divide(vx, c(1)), c(2))) + .advanced("((2 + 1) / 1)", "(2 + 1) / 1", v -> v(3), new Divide(new Add(c(2), c(1)), c(1))) + .advanced( + "(1 + (1 + (2 + 1)))", + "1 + 1 + 2 + 1", + v -> v(1 + 1 + 2 + 1), + new Add(c(1), new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "($x - ((1 * 2) + $x))", + "$x - (1 * 2 + $x)", + v -> x(v).subtract(v(1 * 2).add(x(v))), + new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx)) + ) + .advanced( + "($x * (2 / ($x - 1)))", + "$x * (2 / ($x - 1))", + v -> x(v).multiply(v(2).divide(x(v).subtract(v(1)))), + new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1)))) + ) + .advanced( + "($x / (1 + (2 + 1)))", + "$x / (1 + 2 + 1)", + v -> x(v).divide(v(1 + 2 + 1)), + new Divide(vx, new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "((1 * 2) + (2 + 1))", + "1 * 2 + 2 + 1", + v -> v(1 * 2 + 2 + 1), + new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1))) + ) + .advanced( + "((2 + 1) - (2 + 1))", + "2 + 1 - (2 + 1)", + v -> v(2 + 1 - (2 + 1)), + new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1))) + ) + .advanced( + "(($x - 1) * ($x / 1))", + "($x - 1) * ($x / 1)", + v -> x(v).subtract(v(1)).multiply(x(v).divide(v(1))), + new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1))) + ) + .advanced( + "(($x - 1) / (1 * 2))", + "($x - 1) / (1 * 2)", + v -> x(v).subtract(v(1)).divide(v(2)), + new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2))) + ) + .advanced( + "((($x - 1) - 2) + $x)", + "$x - 1 - 2 + $x", + v -> x(v).subtract(v(3)).add(x(v)), + new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx) + ) + .advanced( + "(((1 * 2) + $x) - 1)", + "1 * 2 + $x - 1", + v -> v(1).add(x(v)), + new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1)) + ) + .advanced( + "(((2 + 1) / 1) * $x)", + "(2 + 1) / 1 * $x", + v -> v(3).multiply(x(v)), + new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx) + ) + .advanced( + "((2 / ($x - 1)) / 2)", + "2 / ($x - 1) / 2", + v -> v(2).divide(x(v).subtract(v(1))).divide(v(2)), + new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2)) + ); + } + + private static BigInteger x(final List vars) { + return vars.get(0); + } + + private static BigInteger y(final List vars) { + return vars.get(1); + } + + private static Const c(final long v) { + return TYPE.constant(v(v)); + } + + private static BigInteger v(final long v) { + return BigInteger.valueOf(v); + } + + static void main(final String... args) { + TripleExpression.SELECTOR + .variant("BigIntegerList", ExpressionTest.v(BigIntegerListExpression::tester)) + .main(args); + } +} diff --git a/java/expression/Expression.java b/java/expression/Expression.java new file mode 100644 index 0000000..d52c16b --- /dev/null +++ b/java/expression/Expression.java @@ -0,0 +1,112 @@ +package expression; + +import base.Asserts; +import base.ExtendedRandom; +import base.Pair; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; + +import java.util.List; + +/** + * One-argument arithmetic expression over integers. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +@SuppressWarnings("ClassReferencesSubclass") +public interface Expression extends ToMiniString { + int evaluate(int x); + + // Tests follow. You may temporarily remove everything til the end. + + Subtract EXAMPLE = new Subtract( + new Multiply(new Const(2), new Variable("x")), + new Const(3) + ); + + Type TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + Expression.class, + List.of(Pair.of("x", new Variable("x"))), + (expr, variables, values) -> expr.evaluate(values.get(0)) + ); + + private static Const c(final int c) { + return new Const(c); + } + + @SuppressWarnings({"PointlessArithmeticExpression", "Convert2MethodRef"}) + static ExpressionTester tester(final TestCounter counter) { + Asserts.assertEquals("Example toString()", "((2 * x) - 3)", EXAMPLE.toString()); + Asserts.assertEquals("Example at 5", 7, EXAMPLE.evaluate(5)); + Asserts.assertTrue("Example equals 1", + new Multiply(new Const(2), new Variable("x")) + .equals(new Multiply(new Const(2), new Variable("x")))); + Asserts.assertTrue("Example equals 2", + !new Multiply(new Const(2), new Variable("x")) + .equals(new Multiply(new Variable("x"), new Const(2)))); + + final Variable vx = new Variable("x"); + final Const c1 = c(1); + final Const c2 = c(2); + + return new ExpressionTester<>( + counter, KIND, c -> x -> c, + (op, a, b) -> x -> op.apply(a.evaluate(x), b.evaluate(x)), + (a, b) -> a + b, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b + ) + .basic("10", "10", x -> 10, c(10)) + .basic("x", "x", x -> x, vx) + .basic("(x + 2)", "x + 2", x -> x + 2, new Add(vx, c(2))) + .basic("(2 - x)", "2 - x", x -> 2 - x, new Subtract(c(2), vx)) + .basic("(3 * x)", "3 * x", x -> 3*x, new Multiply(c(3), vx)) + .basic("(x + x)", "x + x", x -> x + x, new Add(vx, vx)) + .basic("(x / -2)", "x / -2", x -> -x / 2, new Divide(vx, c(-2))) + .basic("(2 + x)", "2 + x", x -> 2 + x, new Add(c(2), vx)) + .basic("((1 + 2) + 3)", "1 + 2 + 3", x -> 6, new Add(new Add(c(1), c(2)), c(3))) + .basic("(1 + (2 + 3))", "1 + 2 + 3", x -> 6, new Add(c(1), new Add(c(2), c(3)))) + .basic("((1 - 2) - 3)", "1 - 2 - 3", x -> -4, new Subtract(new Subtract(c(1), c(2)), c(3))) + .basic("(1 - (2 - 3))", "1 - (2 - 3)", x -> 2, new Subtract(c(1), new Subtract(c(2), c(3)))) + .basic("((1 * 2) * 3)", "1 * 2 * 3", x -> 6, new Multiply(new Multiply(c(1), c(2)), c(3))) + .basic("(1 * (2 * 3))", "1 * 2 * 3", x -> 6, new Multiply(c(1), new Multiply(c(2), c(3)))) + .basic("((10 / 2) / 3)", "10 / 2 / 3", x -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3))) + .basic("(10 / (3 / 2))", "10 / (3 / 2)", x -> 10 / (3 / 2), new Divide(c(10), new Divide(c(3), c(2)))) + .basic("(10 * (3 / 2))", "10 * (3 / 2)", x -> 10 * (3 / 2), new Multiply(c(10), new Divide(c(3), c(2)))) + .basic("(10 + (3 - 2))", "10 + 3 - 2", x -> 10 + (3 - 2), new Add(c(10), new Subtract(c(3), c(2)))) + .basic("((x * x) + ((x - 1) / 10))", "x * x + (x - 1) / 10", x -> x * x + (x - 1) / 10, new Add( + new Multiply(vx, vx), + new Divide(new Subtract(vx, c(1)), c(10)) + )) + .basic("(x * -1000000000)", "x * -1000000000", x -> x * -1_000_000_000, new Multiply(vx, c(-1_000_000_000))) + .basic("(10 / x)", "10 / x", x -> 10 / x, new Divide(c(10), vx)) + .basic("(x / x)", "x / x", x -> x / x, new Divide(vx, vx)) + + .advanced("(2 + 1)", "2 + 1", x -> 2 + 1, new Add(c2, c1)) + .advanced("(x - 1)", "x - 1", x -> x - 1, new Subtract(vx, c1)) + .advanced("(1 * 2)", "1 * 2", x -> 1 * 2, new Multiply(c1, c2)) + .advanced("(x / 1)", "x / 1", x -> x / 1, new Divide(vx, c1)) + .advanced("(1 + (2 + 1))", "1 + 2 + 1", x -> 1 + 2 + 1, new Add(c1, new Add(c2, c1))) + .advanced("(x - (x - 1))", "x - (x - 1)", x -> x - (x - 1), new Subtract(vx, new Subtract(vx, c1))) + .advanced("(2 * (x / 1))", "2 * (x / 1)", x -> 2 * (x / 1), new Multiply(c2, new Divide(vx, c1))) + .advanced("(2 / (x - 1))", "2 / (x - 1)", x -> 2 / (x - 1), new Divide(c2, new Subtract(vx, c1))) + .advanced("((1 * 2) + x)", "1 * 2 + x", x -> 1 * 2 + x, new Add(new Multiply(c1, c2), vx)) + .advanced("((x - 1) - 2)", "x - 1 - 2", x -> x - 1 - 2, new Subtract(new Subtract(vx, c1), c2)) + .advanced("((x / 1) * 2)", "x / 1 * 2", x -> x / 1 * 2, new Multiply(new Divide(vx, c1), c2)) + .advanced("((2 + 1) / 1)", "(2 + 1) / 1", x -> (2 + 1) / 1, new Divide(new Add(c2, c1), c1)) + .advanced("(1 + (1 + (2 + 1)))", "1 + 1 + 2 + 1", x -> 1 + 1 + 2 + 1, new Add(c1, new Add(c1, new Add(c2, c1)))) + .advanced("(x - ((1 * 2) + x))", "x - (1 * 2 + x)", x -> x - (1 * 2 + x), new Subtract(vx, new Add(new Multiply(c1, c2), vx))) + .advanced("(x * (2 / (x - 1)))", "x * (2 / (x - 1))", x -> x * (2 / (x - 1)), new Multiply(vx, new Divide(c2, new Subtract(vx, c1)))) + .advanced("(x / (1 + (2 + 1)))", "x / (1 + 2 + 1)", x -> x / (1 + 2 + 1), new Divide(vx, new Add(c1, new Add(c2, c1)))) + .advanced("((1 * 2) + (2 + 1))", "1 * 2 + 2 + 1", x -> 1 * 2 + 2 + 1, new Add(new Multiply(c1, c2), new Add(c2, c1))) + .advanced("((2 + 1) - (2 + 1))", "2 + 1 - (2 + 1)", x -> 2 + 1 - (2 + 1), new Subtract(new Add(c2, c1), new Add(c2, c1))) + .advanced("((x - 1) * (x / 1))", "(x - 1) * (x / 1)", x -> (x - 1) * (x / 1), new Multiply(new Subtract(vx, c1), new Divide(vx, c1))) + .advanced("((x - 1) / (1 * 2))", "(x - 1) / (1 * 2)", x -> (x - 1) / (1 * 2), new Divide(new Subtract(vx, c1), new Multiply(c1, c2))) + .advanced("(((x - 1) - 2) + x)", "x - 1 - 2 + x", x -> x - 1 - 2 + x, new Add(new Subtract(new Subtract(vx, c1), c2), vx)) + .advanced("(((1 * 2) + x) - 1)", "1 * 2 + x - 1", x -> 1 * 2 + x - 1, new Subtract(new Add(new Multiply(c1, c2), vx), c1)) + .advanced("(((2 + 1) / 1) * x)", "(2 + 1) / 1 * x", x -> (2 + 1) / 1 * x, new Multiply(new Divide(new Add(c2, c1), c1), vx)) + .advanced("((2 / (x - 1)) / 2)", "2 / (x - 1) / 2", x -> 2 / (x - 1) / 2, new Divide(new Divide(c2, new Subtract(vx, c1)), c2)); + } +} diff --git a/java/expression/ExpressionTest.java b/java/expression/ExpressionTest.java new file mode 100644 index 0000000..ce24173 --- /dev/null +++ b/java/expression/ExpressionTest.java @@ -0,0 +1,26 @@ +package expression; + +import base.Selector; +import base.TestCounter; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ExpressionTest { + public static final Selector SELECTOR = new Selector(ExpressionTest.class, "easy", "hard") + .variant("Base", v(Expression::tester)); + + private ExpressionTest() { + } + + public static Consumer v(final Function> tester) { + return t -> tester.apply(t).test(); + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/expression/ExpressionTester.java b/java/expression/ExpressionTester.java new file mode 100644 index 0000000..28e789f --- /dev/null +++ b/java/expression/ExpressionTester.java @@ -0,0 +1,288 @@ +package expression; + +import base.*; +import expression.common.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static base.Asserts.assertTrue; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ExpressionTester extends Tester { + private final List VALUES = IntStream.rangeClosed(-10, 10).boxed().toList(); + private final ExpressionKind kind; + + private final List basic = new ArrayList<>(); + private final List advanced = new ArrayList<>(); + private final Set used = new HashSet<>(); + private final GeneratorBuilder generator; + + private final List> prev = new ArrayList<>(); + private final Map mappings; + + protected ExpressionTester( + final TestCounter counter, + final ExpressionKind kind, + final Function expectedConstant, + final Binary binary, + final BinaryOperator add, + final BinaryOperator sub, + final BinaryOperator mul, + final BinaryOperator div, + final Map mappings + ) { + super(counter); + this.kind = kind; + this.mappings = mappings; + + generator = new GeneratorBuilder(expectedConstant, kind::constant, binary, kind::randomValue); + generator.binary("+", 1600, add, Add.class); + generator.binary("-", 1602, sub, Subtract.class); + generator.binary("*", 2001, mul, Multiply.class); + generator.binary("/", 2002, div, Divide.class); + } + + protected ExpressionTester( + final TestCounter counter, + final ExpressionKind kind, + final Function expectedConstant, + final Binary binary, + final BinaryOperator add, + final BinaryOperator sub, + final BinaryOperator mul, + final BinaryOperator div + ) { + this(counter, kind, expectedConstant, binary, add, sub, mul, div, Map.of()); + } + + @Override + public String toString() { + return kind.getName(); + } + + @Override + public void test() { + counter.scope("Basic tests", () -> basic.forEach(Test::test)); + counter.scope("Advanced tests", () -> advanced.forEach(Test::test)); + counter.scope("Random tests", generator::testRandom); + } + + @SuppressWarnings({"ConstantValue", "EqualsWithItself"}) + private void checkEqualsAndToString(final String full, final String mini, final ToMiniString expression, final ToMiniString copy) { + checkToString("toString", full, expression.toString()); + if (mode() > 0) { + checkToString("toMiniString", mini, expression.toMiniString()); + } + + counter.test(() -> { + assertTrue("Equals to this", expression.equals(expression)); + assertTrue("Equals to copy", expression.equals(copy)); + assertTrue("Equals to null", !expression.equals(null)); + assertTrue("Copy equals to null", !copy.equals(null)); + }); + + final String expressionToString = Objects.requireNonNull(expression.toString()); + for (final Pair pair : prev) { + counter.test(() -> { + final ToMiniString prev = pair.first(); + final String prevToString = pair.second(); + final boolean equals = prevToString.equals(expressionToString); + assertTrue("Equals to " + prevToString, prev.equals(expression) == equals); + assertTrue("Equals to " + prevToString, expression.equals(prev) == equals); + assertTrue("Inconsistent hashCode for " + prev + " and " + expression, (prev.hashCode() == expression.hashCode()) == equals); + }); + } + } + + private void checkToString(final String method, final String expected, final String actual) { + counter.test(() -> assertTrue(String.format("Invalid %s\n expected: %s\n actual: %s", method, expected, actual), expected.equals(actual))); + } + + private void check( + final String full, + final E expected, + final E actual, + final List variables, + final List values + ) { + final String vars = IntStream.range(0, variables.size()) + .mapToObj(i -> variables.get(i) + "=" + values.get(i)) + .collect(Collectors.joining(",")); + counter.test(() -> { + final Object expectedResult = evaluate(expected, variables, values); + final Object actualResult = evaluate(actual, variables, values); + final String reason = String.format( + "%s:%n expected `%s`,%n actual `%s`", + String.format("f(%s)\nwhere f is %s", vars, full), + Asserts.toString(expectedResult), + Asserts.toString(actualResult) + ); + if ( + expectedResult != null && actualResult != null && + expectedResult.getClass() == actualResult.getClass() + && (expectedResult.getClass() == Double.class || expectedResult.getClass() == Float.class) + ) { + final double expectedValue = ((Number) expectedResult).doubleValue(); + final double actualValue = ((Number) actualResult).doubleValue(); + Asserts.assertEquals(reason, expectedValue, actualValue, 1e-6); + } else { + assertTrue(reason, Objects.deepEquals(expectedResult, actualResult)); + } + }); + } + + private Object evaluate(final E expression, final List variables, final List values) { + try { + return kind.evaluate(expression, variables, values); + } catch (final Exception e) { + return e.getClass().getName(); + } + } + + protected ExpressionTester basic(final String full, final String mini, final E expected, final E actual) { + return basicF(full, mini, expected, vars -> actual); + } + + protected ExpressionTester basicF(final String full, final String mini, final E expected, final Function, E> actual) { + return basic(new Test(full, mini, expected, actual)); + } + + private ExpressionTester basic(final Test test) { + Asserts.assertTrue(test.full, used.add(test.full)); + basic.add(test); + return this; + } + + protected ExpressionTester advanced(final String full, final String mini, final E expected, final E actual) { + return advancedF(full, mini, expected, vars -> actual); + } + + protected ExpressionTester advancedF(final String full, final String mini, final E expected, final Function, E> actual) { + Asserts.assertTrue(full, used.add(full)); + advanced.add(new Test(full, mini, expected, actual)); + return this; + } + + protected static Named variable(final String name, final E expected) { + return Named.of(name, expected); + } + + @FunctionalInterface + public interface Binary { + E apply(BinaryOperator op, E a, E b); + } + + private final class Test { + private final String full; + private final String mini; + private final E expected; + private final Function, E> actual; + + private Test(final String full, final String mini, final E expected, final Function, E> actual) { + this.full = full; + this.mini = mini; + this.expected = expected; + this.actual = actual; + } + + private void test() { + final List> variables = kind.variables().generate(random(), 3); + final List names = Functional.map(variables, Pair::first); + final E actual = kind.cast(this.actual.apply(names)); + final String full = mangle(this.full, names); + final String mini = mangle(this.mini, names); + + counter.test(() -> { + kind.allValues(variables.size(), VALUES).forEach(values -> check(mini, expected, actual, names, values)); + checkEqualsAndToString(full, mini, actual, actual); + prev.add(Pair.of(actual, full)); + }); + } + + private String mangle(String string, final List names) { + for (int i = 0; i < names.size(); i++) { + string = string.replace("$" + (char) ('x' + i), names.get(i)); + } + for (final Map.Entry mapping : mappings.entrySet()) { + string = string.replace(mapping.getKey(), mapping.getValue().toString()); + } + return string; + } + } + + private final class GeneratorBuilder { + private final Generator.Builder generator; + private final NodeRendererBuilder renderer = new NodeRendererBuilder<>(random()); + private final Renderer.Builder expected; + private final Renderer.Builder actual; + private final Renderer.Builder copy; + private final Binary binary; + + private GeneratorBuilder( + final Function expectedConstant, + final Function actualConstant, + final Binary binary, + final Function randomValue + ) { + generator = Generator.builder(() -> randomValue.apply(random()), random()); + expected = Renderer.builder(expectedConstant::apply); + actual = Renderer.builder(actualConstant::apply); + copy = Renderer.builder(actualConstant::apply); + + this.binary = binary; + } + + private void binary(final String name, final int priority, final BinaryOperator op, final Class type) { + generator.add(name, 2); + renderer.binary(name, priority); + + expected.binary(name, (unit, a, b) -> binary.apply(op, a, b)); + + @SuppressWarnings("unchecked") final Constructor constructor = (Constructor) Arrays.stream(type.getConstructors()) + .filter(cons -> Modifier.isPublic(cons.getModifiers())) + .filter(cons -> cons.getParameterCount() == 2) + .findFirst() + .orElseGet(() -> counter.fail("%s(..., ...) constructor not found", type.getSimpleName())); + final Renderer.BinaryOperator actual = (unit, a, b) -> { + try { + return constructor.newInstance(a, b); + } catch (final Exception e) { + return counter.fail(e); + } + }; + this.actual.binary(name, actual); + copy.binary(name, actual); + } + + private void testRandom() { + final NodeRenderer renderer = this.renderer.build(); + final Renderer expectedRenderer = this.expected.build(); + final Renderer actualRenderer = this.actual.build(); + final expression.common.Generator generator = this.generator.build(kind.variables(), List.of()); + generator.testRandom(counter, 1, expr -> { + final String full = renderer.render(expr, NodeRenderer.FULL); + final String mini = renderer.render(expr, NodeRenderer.MINI); + final E expected = expectedRenderer.render(expr, Unit.INSTANCE); + final E actual = actualRenderer.render(expr, Unit.INSTANCE); + + final List> variables = expr.variables(); + final List names = Functional.map(variables, Pair::first); + final List values = Stream.generate(() -> kind.randomValue(random())) + .limit(variables.size()) + .toList(); + + checkEqualsAndToString(full, mini, actual, copy.build().render(expr, Unit.INSTANCE)); + check(full, expected, actual, names, values); + }); + } + } +} diff --git a/java/expression/ListExpression.java b/java/expression/ListExpression.java new file mode 100644 index 0000000..7f5ff95 --- /dev/null +++ b/java/expression/ListExpression.java @@ -0,0 +1,211 @@ +package expression; + +import base.Asserts; +import base.ExtendedRandom; +import base.Pair; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; + +import java.util.List; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("ClassReferencesSubclass") +@FunctionalInterface +public interface ListExpression extends ToMiniString { + int evaluate(List variables); + + // Tests follow. You may temporarily remove everything til the end. + + Add EXAMPLE = new Add( + new Subtract(new Variable(0), new Const(1)), + new Multiply(new Variable(1), new Const(10)) + ); + + Type TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + ListExpression.class, + (r, c) -> IntStream.range(0, c) + .mapToObj(name -> Pair.of("$" + name, new Variable(name))) + .toList(), + (expr, variables, values) -> expr.evaluate(values) + ); + + private static Const c(final Integer c) { + return TYPE.constant(c); + } + + @SuppressWarnings({"PointlessArithmeticExpression", "Convert2MethodRef"}) + static ExpressionTester tester(final TestCounter counter) { + Asserts.assertEquals("Example toString()", "(($0 - 1) + ($1 * 10))", EXAMPLE.toString()); + Asserts.assertEquals(EXAMPLE + " at (2, 3)", 31, EXAMPLE.evaluate(List.of(2, 3))); + + final Variable vx = new Variable(0); + return new ExpressionTester<>( + counter, KIND, c -> vars -> c, + (op, a, b) -> vars -> op.apply(a.evaluate(vars), b.evaluate(vars)), + (a, b) -> a + b, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b + ) + .basic("10", "10", vars -> 10, c(10)) + .basic("$x", "$x", ListExpression::x, vx) + .basic("($x + 2)", "$x + 2", vars -> x(vars) + 2, new Add(vx, c(2))) + .basic("(2 - $x)", "2 - $x", vars -> 2 - x(vars), new Subtract(c(2), vx)) + .basic("(3 * $x)", "3 * $x", vars -> 3 * x(vars), new Multiply(c(3), vx)) + .basic("($x + $x)", "$x + $x", vars -> x(vars) + x(vars), new Add(vx, vx)) + .basic("($x / -2)", "$x / -2", vars -> -x(vars) / 2, new Divide(vx, c(-2))) + .basic("(2 + $x)", "2 + $x", vars -> 2 + x(vars), new Add(c(2), vx)) + .basic("((1 + 2) + 3)", "1 + 2 + 3", vars -> 6, new Add(new Add(c(1), c(2)), c(3))) + .basic("(1 + (2 + 3))", "1 + 2 + 3", vars -> 6, new Add(c(1), new Add(c(2), c(3)))) + .basic("((1 - 2) - 3)", "1 - 2 - 3", vars -> -4, new Subtract(new Subtract(c(1), c(2)), c(3))) + .basic("(1 - (2 - 3))", "1 - (2 - 3)", vars -> 2, new Subtract(c(1), new Subtract(c(2), c(3)))) + .basic("((1 * 2) * 3)", "1 * 2 * 3", vars -> 6, new Multiply(new Multiply(c(1), c(2)), c(3))) + .basic("(1 * (2 * 3))", "1 * 2 * 3", vars -> 6, new Multiply(c(1), new Multiply(c(2), c(3)))) + .basic("((10 / 2) / 3)", "10 / 2 / 3", vars -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3))) + .basic("(10 / (3 / 2))", "10 / (3 / 2)", vars -> 10 / (3 / 2), new Divide(c(10), new Divide(c(3), c(2)))) + .basic("(10 * (3 / 2))", "10 * (3 / 2)", vars -> 10 * (3 / 2), new Multiply(c(10), new Divide(c(3), c(2)))) + .basic("(10 + (3 - 2))", "10 + 3 - 2", vars -> 10 + (3 - 2), new Add(c(10), new Subtract(c(3), c(2)))) + .basic( + "(($x * $x) + (($x - 1) / 10))", + "$x * $x + ($x - 1) / 10", + vars -> x(vars) * x(vars) + (x(vars) - 1) / 10, + new Add(new Multiply(vx, vx), new Divide(new Subtract(vx, c(1)), c(10))) + ) + .basic( + "($x * -1000000000)", + "$x * -1000000000", + vars -> x(vars) * -1_000_000_000, + new Multiply(vx, c(-1_000_000_000)) + ) + .basic("(10 / $x)", "10 / $x", vars -> 10 / x(vars), new Divide(c(10), vx)) + .basic("($x / $x)", "$x / $x", vars -> x(vars) / x(vars), new Divide(vx, vx)) + + .advanced("(2 + 1)", "2 + 1", vars -> 2 + 1, new Add(c(2), c(1))) + .advanced("($x - 1)", "$x - 1", vars -> x(vars) - 1, new Subtract(vx, c(1))) + .advanced("(1 * 2)", "1 * 2", vars -> 1 * 2, new Multiply(c(1), c(2))) + .advanced("($x / 1)", "$x / 1", vars -> x(vars) / 1, new Divide(vx, c(1))) + .advanced("(1 + (2 + 1))", "1 + 2 + 1", vars -> 1 + 2 + 1, new Add(c(1), new Add(c(2), c(1)))) + .advanced( + "($x - ($x - 1))", + "$x - ($x - 1)", + vars -> x(vars) - (x(vars) - 1), + new Subtract(vx, new Subtract(vx, c(1))) + ) + .advanced( + "(2 * ($x / 1))", + "2 * ($x / 1)", + vars -> 2 * (x(vars) / 1), + new Multiply(c(2), new Divide(vx, c(1))) + ) + .advanced( + "(2 / ($x - 1))", + "2 / ($x - 1)", + vars -> 2 / (x(vars) - 1), + new Divide(c(2), new Subtract(vx, c(1))) + ) + .advanced( + "((1 * 2) + $x)", + "1 * 2 + $x", + vars -> 1 * 2 + x(vars), + new Add(new Multiply(c(1), c(2)), vx) + ) + .advanced( + "(($x - 1) - 2)", + "$x - 1 - 2", + vars -> x(vars) - 1 - 2, + new Subtract(new Subtract(vx, c(1)), c(2)) + ) + .advanced( + "(($x / 1) * 2)", + "$x / 1 * 2", + vars -> x(vars) / 1 * 2, + new Multiply(new Divide(vx, c(1)), c(2)) + ) + .advanced("((2 + 1) / 1)", "(2 + 1) / 1", vars -> (2 + 1) / 1, new Divide(new Add(c(2), c(1)), c(1))) + .advanced( + "(1 + (1 + (2 + 1)))", + "1 + 1 + 2 + 1", + vars -> 1 + 1 + 2 + 1, + new Add(c(1), new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "($x - ((1 * 2) + $x))", + "$x - (1 * 2 + $x)", + vars -> x(vars) - (1 * 2 + x(vars)), + new Subtract(vx, new Add(new Multiply(c(1), c(2)), vx)) + ) + .advanced( + "($x * (2 / ($x - 1)))", + "$x * (2 / ($x - 1))", + vars -> x(vars) * (2 / (x(vars) - 1)), + new Multiply(vx, new Divide(c(2), new Subtract(vx, c(1)))) + ) + .advanced( + "($x / (1 + (2 + 1)))", + "$x / (1 + 2 + 1)", + vars -> x(vars) / (1 + 2 + 1), + new Divide(vx, new Add(c(1), new Add(c(2), c(1)))) + ) + .advanced( + "((1 * 2) + (2 + 1))", + "1 * 2 + 2 + 1", + vars -> 1 * 2 + 2 + 1, + new Add(new Multiply(c(1), c(2)), new Add(c(2), c(1))) + ) + .advanced( + "((2 + 1) - (2 + 1))", + "2 + 1 - (2 + 1)", + vars -> 2 + 1 - (2 + 1), + new Subtract(new Add(c(2), c(1)), new Add(c(2), c(1))) + ) + .advanced( + "(($x - 1) * ($x / 1))", + "($x - 1) * ($x / 1)", + vars -> (x(vars) - 1) * (x(vars) / 1), + new Multiply(new Subtract(vx, c(1)), new Divide(vx, c(1))) + ) + .advanced( + "(($x - 1) / (1 * 2))", + "($x - 1) / (1 * 2)", + vars -> (x(vars) - 1) / (1 * 2), + new Divide(new Subtract(vx, c(1)), new Multiply(c(1), c(2))) + ) + .advanced( + "((($x - 1) - 2) + $x)", + "$x - 1 - 2 + $x", + vars -> x(vars) - 1 - 2 + x(vars), + new Add(new Subtract(new Subtract(vx, c(1)), c(2)), vx) + ) + .advanced( + "(((1 * 2) + $x) - 1)", + "1 * 2 + $x - 1", + vars -> 1 * 2 + x(vars) - 1, + new Subtract(new Add(new Multiply(c(1), c(2)), vx), c(1)) + ) + .advanced( + "(((2 + 1) / 1) * $x)", + "(2 + 1) / 1 * $x", + vars -> (2 + 1) / 1 * x(vars), + new Multiply(new Divide(new Add(c(2), c(1)), c(1)), vx) + ) + .advanced( + "((2 / ($x - 1)) / 2)", + "2 / ($x - 1) / 2", + vars -> 2 / (x(vars) - 1) / 2, + new Divide(new Divide(c(2), new Subtract(vx, c(1))), c(2)) + ); + } + + private static Integer x(final List vars) { + return vars.get(0); + } + + static void main(final String... args) { + TripleExpression.SELECTOR + .variant("List", ExpressionTest.v(ListExpression::tester)) + .main(args); + } +} diff --git a/java/expression/ToMiniString.java b/java/expression/ToMiniString.java new file mode 100644 index 0000000..60a655c --- /dev/null +++ b/java/expression/ToMiniString.java @@ -0,0 +1,10 @@ +package expression; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public interface ToMiniString { + default String toMiniString() { + return toString(); + } +} diff --git a/java/expression/TripleExpression.java b/java/expression/TripleExpression.java new file mode 100644 index 0000000..efc6705 --- /dev/null +++ b/java/expression/TripleExpression.java @@ -0,0 +1,190 @@ +package expression; + +import base.ExtendedRandom; +import base.Pair; +import base.Selector; +import base.TestCounter; +import expression.common.ExpressionKind; +import expression.common.Type; + +import java.util.List; + +/** + * Three-argument arithmetic expression over integers. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +@SuppressWarnings("ClassReferencesSubclass") +public interface TripleExpression extends ToMiniString { + int evaluate(int x, int y, int z); + + // Tests follow. You may temporarily remove everything til the end. + + Type TYPE = new Type<>(a -> a, ExtendedRandom::nextInt, int.class); + ExpressionKind KIND = new ExpressionKind<>( + TYPE, + TripleExpression.class, + List.of( + Pair.of("x", new Variable("x")), + Pair.of("y", new Variable("y")), + Pair.of("z", new Variable("z")) + ), + (expr, variables, values) -> expr.evaluate(values.get(0), values.get(1), values.get(2)) + ); + + @SuppressWarnings("PointlessArithmeticExpression") + static ExpressionTester tester(final TestCounter counter) { + final Variable vx = new Variable("x"); + final Variable vy = new Variable("y"); + final Variable vz = new Variable("z"); + + return new ExpressionTester<>( + counter, KIND, c -> (x, y, z) -> c, + (op, a, b) -> (x, y, z) -> op.apply(a.evaluate(x, y, z), b.evaluate(x, y, z)), + Integer::sum, (a, b) -> a - b, (a, b) -> a * b, (a, b) -> a / b + ) + .basic("10", "10", (x, y, z) -> 10, c(10)) + .basic("x", "x", (x, y, z) -> x, vx) + .basic("y", "y", (x, y, z) -> y, vy) + .basic("z", "z", (x, y, z) -> z, vz) + .basic("(x + 2)", "x + 2", (x, y, z) -> x + 2, new Add(vx, c(2))) + .basic("(2 - y)", "2 - y", (x, y, z) -> 2 - y, new Subtract(c(2), vy)) + .basic("(3 * z)", "3 * z", (x, y, z) -> 3 * z, new Multiply(c(3), vz)) + .basic("(x / -2)", "x / -2", (x, y, z) -> -x / 2, new Divide(vx, c(-2))) + .basic("((1 + 2) + 3)", "1 + 2 + 3", (x, y, z) -> 6, new Add(new Add(c(1), c(2)), c(3))) + .basic("(1 + (2 + 3))", "1 + 2 + 3", (x, y, z) -> 6, new Add(c(1), new Add(c(2), c(3)))) + .basic("((1 - 2) - 3)", "1 - 2 - 3", (x, y, z) -> -4, new Subtract(new Subtract(c(1), c(2)), c(3))) + .basic("(1 - (2 - 3))", "1 - (2 - 3)", (x, y, z) -> 2, new Subtract(c(1), new Subtract(c(2), c(3)))) + .basic("((1 * 2) * 3)", "1 * 2 * 3", (x, y, z) -> 6, new Multiply(new Multiply(c(1), c(2)), c(3))) + .basic("(1 * (2 * 3))", "1 * 2 * 3", (x, y, z) -> 6, new Multiply(c(1), new Multiply(c(2), c(3)))) + .basic("((10 / 2) / 3)", "10 / 2 / 3", (x, y, z) -> 10 / 2 / 3, new Divide(new Divide(c(10), c(2)), c(3))) + .basic("(10 / (3 / 2))", "10 / (3 / 2)", (x, y, z) -> 10, new Divide(c(10), new Divide(c(3), c(2)))) + .basic("((x * y) + ((z - 1) / 10))", "x * y + (z - 1) / 10", (x, y, z) -> x * y + (z - 1) / 10, new Add( + new Multiply(vx, vy), + new Divide(new Subtract(vz, c(1)), c(10)) + )) + .basic("(x + y)", "x + y", (x, y, z) -> x + y, new Add(vx, vy)) + .basic("(y + x)", "y + x", (x, y, z) -> y + x, new Add(vy, vx)) + + .advanced("(1 + 1)", "1 + 1", (x, y, z) -> 1 + 1, new Add(c(1), c(1))) + .advanced("(y - x)", "y - x", (x, y, z) -> y - x, new Subtract(vy, vx)) + .advanced("(2 * x)", "2 * x", (x, y, z) -> 2 * x, new Multiply(c(2), vx)) + .advanced("(2 / x)", "2 / x", (x, y, z) -> 2 / x, new Divide(c(2), vx)) + .advanced("(z + (1 + 1))", "z + 1 + 1", (x, y, z) -> z + 1 + 1, new Add(vz, new Add(c(1), c(1)))) + .advanced( + "(2 - (y - x))", + "2 - (y - x)", + (x, y, z) -> 2 - (y - x), + new Subtract(c(2), new Subtract(vy, vx)) + ) + .advanced( + "(z * (2 / x))", + "z * (2 / x)", + (x, y, z) -> z * (2 / x), + new Multiply(vz, new Divide(c(2), vx)) + ) + .advanced( + "(z / (y - x))", + "z / (y - x)", + (x, y, z) -> z / (y - x), + new Divide(vz, new Subtract(vy, vx)) + ) + .advanced("((2 * x) + y)", "2 * x + y", (x, y, z) -> 2 * x + y, new Add(new Multiply(c(2), vx), vy)) + .advanced( + "((y - x) - 2)", + "y - x - 2", + (x, y, z) -> y - x - 2, + new Subtract(new Subtract(vy, vx), c(2)) + ) + .advanced("((2 / x) * y)", "2 / x * y", (x, y, z) -> 2 / x * y, new Multiply(new Divide(c(2), vx), vy)) + .advanced("((1 + 1) / x)", "(1 + 1) / x", (x, y, z) -> (1 + 1) / x, new Divide(new Add(c(1), c(1)), vx)) + .advanced("(1 + (2 * 3))", "1 + 2 * 3", (x, y, z) -> 7, new Add(c(1), new Multiply(c(2), c(3)))) + .advanced("(1 - (2 * 3))", "1 - 2 * 3", (x, y, z) -> -5, new Subtract(c(1), new Multiply(c(2), c(3)))) + .advanced("(1 + (2 / 3))", "1 + 2 / 3", (x, y, z) -> 1, new Add(c(1), new Divide(c(2), c(3)))) + .advanced("(1 - (2 / 3))", "1 - 2 / 3", (x, y, z) -> 1, new Subtract(c(1), new Divide(c(2), c(3)))) + .advanced( + "(2 + (z + (1 + 1)))", + "2 + z + 1 + 1", + (x, y, z) -> 2 + z + 1 + 1, + new Add(c(2), new Add(vz, new Add(c(1), c(1)))) + ) + .advanced( + "(1 - ((2 * x) + y))", + "1 - (2 * x + y)", + (x, y, z) -> 1 - (2 * x + y), + new Subtract(c(1), new Add(new Multiply(c(2), vx), vy)) + ) + .advanced( + "(1 * (z / (y - x)))", + "1 * (z / (y - x))", + (x, y, z) -> 1 * (z / (y - x)), + new Multiply(c(1), new Divide(vz, new Subtract(vy, vx))) + ) + .advanced( + "(z / (z + (1 + 1)))", + "z / (z + 1 + 1)", + (x, y, z) -> z / (z + 1 + 1), + new Divide(vz, new Add(vz, new Add(c(1), c(1)))) + ) + .advanced( + "((2 * x) + (1 + 1))", + "2 * x + 1 + 1", + (x, y, z) -> 2 * x + 1 + 1, + new Add(new Multiply(c(2), vx), new Add(c(1), c(1))) + ) + .advanced( + "((1 + 1) - (1 + 1))", + "1 + 1 - (1 + 1)", + (x, y, z) -> 1 + 1 - (1 + 1), + new Subtract(new Add(c(1), c(1)), new Add(c(1), c(1))) + ) + .advanced( + "((y - x) * (2 / x))", + "(y - x) * (2 / x)", + (x, y, z) -> (y - x) * (2 / x), + new Multiply(new Subtract(vy, vx), new Divide(c(2), vx)) + ) + .advanced( + "((y - x) / (2 * x))", + "(y - x) / (2 * x)", + (x, y, z) -> (y - x) / (2 * x), + new Divide(new Subtract(vy, vx), new Multiply(c(2), vx)) + ) + .advanced( + "(((y - x) - 2) + 1)", + "y - x - 2 + 1", + (x, y, z) -> y - x - 2 + 1, + new Add(new Subtract(new Subtract(vy, vx), c(2)), c(1)) + ) + .advanced( + "(((2 * x) + y) - z)", + "2 * x + y - z", + (x, y, z) -> 2 * x + y - z, + new Subtract(new Add(new Multiply(c(2), vx), vy), vz) + ) + .advanced( + "(((1 + 1) / x) * 2)", + "(1 + 1) / x * 2", + (x, y, z) -> (1 + 1) / x * 2, + new Multiply(new Divide(new Add(c(1), c(1)), vx), c(2)) + ) + .advanced( + "((z / (y - x)) / x)", + "z / (y - x) / x", + (x, y, z) -> z / (y - x) / x, + new Divide(new Divide(vz, new Subtract(vy, vx)), vx) + ); + } + + private static Const c(final Integer c) { + return TYPE.constant(c); + } + + Selector SELECTOR = ExpressionTest.SELECTOR + .variant("Triple", ExpressionTest.v(TripleExpression::tester)); + + static void main(final String... args) { + TripleExpression.SELECTOR.main(args); + } +} diff --git a/java/expression/common/Expr.java b/java/expression/common/Expr.java new file mode 100644 index 0000000..b1dd418 --- /dev/null +++ b/java/expression/common/Expr.java @@ -0,0 +1,32 @@ +package expression.common; + +import base.Functional; +import base.Pair; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public record Expr(Node node, List> variables) { + public List> variables(final BiFunction f) { + return Functional.map( + variables, + variable -> variable.second(f.apply(variable.first(), variable.second())) + ); + } + + public Expr convert(final BiFunction f) { + return of(node, variables(f)); + } + + public Expr node(final Function, Node> f) { + return of(f.apply(node), variables); + } + + public static Expr of(final Node node, final List> variables) { + return new Expr<>(node, variables); + } +} diff --git a/java/expression/common/ExpressionKind.java b/java/expression/common/ExpressionKind.java new file mode 100644 index 0000000..d8da0aa --- /dev/null +++ b/java/expression/common/ExpressionKind.java @@ -0,0 +1,94 @@ +package expression.common; + +import base.ExtendedRandom; +import base.Functional; +import base.Pair; +import expression.ToMiniString; + +import java.util.List; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ExpressionKind { + private final Type type; + private final Class kind; + private final Variables variables; + private final Evaluator evaluator; + + public ExpressionKind( + final Type type, + final Class kind, + final Variables variables, + final Evaluator evaluator + ) { + this.type = type; + this.kind = kind; + this.variables = variables; + this.evaluator = evaluator; + } + + public ExpressionKind( + final Type type, + final Class kind, + final List> variables, + final Evaluator evaluator + ) { + this(type, kind, (r, c) -> variables, evaluator); + } + + public C evaluate(final E expression, final List variables, final List values) throws Exception { + return evaluator.evaluate(expression, variables, values); + } + + public E cast(final Object expression) { + return kind.cast(expression); + } + + public String getName() { + return kind.getSimpleName(); + } + + public E constant(final C value) { + return cast(type.constant(value)); + } + + public C randomValue(final ExtendedRandom random) { + return type.randomValue(random); + } + + public List> allValues(final int length, final List values) { + return Functional.allValues(fromInts(values), length); + } + + public List fromInts(final List values) { + return Functional.map(values, this::fromInt); + } + + public C fromInt(final int value) { + return type.fromInt(value); + } + + @Override + public String toString() { + return kind.getName(); + } + + public ExpressionKind withVariables(final Variables variables) { + return new ExpressionKind<>(type, kind, variables, evaluator); + } + + public Variables variables() { + return variables; + } + + @FunctionalInterface + public interface Variables { + List> generate(final ExtendedRandom random, final int count); + } + + @FunctionalInterface + public interface Evaluator { + R evaluate(final E expression, final List vars, final List values) throws Exception; + } +} diff --git a/java/expression/common/Generator.java b/java/expression/common/Generator.java new file mode 100644 index 0000000..2b7a52d --- /dev/null +++ b/java/expression/common/Generator.java @@ -0,0 +1,173 @@ +package expression.common; + +import base.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class Generator { + private final Supplier constant; + private final List> ops; + private final ExpressionKind.Variables variables; + private final Set forbidden; + private final ExtendedRandom random; + private final List>, Stream>>> basicTests; + + public Generator( + final Supplier constant, + final List> ops, + final ExpressionKind.Variables variables, + final Set forbidden, + final ExtendedRandom random, + final List>, Stream>>> basicTests + ) { + this.constant = constant; + this.ops = List.copyOf(ops); + this.variables = variables; + this.forbidden = Set.copyOf(forbidden); + this.random = random; + this.basicTests = List.copyOf(basicTests); + } + + public static Builder builder(final Supplier constant, final ExtendedRandom random) { + return new Builder<>(random, constant); + } + + public void testRandom( + final TestCounter counter, + final int denominator, + final Consumer> consumer + ) { + final int d = Math.max(TestCounter.DENOMINATOR, denominator); + testRandom(counter, consumer, 1, 100, 100 / d, (vars, depth) -> generateFullDepth(vars, Math.min(depth, 3))); + testRandom(counter, consumer, 2, 1000 / d, 1, this::generateSize); + testRandom(counter, consumer, 3, 12, 100 / d, this::generateFullDepth); + testRandom(counter, consumer, 4, 777 / d, 1, this::generatePartialDepth); + } + + private void testRandom( + final TestCounter counter, + final Consumer> consumer, + final int seq, + final int levels, + final int perLevel, + final BiFunction>, Integer, Node> generator + ) { + counter.scope("Random tests #" + seq, () -> { + final int total = levels * perLevel; + int generated = 0; + for (int level = 0; level < levels; level++) { + for (int j = 0; j < perLevel; j++) { + if (generated % 100 == 0) { + progress(counter, total, generated); + } + generated++; + + final List> vars = variables(random.nextInt(10) + 1); + consumer.accept(Expr.of(generator.apply(Functional.map(vars, v -> Node.op(v.first())), level), vars)); + } + } + progress(counter, generated, total); + }); + } + + private static void progress(final TestCounter counter, final int total, final int generated) { + counter.format("Completed %4d out of %d%n", generated, total); + } + + private Node generate( + final List> variables, + final boolean nullary, + final Supplier> unary, + final Supplier, Node>> binary + ) { + if (nullary || ops.isEmpty()) { + return random.nextBoolean() ? random.randomItem(variables) : Node.constant(constant.get()); + } else { + final Named op = random.randomItem(ops); + if (Math.abs(op.value()) == 1) { + return Node.op(op.name(), (op.value() + 1) >> 1, unary.get()); + } else { + final Pair, Node> pair = binary.get(); + return Node.op(op.name(), pair.first(), pair.second()); + } + } + } + + private Node generate(final List> variables, final boolean nullary, final Supplier> child) { + return generate(variables, nullary, child, () -> Pair.of(child.get(), child.get())); + } + + private Node generateFullDepth(final List> variables, final int depth) { + return generate(variables, depth == 0, () -> generateFullDepth(variables, depth - 1)); + } + + private Node generatePartialDepth(final List> variables, final int depth) { + return generate(variables, depth == 0, () -> generatePartialDepth(variables, random.nextInt(depth))); + } + + private Node generateSize(final List> variables, final int size) { + final int first = size <= 1 ? 0 : random.nextInt(size); + return generate( + variables, + size == 0, + () -> generateSize(variables, size - 1), + () -> Pair.of( + generateSize(variables, first), + generateSize(variables, size - 1 - first) + ) + ); + } + + public void testBasic(final Consumer> consumer) { + basicTests.forEach(test -> { + final List> vars = variables(random.nextInt(5) + 3); + test.apply(Functional.map(vars, v -> Node.op(v.first()))) + .map(node -> Expr.of(node, vars)) + .forEachOrdered(consumer); + }); + } + + public List> variables(final int count) { + List> vars; + do { + vars = variables.generate(random, count); + } while (vars.stream().map(Pair::first).anyMatch(forbidden::contains)); + return vars; + } + + /** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ + public static final class Builder { + private final ExtendedRandom random; + private final Supplier constant; + + private final List> ops = new ArrayList<>(); + private final Set forbidden = new HashSet<>(); + + private Builder(final ExtendedRandom random, final Supplier constant) { + this.random = random; + this.constant = constant; + } + + public void add(final String name, final int arity) { + ops.add(Named.of(name, arity)); + forbidden.add(name); + } + + public Generator build( + final ExpressionKind.Variables variables, + final List>, Stream>>> basicTests + ) { + return new Generator<>(constant, ops, variables, forbidden, random, basicTests); + } + } +} diff --git a/java/expression/common/Node.java b/java/expression/common/Node.java new file mode 100644 index 0000000..df13ff8 --- /dev/null +++ b/java/expression/common/Node.java @@ -0,0 +1,106 @@ +package expression.common; + +import java.util.function.Function; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public abstract class Node { + private Node() { + } + + public abstract R get(Const con, Nullary nul, Unary, R> un, Binary, R> bin); + public abstract R cata(Const con, Nullary nul, Unary un, Binary bin); + + public final String toPolish() { + return cata( + T::toString, + name -> name, + (name, priority, a) -> a + " " + name + ":1", + (name, a1, a2) -> a1 + " " + a2 + " " + name + ":2" + ); + } + + @Override + public final String toString() { + return cata( + T::toString, + name -> name, + (name, priority, a) -> name.equals("[") ? "[" + a + "]" : + (priority & 1) == 1 ? "(" + name + " " + a + ")" : "(" + a + " " + name + ")", + (name, a1, a2) -> "(" + a1 + " " + name + " " + a2 + ")" + ); + } + + public static Node constant(final T value) { + return new Node<>() { + @Override + public R get(final Const con, final Nullary nul, final Unary, R> un, final Binary, R> bin) { + return con.apply(value); + } + + @Override + public R cata(final Const con, final Nullary nul, final Unary un, final Binary bin) { + return con.apply(value); + } + }; + } + + public static Node op(final String name) { + return new Node<>() { + @Override + public R get(final Const con, final Nullary nul, final Unary, R> un, final Binary, R> bin) { + return nul.apply(name); + } + + @Override + public R cata(final Const con, final Nullary nul, final Unary un, final Binary bin) { + return nul.apply(name); + } + }; + } + + public static Node op(final String name, final int priority, final Node arg) { + return new Node<>() { + @Override + public R get(final Const con, final Nullary nul, final Unary, R> un, final Binary, R> bin) { + return un.apply(name, priority, arg); + } + + @Override + public R cata(final Const con, final Nullary nul, final Unary un, final Binary bin) { + return un.apply(name, priority, arg.cata(con, nul, un, bin)); + } + }; + } + + public static Node op(final String name, final Node arg1, final Node arg2) { + return new Node<>() { + @Override + public R get(final Const con, final Nullary nul, final Unary, R> un, final Binary, R> bin) { + return bin.apply(name, arg1, arg2); + } + + @Override + public R cata(final Const con, final Nullary nul, final Unary un, final Binary bin) { + return bin.apply(name, arg1.cata(con, nul, un, bin), arg2.cata(con, nul, un, bin)); + } + }; + } + + @FunctionalInterface + public interface Const extends Function {} + + @FunctionalInterface + public interface Nullary extends Function {} + + @FunctionalInterface + public interface Unary { + R apply(String name, int priority, T arg); + } + + @FunctionalInterface + public interface Binary { + R apply(String name, T arg1, T arg2); + } +} diff --git a/java/expression/common/NodeRenderer.java b/java/expression/common/NodeRenderer.java new file mode 100644 index 0000000..d35e4da --- /dev/null +++ b/java/expression/common/NodeRenderer.java @@ -0,0 +1,96 @@ +package expression.common; + +import base.ExtendedRandom; + +import java.util.List; +import java.util.Map; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class NodeRenderer { + public static final String PAREN = "["; + public static final List DEFAULT_PARENS = List.of(paren("(", ")")); + + public static final Mode MINI_MODE = Mode.SIMPLE_MINI; // Replace by TRUE_MINI for some challenge; + public static final Settings FULL = Mode.FULL.settings(0); + public static final Settings FULL_EXTRA = Mode.FULL.settings(Integer.MAX_VALUE / 4); + public static final Settings SAME = Mode.SAME.settings(0); + public static final Settings MINI = MINI_MODE.settings(0); + public static final Settings TRUE_MINI = Mode.TRUE_MINI.settings(0); + + private final Renderer> renderer; + private final Map brackets; + private final ExtendedRandom random; + + public NodeRenderer( + final Renderer> renderer, + final Map brackets, + final ExtendedRandom random + ) { + this.renderer = renderer; + this.brackets = Map.copyOf(brackets); + this.random = random; + } + + public static Node paren(final boolean condition, final Node node) { + return condition ? Node.op(PAREN, 1, node) : node; + } + + public static Paren paren(final String open, final String close) { + return new Paren(open, close); + } + + public Node renderToNode(final Settings settings, final Expr expr) { + final Expr> convert = expr.convert((name, variable) -> Node.op(name)); + return renderer.render(convert, settings); + } + + public String render(final Node node, final List parens) { + return node.cata( + String::valueOf, + name -> name, + (name, priority, arg) -> + name == PAREN ? random.randomItem(parens).apply(arg) : + priority == Integer.MAX_VALUE ? name + arg + brackets.get(name) : + (priority & 1) == 1 ? name + arg : + arg + name, + (name, a, b) -> a + " " + name + " " + b + ); + } + + public String render(final Expr expr, final Settings settings) { + return render(renderToNode(settings, expr), settings.parens()); + } + + public enum Mode { + FULL, SAME, TRUE_MINI, SIMPLE_MINI; + + public Settings settings(final int limit) { + return new Settings(this, limit); + } + } + + public record Paren(String open, String close) { + String apply(final String expression) { + return open() + expression + close(); + } + } + + public record Settings(Mode mode, int limit, List parens) { + public Settings(final Mode mode, final int limit) { + this(mode, limit, DEFAULT_PARENS); + } + + public Node extra(Node node, final ExtendedRandom random) { + while (random.nextInt(Integer.MAX_VALUE) < limit) { + node = paren(true, node); + } + return node; + } + + public Settings withParens(final List parens) { + return this.parens.equals(parens) ? this : new Settings(mode, limit, List.copyOf(parens)); + } + } +} diff --git a/java/expression/common/NodeRendererBuilder.java b/java/expression/common/NodeRendererBuilder.java new file mode 100644 index 0000000..4e4032a --- /dev/null +++ b/java/expression/common/NodeRendererBuilder.java @@ -0,0 +1,145 @@ +package expression.common; + +import base.ExtendedRandom; +import base.Functional; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@SuppressWarnings("StaticMethodOnlyUsedInOneClass") +public class NodeRendererBuilder { + private final Renderer.Builder> nodeRenderer = Renderer.builder(Node::constant); + private final Map priorities = new HashMap<>(); + private final Map brackets = new HashMap<>(); + private final ExtendedRandom random; + + public NodeRendererBuilder(final ExtendedRandom random) { + this.random = random; + nodeRenderer.unary(NodeRenderer.PAREN, (mode, arg) -> NodeRenderer.paren(true, arg)); + } + + public void unary(final String name, final int priority) { + final String space = name.equals("-") || Character.isLetter(name.charAt(0)) ? " " : ""; + nodeRenderer.unary( + name, + (settings, arg) -> settings.extra(Node.op(name, priority, inner(settings, priority, arg, space)), random) + ); + } + + public void unary(final String left, final String right) { + brackets.put(left, right); + nodeRenderer.unary( + left, + (settings, arg) -> settings.extra(Node.op(left, Integer.MAX_VALUE, arg), random) + ); + } + + private Node inner(final NodeRenderer.Settings settings, final int priority, final Node arg, final String space) { + if (settings.mode() == NodeRenderer.Mode.FULL) { + return NodeRenderer.paren(true, arg); + } else { + final String op = arg.get( + c -> space, + n -> space, + (n, p, a) -> + priority > unaryPriority(arg) ? NodeRenderer.PAREN : + NodeRenderer.PAREN.equals(n) ? "" : + space, + (n, a, b) -> NodeRenderer.PAREN + ); + return op.isEmpty() ? arg : Node.op(op, Priority.MAX.priority | 1, arg); + } + } + + private static Integer unaryPriority(final Node node) { + return node.get(c -> Integer.MAX_VALUE, n -> Integer.MAX_VALUE, (n, p, a) -> p, (n, a, b) -> Integer.MIN_VALUE); + } + + public void binary(final String name, final int priority) { + final Priority mp = new Priority(name, priority); + priorities.put(name, mp); + + nodeRenderer.binary(name, (settings, l, r) -> settings.extra(process(settings, mp, l, r), random)); + } + + private Node process(final NodeRenderer.Settings settings, final Priority mp, final Node l, final Node r) { + if (settings.mode() == NodeRenderer.Mode.FULL) { + return NodeRenderer.paren(true, op(mp, l, r)); + } + + final Priority lp = priority(l); + final Priority rp = priority(r); + + final int rc = rp.compareLevels(mp); + + // :NOTE: Especially ugly code, do not replicate + final boolean advanced = settings.mode() == NodeRenderer.Mode.SAME + || mp.has(2) + || mp.has(1) && (mp != rp || (settings.mode() == NodeRenderer.Mode.TRUE_MINI && hasOther(r, rp))); + + final Node al = NodeRenderer.paren(lp.compareLevels(mp) < 0, l); + if (rc == 0 && !advanced) { + return get(r, null, (n, a, b) -> rp.op(mp.op(al, a), b)); + } else { + return mp.op(al, NodeRenderer.paren(rc == 0 && advanced || rc < 0, r)); + } + } + + private boolean hasOther(final Node node, final Priority priority) { + return get(node, () -> false, (name, l, r) -> { + final Priority p = Functional.get(priorities, name); + if (p.compareLevels(priority) != 0) { + return false; + } + return p != priority || hasOther(l, priority); + }); + } + + private Node op(final Priority mp, final Node l, final Node r) { + return mp.op(l, r); + } + + private Priority priority(final Node node) { + return get(node, () -> Priority.MAX, (n, a, b) -> Functional.get(priorities, n)); + } + + private R get(final Node node, final Supplier common, final Node.Binary, R> binary) { + return node.get( + c -> common.get(), + n -> common.get(), + (n, p, a) -> common.get(), + binary + ); + } + + public NodeRenderer build() { + return new NodeRenderer<>(nodeRenderer.build(), brackets, random); + } + + // :NOTE: Especially ugly bit-fiddling, do not replicate + private record Priority(String op, int priority) { + private static final int Q = 3; + private static final Priority MAX = new Priority("MAX", Integer.MAX_VALUE - Q); + + private int compareLevels(final Priority that) { + return (priority | Q) - (that.priority | Q); + } + + @Override + public String toString() { + return String.format("Priority(%s, %d, %d)", op, priority | Q, priority & Q); + } + + public Node op(final Node l, final Node r) { + return Node.op(op, l, r); + } + + private boolean has(final int value) { + return (priority & Q) == value; + } + } +} diff --git a/java/expression/common/Reason.java b/java/expression/common/Reason.java new file mode 100644 index 0000000..9ad5b22 --- /dev/null +++ b/java/expression/common/Reason.java @@ -0,0 +1,60 @@ +package expression.common; + +import base.Either; + +import java.util.function.LongUnaryOperator; +import java.util.function.Supplier; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Reason { + public static final Reason OVERFLOW = new Reason("Overflow"); + public static final Reason DBZ = new Reason("Division by zero"); + + private final String description; + + public Reason(final String description) { + this.description = description; + } + + public static Either eval(final Supplier action) { + try { + return Either.right(action.get()); + } catch (final ReasonException e) { + return Either.left(e.reason); + } + } + + public static int overflow(final long value) { + return value < Integer.MIN_VALUE || Integer.MAX_VALUE < value + ? OVERFLOW.error() + : (int) value; + } + + public T error() { + throw new ReasonException(this); + } + + public LongUnaryOperator less(final long limit, final LongUnaryOperator op) { + return a -> a < limit ? error() : op.applyAsLong(a); + } + + public LongUnaryOperator greater(final int limit, final LongUnaryOperator op) { + return a -> a > limit ? error() : op.applyAsLong(a); + } + + private static class ReasonException extends RuntimeException { + private final Reason reason; + + public ReasonException(final Reason reason) { + super(reason.description); + this.reason = reason; + } + } + + @Override + public String toString() { + return String.format("Reason(%s)", description); + } +} diff --git a/java/expression/common/Renderer.java b/java/expression/common/Renderer.java new file mode 100644 index 0000000..1187f31 --- /dev/null +++ b/java/expression/common/Renderer.java @@ -0,0 +1,60 @@ +package expression.common; + +import base.Functional; +import base.Pair; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public interface Renderer { + static Builder builder(final Node.Const constant) { + return new Builder<>(constant); + } + + R render(final Expr expr, final S settings); + + @FunctionalInterface + interface UnaryOperator { + R apply(S settings, R arg); + } + + @FunctionalInterface + interface BinaryOperator { + R apply(S settings, R arg1, R arg2); + } + + final class Builder { + private final Node.Const constant; + private final Map> unary = new HashMap<>(); + private final Map> binary = new HashMap<>(); + + private Builder(final Node.Const constant) { + this.constant = constant; + } + + public void unary(final String name, final UnaryOperator op) { + unary.put(name, op); + } + + public void binary(final String name, final BinaryOperator op) { + binary.put(name, op); + } + + public Renderer build() { + return (expr, settings) -> { + final Map vars = expr.variables().stream() + .collect(Collectors.toMap(Pair::first, Pair::second)); + return expr.node().cata( + constant, + name -> Functional.get(vars, name), + (name, p, arg) -> Functional.get(unary, name).apply(settings, arg), + (name, arg1, arg2) -> Functional.get(binary, name).apply(settings, arg1, arg2) + ); + }; + } + } +} diff --git a/java/expression/common/TestGenerator.java b/java/expression/common/TestGenerator.java new file mode 100644 index 0000000..fcf7288 --- /dev/null +++ b/java/expression/common/TestGenerator.java @@ -0,0 +1,56 @@ +package expression.common; + +import base.Pair; +import base.TestCounter; +import expression.ToMiniString; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class TestGenerator { + private final Generator generator; + private final NodeRenderer renderer; + + public TestGenerator(final Generator generator, final NodeRenderer renderer) { + this.generator = generator; + this.renderer = renderer; + } + + public void testBasic(final Consumer> test) { + generator.testBasic(consumer(test)); + } + + public void testRandom(final TestCounter counter, final int denominator, final Consumer> test) { + generator.testRandom(counter, denominator, consumer(test)); + } + + private Consumer> consumer(final Consumer> consumer) { + return expr -> consumer.accept(new TestGenerator.Test<>(expr, renderer)); + } + + + public List> variables(final int count) { + return generator.variables(count); + } + + public String render(final Expr expr, final NodeRenderer.Settings settings) { + return renderer.render(expr, settings); + } + + public static class Test { + public final Expr expr; + private final Map rendered = new HashMap<>(); + private final NodeRenderer renderer; + + public Test(final Expr expr, final NodeRenderer renderer) { + this.expr = expr; + this.renderer = renderer; + } + + public String render(final NodeRenderer.Settings settings) { + return rendered.computeIfAbsent(settings, s -> renderer.render(expr, s)); + } + } +} diff --git a/java/expression/common/TestGeneratorBuilder.java b/java/expression/common/TestGeneratorBuilder.java new file mode 100644 index 0000000..fa3a7b5 --- /dev/null +++ b/java/expression/common/TestGeneratorBuilder.java @@ -0,0 +1,145 @@ +package expression.common; + +import base.ExtendedRandom; +import base.Functional; +import expression.ToMiniString; +import expression.common.ExpressionKind.Variables; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class TestGeneratorBuilder { + private final ExtendedRandom random; + + private final Generator.Builder generator; + private final NodeRendererBuilder renderer; + + private final List>, Stream>>> basicTests = new ArrayList<>(); + private final List> consts; + private final boolean verbose; + + public TestGeneratorBuilder( + final ExtendedRandom random, + final Supplier constant, + final List constants, + final boolean verbose + ) { + this.random = random; + this.verbose = verbose; + + generator = Generator.builder(constant, random); + renderer = new NodeRendererBuilder<>(random); + + consts = Functional.map(constants, Node::constant); + basicTests.add(vars -> consts.stream()); + basicTests.add(List::stream); + } + + private Node c() { + return random.randomItem(consts); + } + + private Node v(final List> variables) { + return random.randomItem(variables); + } + + private static Node f(final String name, final int priority, final Node arg) { + return Node.op(name, priority, arg); + } + + private static Node f(final String left, final Node arg) { + return Node.op(left, Integer.MAX_VALUE, arg); + } + + private static Node f(final String name, final Node arg1, final Node arg2) { + return Node.op(name, arg1, arg2); + } + + @SafeVarargs + private void basicTests(final Function>, Node>... tests) { + Arrays.stream(tests).map(test -> test.andThen(Stream::of)).forEachOrdered(basicTests::add); + } + + public void unary(final String name, final int priority) { + generator.add(name, (priority & 1) * 2 - 1); + renderer.unary(name, priority); + + if (verbose) { + basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(name, priority, a))); + } else { + basicTests(vars -> f(name, priority, c()), vars -> f(name, priority, v(vars))); + } + + final Function>, Node> p1 = vars -> f(name, priority, f(name, priority, f("+", v(vars), c()))); + final Function>, Node> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, priority, c()))); + basicTests( + vars -> f(name, priority, f("+", v(vars), v(vars))), + vars -> f(name, priority, f(name, priority, v(vars))), + vars -> f(name, priority, f("/", f(name, priority, v(vars)), f("+", v(vars), v(vars)))), + p1, + p2, + vars -> f("+", p1.apply(vars), p2.apply(vars)) + ); + } + + public void unary(final String left, final String right) { + generator.add(left, 1); + renderer.unary(left, right); + + if (verbose) { + basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream()).map(a -> f(left, a))); + } else { + basicTests(vars -> f(left, c()), vars -> f(left, v(vars))); + } + + final Function>, Node> p1 = vars -> f(left, f(left, f("+", v(vars), c()))); + final Function>, Node> p2 = vars -> f("*", v(vars), f("*", v(vars), f(left, c()))); + basicTests( + vars -> f(left, f("+", v(vars), v(vars))), + vars -> f(left, f(left, v(vars))), + vars -> f(left, f("/", f(left, v(vars)), f("+", v(vars), v(vars)))), + p1, + p2, + vars -> f("+", p1.apply(vars), p2.apply(vars)) + ); + } + + public void binary(final String name, final int priority) { + generator.add(name, 2); + renderer.binary(name, priority); + + if (verbose) { + basicTests.add(vars -> Stream.concat(consts.stream(), vars.stream().limit(3)) + .flatMap(a -> consts.stream().map(b -> f(name, a, b)))); + } else { + basicTests( + vars -> f(name, c(), c()), + vars -> f(name, v(vars), c()), + vars -> f(name, c(), v(vars)), + vars -> f(name, v(vars), v(vars)) + ); + } + + final Function>, Node> p1 = vars -> f(name, f(name, f("+", v(vars), c()), v(vars)), v(vars)); + final Function>, Node> p2 = vars -> f("*", v(vars), f("*", v(vars), f(name, c(), v(vars)))); + + basicTests( + vars -> f(name, f(name, v(vars), v(vars)), v(vars)), + vars -> f(name, v(vars), f(name, v(vars), v(vars))), + vars -> f(name, f(name, v(vars), v(vars)), f(name, v(vars), v(vars))), + vars -> f(name, f("-", f(name, v(vars), v(vars)), c()), f("+", v(vars), v(vars))), + p1, + p2, + vars -> f("+", p1.apply(vars), p2.apply(vars)) + ); + } + + public TestGenerator build(final Variables variables) { + return new TestGenerator<>(generator.build(variables, basicTests), renderer.build()); + } +} diff --git a/java/expression/common/Type.java b/java/expression/common/Type.java new file mode 100644 index 0000000..3b396e9 --- /dev/null +++ b/java/expression/common/Type.java @@ -0,0 +1,51 @@ +package expression.common; + +import base.Asserts; +import base.ExtendedRandom; +import expression.Const; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.Function; +import java.util.function.IntFunction; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class Type { + private final IntFunction fromInt; + private final Function random; + private final Function constant; + + public Type(final IntFunction fromInt, final Function random, final Class type) { + this.fromInt = fromInt; + this.random = random; + + try { + final MethodHandle constructor = MethodHandles.publicLookup() + .findConstructor(Const.class, MethodType.methodType(void.class, type)); + constant = c -> { + try { + return (Const) constructor.invoke(c); + } catch (final Throwable e) { + throw Asserts.error("Cannot create new Const(%s): %s", c, e); + } + }; + } catch (final IllegalAccessException | NoSuchMethodException e) { + throw Asserts.error("Cannot find constructor Const(%s): %s", type, e); + } + } + + public Const constant(final C value) { + return constant.apply(value); + } + + public C fromInt(final int value) { + return fromInt.apply(value); + } + + public C randomValue(final ExtendedRandom random) { + return this.random.apply(random); + } +} diff --git a/java/expression/common/package-info.java b/java/expression/common/package-info.java new file mode 100644 index 0000000..70d03f2 --- /dev/null +++ b/java/expression/common/package-info.java @@ -0,0 +1,7 @@ +/** + * Expressions generators for expression-based homeworks + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package expression.common; \ No newline at end of file diff --git a/java/expression/exceptions/ExceptionsTest.java b/java/expression/exceptions/ExceptionsTest.java new file mode 100644 index 0000000..851f635 --- /dev/null +++ b/java/expression/exceptions/ExceptionsTest.java @@ -0,0 +1,30 @@ +package expression.exceptions; + +import base.Selector; +import expression.ListExpression; +import expression.parser.Operations; + +import static expression.parser.Operations.*; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ExceptionsTest { + private static final ExpressionParser PARSER = new ExpressionParser(); + private static final Operations.Operation LIST = kind(ListExpression.KIND, PARSER::parse); + + public static final Selector SELECTOR = Selector.composite(ExceptionsTest.class, ExceptionsTester::new, "easy", "hard") + .variant("Base", LIST, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE) + .variant("3637", POW, LOG) + .variant("3839", POW, LOG, POW_2, LOG_2) + .variant("3435", POW_2, LOG_2) + .variant("3233", HIGH, LOW) + .selector(); + + private ExceptionsTest() { + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/expression/exceptions/ExceptionsTestSet.java b/java/expression/exceptions/ExceptionsTestSet.java new file mode 100644 index 0000000..f19baa5 --- /dev/null +++ b/java/expression/exceptions/ExceptionsTestSet.java @@ -0,0 +1,162 @@ +package expression.exceptions; + +import base.Functional; +import base.Named; +import base.Pair; +import expression.ToMiniString; +import expression.Variable; +import expression.common.ExpressionKind; +import expression.parser.ParserTestSet; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.LongBinaryOperator; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ExceptionsTestSet extends ParserTestSet { + private static final int D = 5; + private static final List OVERFLOW_VALUES = new ArrayList<>(); + private final char[] CHARS = "AZ+-*%()[]<>".toCharArray(); + + static { + Functional.addRange(OVERFLOW_VALUES, D, Integer.MIN_VALUE + D); + Functional.addRange(OVERFLOW_VALUES, D, Integer.MIN_VALUE / 2); + Functional.addRange(OVERFLOW_VALUES, D, (int) -Math.sqrt(Integer.MAX_VALUE)); + Functional.addRange(OVERFLOW_VALUES, D, 0); + Functional.addRange(OVERFLOW_VALUES, D, (int) Math.sqrt(Integer.MAX_VALUE)); + Functional.addRange(OVERFLOW_VALUES, D, Integer.MAX_VALUE / 2); + Functional.addRange(OVERFLOW_VALUES, D, Integer.MAX_VALUE - D); + } + + private final List> parsingTest; + + public ExceptionsTestSet(final ExceptionsTester tester, final ParsedKind kind) { + super(tester, kind, false); + parsingTest = tester.parsingTest; + } + + private void testParsingErrors() { + counter.testForEach(parsingTest, op -> { + final List names = Functional.map(kind.kind().variables().generate(counter.random(), 3), Pair::first); + final String expr = mangle(op.value(), names); + try { + kind.parse(expr, names); + counter.fail("Successfully parsed '%s'", op.value()); + } catch (final Exception e) { + counter.format("%-30s %s%n", op.name(), e.getClass().getSimpleName() + ": " + e.getMessage()); + } + }); + } + + private void testOverflow() { + final List> variables = kind.kind().variables().generate(counter.random(), 3); + final List names = Functional.map(variables, Pair::first); + final Variable vx = (Variable) variables.get(0).second(); + final Variable vy = (Variable) variables.get(1).second(); + + //noinspection Convert2MethodRef + testOverflow(names, (a, b) -> a + b, "+", new CheckedAdd(vx, vy)); + testOverflow(names, (a, b) -> a - b, "-", new CheckedSubtract(vx, vy)); + testOverflow(names, (a, b) -> a * b, "*", new CheckedMultiply(vx, vy)); + testOverflow(names, (a, b) -> b == 0 ? Long.MAX_VALUE : a / b, "/", new CheckedDivide(vx, vy)); + testOverflow(names, (a, b) -> -b, "<- ignore first argument, unary -", new CheckedNegate(vy)); + } + + private void testOverflow(final List names, final LongBinaryOperator f, final String op, final Object expression) { + final ExpressionKind kind = this.kind.kind(); + for (final int a : OVERFLOW_VALUES) { + for (final int b : OVERFLOW_VALUES) { + final long expected = f.applyAsLong(a, b); + final boolean isInt = Integer.MIN_VALUE <= expected && expected <= Integer.MAX_VALUE; + try { + final C actual = kind.evaluate( + kind.cast(expression), + names, + kind.fromInts(List.of(a, b, 0)) + ); + counter.checkTrue( + isInt && kind.fromInt((int) expected).equals(actual), + "%d %s %d == %d", a, op, b, actual + ); + } catch (final Exception e) { + if (isInt) { + counter.fail(e, "Unexpected error in %d %s %d", a, op, b); + } + } + } + } + } + + @Override + protected void test() { + counter.scope("Overflow tests", (Runnable) this::testOverflow); + super.test(); + counter.scope("Parsing error tests", this::testParsingErrors); + } + + + @Override + protected E parse(final String expression, final List variables, final boolean reparse) { + final String expr = expression.strip(); + if (expr.length() > 10) { + for (final char ch : CHARS) { + for (int i = 0; i < 10; i++) { + final int index = 1 + tester.random().nextInt(expr.length() - 2); + int pi = index - 1; + while (Character.isWhitespace(expr.charAt(pi))) { + pi--; + } + int ni = index; + while (Character.isWhitespace(expr.charAt(ni))) { + ni++; + } + final char pc = expr.charAt(pi); + final char nc = expr.charAt(ni); + if ( + "-([{*∛√²³₂₃!‖⎵⎴⌊⌈=?".indexOf(nc) < 0 && + (!Character.isLetterOrDigit(pc) || !Character.isLetterOrDigit(ch)) && + nc != ch && pc != ch && + !Character.isLetterOrDigit(nc) && nc != '$' + ) { + shouldFail( + variables, + "Parsing error expected for " + insert(expr, index, "" + ch + "<-- ERROR_INSERTED>"), + insert(expr, index, String.valueOf(ch)) + ); + break; + } + } + } + parens(variables, expr, '[', ']'); + parens(variables, expr, '{', '}'); + } + + return counter.testV(() -> counter.call("parse", () -> kind.parse(expr, variables))); + } + + private static String insert(final String expr, final int index, final String value) { + return expr.substring(0, index) + value + expr.substring(index); + } + + private void parens(final List variables, final String expr, final char open, final char close) { + if (expr.indexOf(open) >= 0) { + replaces(variables, expr, open, '('); + replaces(variables, expr, close, ')'); + if (expr.indexOf('(') >= 0) { + replaces(variables, expr, '(', open); + replaces(variables, expr, ')', close); + } + } + } + + private void replaces(final List variables, final String expr, final char what, final char by) { + final String input = expr.replace(what, by); + shouldFail(variables, "Unmatched parentheses: " + input, input); + } + + private void shouldFail(final List variables, final String message, final String input) { + counter.shouldFail(message, () -> kind.parse(input, variables)); + } +} diff --git a/java/expression/exceptions/ExceptionsTester.java b/java/expression/exceptions/ExceptionsTester.java new file mode 100644 index 0000000..df9048b --- /dev/null +++ b/java/expression/exceptions/ExceptionsTester.java @@ -0,0 +1,100 @@ +package expression.exceptions; + +import base.Named; +import base.TestCounter; +import expression.common.Reason; +import expression.parser.ParserTestSet; +import expression.parser.ParserTester; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.LongBinaryOperator; +import java.util.function.LongToIntFunction; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ExceptionsTester extends ParserTester { + /* package-private */ final List> parsingTest = new ArrayList<>(List.of( + Named.of("No first argument", "* $y * $z"), + Named.of("No middle argument", "$x * * $z"), + Named.of("No last argument", "$x * $y * "), + Named.of("No first argument'", "1 + (* $y * $z) + 2"), + Named.of("No middle argument'", "1 + ($x * / 9) + 3"), + Named.of("No last argument'", "1 + ($x * $y - ) + 3"), + Named.of("No opening parenthesis", "$x * $y)"), + Named.of("No closing parenthesis", "($x * $y"), + Named.of("Mismatched closing parenthesis", "($x * $y]"), + Named.of("Mismatched open parenthesis", "[$x * $y)"), + Named.of("Start symbol", "@$x * $y"), + Named.of("Middle symbol", "$x @ * $y"), + Named.of("End symbol", "$x * $y@"), + Named.of("Constant overflow 1", Integer.MIN_VALUE - 1L + ""), + Named.of("Constant overflow 2", Integer.MAX_VALUE + 1L + ""), + Named.of("Bare +", "+"), + Named.of("Bare -", "-"), + Named.of("Bare a", "a"), + Named.of("(())", "(())"), + Named.of("Spaces in numbers", "10 20") + )); + + public ExceptionsTester(final TestCounter counter) { + super(counter); + } + + + private void parsingTests(final String... tests) { + for (final String test : tests) { + parsingTest.add(Named.of(test, test)); + } + } + + @Override + public void unary(final String name, final int priority, final BiFunction op) { + if (allowed(name)) { + parsingTests(name, "1 * " + name, name + " * 1"); + } + parsingTests(name + "()", name + "(1, 2)"); + if (name.length() > 1) { + parsingTests(name + "q"); + } + if (allLetterAndDigit(name)) { + parsingTests(name + "1", name + "q"); + } + super.unary(name, priority, op); + } + + private static boolean allowed(final String name) { + return !"xyz".contains(name.substring(0, 1)) && !"xyz".contains(name.substring(name.length() - 1)); + } + + @Override + public void binary(final String name, final int priority, final LongBinaryOperator op) { + if (allowed(name)) { + parsingTests(name); + } + parsingTests("1 " + name, "1 " + name + " * 3"); + if (!"-".equals(name)) { + parsingTests(name + " 1", "1 * " + name + " 2"); + } + if (allLetterAndDigit(name)) { + parsingTests("5" + name + "5", "5 " + name + "5", "5 " + name + "5 5", "1" + name + "x 1", "1 " + name + "x 1"); + } + super.binary(name, priority, op); + } + + private static boolean allLetterAndDigit(final String name) { + return name.chars().allMatch(Character::isLetterOrDigit); + } + + @Override + protected void test(final ParserTestSet.ParsedKind kind) { + new ExceptionsTestSet<>(this, kind).test(); + } + + @Override + protected int cast(final long value) { + return Reason.overflow(value); + } +} diff --git a/java/expression/exceptions/ListParser.java b/java/expression/exceptions/ListParser.java new file mode 100644 index 0000000..2370790 --- /dev/null +++ b/java/expression/exceptions/ListParser.java @@ -0,0 +1,13 @@ +package expression.exceptions; + +import expression.ListExpression; + +import java.util.List; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +public interface ListParser { + ListExpression parse(String expression, final List variables) throws Exception; +} diff --git a/java/expression/exceptions/package-info.java b/java/expression/exceptions/package-info.java new file mode 100644 index 0000000..5debca1 --- /dev/null +++ b/java/expression/exceptions/package-info.java @@ -0,0 +1,8 @@ +/** + * Tests for Expression Error Handling homework + * of Introduction to Programming course. + * + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package expression.exceptions; \ No newline at end of file diff --git a/java/expression/package-info.java b/java/expression/package-info.java new file mode 100644 index 0000000..8d9e3c1 --- /dev/null +++ b/java/expression/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Expressions homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package expression; \ No newline at end of file diff --git a/java/expression/parser/ListParser.java b/java/expression/parser/ListParser.java new file mode 100644 index 0000000..0f331d9 --- /dev/null +++ b/java/expression/parser/ListParser.java @@ -0,0 +1,13 @@ +package expression.parser; + +import expression.ListExpression; + +import java.util.List; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +@FunctionalInterface +public interface ListParser { + ListExpression parse(String expression, List variables); +} diff --git a/java/expression/parser/Operations.java b/java/expression/parser/Operations.java new file mode 100644 index 0000000..3266560 --- /dev/null +++ b/java/expression/parser/Operations.java @@ -0,0 +1,148 @@ +package expression.parser; + +import expression.ToMiniString; +import expression.common.ExpressionKind; +import expression.common.Reason; + +import java.math.BigInteger; +import java.util.function.*; +import java.util.stream.LongStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Operations { + // === Base + + public static final Operation NEGATE = unary("-", 1, a -> -a); + @SuppressWarnings("Convert2MethodRef") + public static final Operation ADD = binary("+", 1600, (a, b) -> a + b); + public static final Operation SUBTRACT = binary("-", 1602, (a, b) -> a - b); + public static final Operation MULTIPLY = binary("*", 2001, (a, b) -> a * b); + public static final Operation DIVIDE = binary("/", 2002, (a, b) -> b == 0 ? Reason.DBZ.error() : a / b); + + + // === MinMax + public static final Operation MIN = binary("min", 401, Math::min); + public static final Operation MAX = binary("max", 401, Math::max); + + + // === Reverse + + private static Operation digits(final String name, final boolean mask, final int r, final LongBinaryOperator q) { + return unary(name, 1, v -> LongStream.iterate(mask ? v & 0xffff_ffffL : v, n -> n != 0, n -> n / r) + .map(n -> n % r) + .reduce(0, q)); + } + + public static final Operation REVERSE = digits("reverse", false, 10, (a, b) -> a * 10 + b); + + + // === Digits + public static final Operation DIGITS = digits("digits", false, 10, Long::sum); + + + // === Floor and Ceiling + + private static long floor(final long a) { + return (a >= 0 ? a : a - FLOOR_CEILING_STEP + 1) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP; + } + + private static long ceiling(final long a) { + return (a >= 0 ? a + FLOOR_CEILING_STEP - 1: a) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP; + } + + public static final int FLOOR_CEILING_STEP = 1000; + public static final Operation FLOOR = unary("floor", 1, Operations::floor); + public static final Operation CEILING = unary("ceiling", 1, Operations::ceiling); + + // === Set, Clear + + @SuppressWarnings("IntegerMultiplicationImplicitCastToLong") + public static final Operation SET = binary("set", 202, (a, b) -> a | (1 << b)); + @SuppressWarnings("IntegerMultiplicationImplicitCastToLong") + public static final Operation CLEAR = binary("clear", 202, (a, b) -> a & ~(1 << b)); + + + // === Pow, Log + public static final Operation POW_O = binary("**", 2402, (a, b) -> + b < 0 ? 1 : BigInteger.valueOf(a).modPow(BigInteger.valueOf(b), BigInteger.valueOf(1L << 32)).intValue()); + public static final Operation LOG_O = binary("//", 2402, (a, b) -> + a == 0 && b > 0 ? Integer.MIN_VALUE : + a <= 0 || b <= 0 || a == 1 && b == 1 ? 0 : + a > 1 && b == 1 ? Integer.MAX_VALUE + : LongStream.iterate(b, v -> v <= a, v -> v * b).count() + ); + + private static final Reason INVALID_POW = new Reason("Invalid power"); + public static final Operation POW = binary("**", 2402, Operations::powC); + + private static long powC(final long a, final long b) { + if (b < 0 || a == 0 && b == 0) { + return INVALID_POW.error(); + } + if (Math.abs(a) > 1 && b > 32) { + return Reason.OVERFLOW.error(); + } + final BigInteger result = BigInteger.valueOf(a).pow((int) b); + if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || BigInteger.valueOf(Integer.MAX_VALUE).compareTo(result) < 0) { + return Reason.OVERFLOW.error(); + } + return result.intValue(); + } + + private static final Reason INVALID_LOG = new Reason("Invalid log"); + public static final Operation LOG = binary("//", 2402, (a, b) -> + a <= 0 || b <= 1 ? INVALID_LOG.error() : (int) (Math.log(a) / Math.log(b))); + + + // Pow2, Log2 + + private static final Reason NEG_LOG = new Reason("Logarithm of negative value"); + public static final Operation LOG_2 + = unary("log₂", 1, NEG_LOG.less(1, a-> (long) (Math.log(a) / Math.log(2)))); + + private static final Reason NEG_POW = new Reason("Exponentiation to negative power"); + public static final Operation POW_2 + = unary("pow₂", 1, NEG_POW.less(0, Reason.OVERFLOW.greater(31, a -> (long) Math.pow(2, a)))); + + + // === High, Low + public static final Operation HIGH = unary("high", 1, v -> Integer.highestOneBit((int) v)); + public static final Operation LOW = unary("low", 1, v -> Integer.lowestOneBit((int) v)); + + // === Common + + private Operations() { + } + + public static Operation unary(final String name, final int priority, final LongUnaryOperator op) { + return unary(name, priority, (a, c) -> op.applyAsLong(a)); + } + + public static Operation unary(final String left, final String right, final LongUnaryOperator op) { + return unary(left, right, (a, c) -> op.applyAsLong(a)); + } + + public static Operation unary(final String name, final int priority, final BiFunction op) { + return tests -> tests.unary(name, priority, op); + } + + public static Operation unary(final String left, final String right, final BiFunction op) { + return tests -> tests.unary(left, right, op); + } + + public static Operation binary(final String name, final int priority, final LongBinaryOperator op) { + return tests -> tests.binary(name, priority, op); + } + + public static Operation kind( + final ExpressionKind kind, + final ParserTestSet.Parser parser + ) { + return factory -> factory.kind(kind, parser); + } + + @FunctionalInterface + public interface Operation extends Consumer {} +} diff --git a/java/expression/parser/ParserTest.java b/java/expression/parser/ParserTest.java new file mode 100644 index 0000000..c7b20bd --- /dev/null +++ b/java/expression/parser/ParserTest.java @@ -0,0 +1,31 @@ +package expression.parser; + +import base.Selector; +import expression.ListExpression; + +import static expression.parser.Operations.*; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ParserTest { + private static final ExpressionParser PARSER = new ExpressionParser(); + private static final Operations.Operation LIST = kind(ListExpression.KIND, PARSER::parse); + + // === Common + + public static final Selector SELECTOR = Selector.composite(ParserTest.class, ParserTester::new, "easy", "hard") + .variant("Base", LIST, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE) + .variant("3637", MIN, MAX, REVERSE) + .variant("3839", MIN, MAX, REVERSE, DIGITS) + .variant("3435", FLOOR, CEILING, SET, CLEAR) + .variant("3233", FLOOR, CEILING) + .selector(); + + private ParserTest() { + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/expression/parser/ParserTestSet.java b/java/expression/parser/ParserTestSet.java new file mode 100644 index 0000000..c70f039 --- /dev/null +++ b/java/expression/parser/ParserTestSet.java @@ -0,0 +1,236 @@ +package expression.parser; + +import base.*; +import expression.ToMiniString; +import expression.common.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ParserTestSet { + private static final int D = 5; + + private static final List TEST_VALUES = new ArrayList<>(); + static { + Functional.addRange(TEST_VALUES, D, D); + Functional.addRange(TEST_VALUES, D, -D); + } + + public static final List CONSTS + = List.of(0, 1, -1, 4, -4, 10, -10, 30, -30, 100, -100, Integer.MAX_VALUE, Integer.MIN_VALUE); + + protected final ParserTester tester; + protected final ParsedKind kind; + private final boolean safe; + + protected final TestCounter counter; + + public ParserTestSet(final ParserTester tester, final ParsedKind kind) { + this(tester, kind, true); + } + + protected ParserTestSet(final ParserTester tester, final ParsedKind kind, final boolean safe) { + this.tester = tester; + this.kind = kind; + this.safe = safe; + + counter = tester.getCounter(); + } + + private void examples(final TestGenerator generator) { + example(generator, "$x+2", (x, y, z) -> x + 2); + example(generator, "2-$y", (x, y, z) -> 2 - y); + example(generator, " 3* $z ", (x, y, z) -> 3 * z); + example(generator, "$x/ - 2", (x, y, z) -> -x / 2); + example(generator, "$x*$y+($z-1 )/10", (x, y, z) -> x * y + (int) (z - 1) / 10); + example(generator, "-(-(-\t\t-5 + 16 *$x*$y) + 1 * $z) -(((-11)))", (x, y, z) -> -(-(5 + 16 * x * y) + z) + 11); + example(generator, "" + Integer.MAX_VALUE, (x, y, z) -> (long) Integer.MAX_VALUE); + example(generator, "" + Integer.MIN_VALUE, (x, y, z) -> (long) Integer.MIN_VALUE); + example(generator, "$x--$y--$z", (x, y, z) -> x + y + z); + example(generator, "((2+2))-0/(--2)*555", (x, y, z) -> 4L); + example(generator, "$x-$x+$y-$y+$z-($z)", (x, y, z) -> 0L); + example(generator, "(".repeat(300) + "$x + $y + (-10*-$z)" + ")".repeat(300), (x, y, z) -> x + y + 10 * z); + example(generator, "$x / $y / $z", (x, y, z) -> y == 0 || z == 0 ? Reason.DBZ.error() : (int) x / (int) y / z); + } + + private void example(final TestGenerator generator, final String expr, final ExampleExpression expression) { + final List names = Functional.map(generator.variables(3), Pair::first); + final TExpression expected = vars -> expression.evaluate(vars.get(0), vars.get(1), vars.get(2)); + + counter.test(() -> { + final String mangled = mangle(expr, names); + final E parsed = parse(mangled, names, true); + Functional.allValues(TEST_VALUES, 3).forEach(values -> check(expected, parsed, names, values, mangled)); + }); + } + + protected static String mangle(final String expr, final List names) { + return expr + .replace("$x", names.get(0)) + .replace("$y", names.get(1)) + .replace("$z", names.get(2)); + } + + protected void test() { + final TestGenerator generator = tester.generator.build(kind.kind.variables()); + final Renderer renderer = tester.renderer.build(); + final Consumer> consumer = test -> test(renderer, test); + counter.scope("Basic tests", () -> generator.testBasic(consumer)); + counter.scope("Handmade tests", () -> examples(generator)); + counter.scope("Random tests", () -> generator.testRandom(counter, 1, consumer)); + } + + private void test(final Renderer renderer, final TestGenerator.Test test) { + final Expr expr = test.expr; + final List> vars = expr.variables(); + final List variables = Functional.map(vars, Pair::first); + final String full = test.render(NodeRenderer.FULL); + final String mini = test.render(NodeRenderer.MINI); + + final E fullParsed = parse(test, variables, NodeRenderer.FULL); + final E miniParsed = parse(test, variables, NodeRenderer.MINI); + final E safeParsed = parse(test, variables, NodeRenderer.SAME); + + checkToString(full, mini, "base", fullParsed); + if (tester.mode() > 0) { + counter.test(() -> Asserts.assertEquals("mini.toMiniString", mini, miniParsed.toMiniString())); + counter.test(() -> Asserts.assertEquals("safe.toMiniString", mini, safeParsed.toMiniString())); + } + checkToString(full, mini, "extraParentheses", parse(test, variables, NodeRenderer.FULL_EXTRA)); + checkToString(full, mini, "noSpaces", parse(removeSpaces(full), variables, false)); + checkToString(full, mini, "extraSpaces", parse(extraSpaces(full), variables, false)); + + final TExpression expected = renderer.render( + Expr.of( + expr.node(), + Functional.map(vars, (i, var) -> Pair.of(var.first(), args -> args.get(i))) + ), + Unit.INSTANCE + ); + + check(expected, fullParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), full); + if (this.safe) { + final String safe = test.render(NodeRenderer.SAME); + check(expected, safeParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), safe); + } + } + + private E parse( + final TestGenerator.Test test, + final List variables, + final NodeRenderer.Settings settings + ) { + return parse(test.render(settings.withParens(tester.parens)), variables, false); + } + + private static final String LOOKBEHIND = "(?*/+=!-])"; + private static final String LOOKAHEAD = "(?![a-zA-Z0-9<>*/])"; + private static final Pattern SPACES = Pattern.compile(LOOKBEHIND + " | " + LOOKAHEAD + "|" + LOOKAHEAD + LOOKBEHIND); + private String extraSpaces(final String expression) { + return SPACES.matcher(expression).replaceAll(r -> tester.random().randomString( + ExtendedRandom.SPACES, + tester.random().nextInt(5) + )); + } + + private static String removeSpaces(final String expression) { + return SPACES.matcher(expression).replaceAll(""); + } + + private void checkToString(final String full, final String mini, final String context, final ToMiniString parsed) { + counter.test(() -> { + assertEquals(context + ".toString", full, full, parsed.toString()); + if (tester.mode() > 0) { + assertEquals(context + ".toMiniString", full, mini, parsed.toMiniString()); + } + }); + } + + private static void assertEquals( + final String context, + final String original, + final String expected, + final String actual + ) { + final String message = String.format("%s:%n original `%s`,%n expected `%s`,%n actual `%s`", + context, original, expected, actual); + Asserts.assertTrue(message, Objects.equals(expected, actual)); + } + + private Either eval(final TExpression expression, final List vars) { + return Reason.eval(() -> tester.cast(expression.evaluate(vars))); + } + + protected E parse(final String expression, final List variables, final boolean reparse) { + return counter.testV(() -> { + final E parsed = counter.testV(() -> counter.call("parse", + () -> kind.parse(expression, variables))); + if (reparse) { + counter.testV(() -> counter.call("parse", () -> kind.parse(parsed.toString(), variables))); + } + return parsed; + }); + } + + private void check( + final TExpression expectedExpression, + final E expression, + final List variables, + final List values, + final String unparsed + ) { + counter.test(() -> { + final Either answer = eval(expectedExpression, values); + final String args = IntStream.range(0, variables.size()) + .mapToObj(i -> variables.get(i) + "=" + values.get(i)) + .collect(Collectors.joining(", ")); + final String message = String.format("f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression); + try { + final C actual = kind.kind.evaluate(expression, variables, kind.kind.fromInts(values)); + counter.checkTrue(answer.isRight(), "Error expected for f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression); + Asserts.assertEquals(message, answer.getRight(), actual); + } catch (final Exception e) { + if (answer.isRight()) { + counter.fail(e, "No error expected for %s", message); + } + } + }); + } + + @FunctionalInterface + public interface TExpression { + long evaluate(List vars); + } + + @FunctionalInterface + protected interface ExampleExpression { + long evaluate(long x, long y, long z); + } + + /** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ + public record ParsedKind(ExpressionKind kind, Parser parser) { + public E parse(final String expression, final List variables) throws Exception { + return parser.parse(expression, variables); + } + + @Override + public String toString() { + return kind.toString(); + } + } + + @FunctionalInterface + public interface Parser { + E parse(final String expression, final List variables) throws Exception; + } +} diff --git a/java/expression/parser/ParserTester.java b/java/expression/parser/ParserTester.java new file mode 100644 index 0000000..059acf2 --- /dev/null +++ b/java/expression/parser/ParserTester.java @@ -0,0 +1,76 @@ +package expression.parser; + +import base.ExtendedRandom; +import base.TestCounter; +import base.Tester; +import base.Unit; +import expression.ToMiniString; +import expression.common.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.LongBinaryOperator; +import java.util.function.LongToIntFunction; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class ParserTester extends Tester { + /* package-private */ final TestGeneratorBuilder generator; + /* package-private */ final Renderer.Builder renderer; + private final List> kinds = new ArrayList<>(); + /* package-private */ final List parens = new ArrayList<>(List.of(NodeRenderer.paren("(", ")"))); + + public ParserTester(final TestCounter counter) { + super(counter); + renderer = Renderer.builder(c -> vars -> c); + final ExtendedRandom random = counter.random(); + generator = new TestGeneratorBuilder<>(random, random::nextInt, ParserTestSet.CONSTS, true); + } + + public void unary(final String name, final int priority, final BiFunction op) { + generator.unary(name, priority); + renderer.unary(name, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast))); + } + + public void unary(final String left, final String right, final BiFunction op) { + generator.unary(left, right); + renderer.unary(left, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast))); + } + + public void binary(final String name, final int priority, final LongBinaryOperator op) { + generator.binary(name, priority); + renderer.binary(name, (unit, a, b) -> vars -> cast(op.applyAsLong(a.evaluate(vars), b.evaluate(vars)))); + } + + void kind(final ExpressionKind kind, final ParserTestSet.Parser parser) { + kinds.add(new ParserTestSet.ParsedKind<>(kind, parser)); + } + + @Override + public void test() { + for (final ParserTestSet.ParsedKind kind : kinds) { + counter.scope(kind.toString(), () -> test(kind)); + } + } + + protected void test(final ParserTestSet.ParsedKind kind) { + new ParserTestSet<>(this, kind).test(); + } + + public TestCounter getCounter() { + return counter; + } + + protected int cast(final long value) { + return (int) value; + } + + public void parens(final String... parens) { + assert parens.length % 2 == 0 : "Parens should come in pairs"; + for (int i = 0; i < parens.length; i += 2) { + this.parens.add(NodeRenderer.paren(parens[i], parens[i + 1])); + } + } +} diff --git a/java/expression/parser/package-info.java b/java/expression/parser/package-info.java new file mode 100644 index 0000000..1f7a1e9 --- /dev/null +++ b/java/expression/parser/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Expressions Parsing homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package expression.parser; \ No newline at end of file diff --git a/java/markup/MarkupListTest.java b/java/markup/MarkupListTest.java new file mode 100644 index 0000000..fbc8b79 --- /dev/null +++ b/java/markup/MarkupListTest.java @@ -0,0 +1,248 @@ +package markup; + +import base.Asserts; +import base.Selector; +import base.TestCounter; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class MarkupListTest { + public static final Consumer VARIANT = MarkupListTest.variant( + "Tex", Map.ofEntries( + Map.entry("

", "\\par{}"), Map.entry("

", ""), + Map.entry("", "\\emph{"), Map.entry("", "}"), + Map.entry("", "\\textbf{"), Map.entry("", "}"), + Map.entry("", "\\textst{"), Map.entry("", "}"), + Map.entry("
    ", "\\begin{itemize}"), Map.entry("
", "\\end{itemize}"), + Map.entry("
    ", "\\begin{enumerate}"), Map.entry("
", "\\end{enumerate}"), + Map.entry("
  • ", "\\item "), Map.entry("
  • ", "") + ) + ); + + + public static final Selector SELECTOR = new Selector(MarkupListTest.class) + .variant("3637", VARIANT) + .variant("3839", VARIANT) + .variant("4142", VARIANT) + .variant("4749", VARIANT) + + ; + + private MarkupListTest() { + } + + public static Consumer variant(final String name, final Map mapping) { + return MarkupTester.variant(MarkupListTest::test, name, mapping); + } + + private static void test(final MarkupTester.Checker checker) { + final Paragraph paragraph0 = new Paragraph(List.of(new Text("hello"))); + final String paragraph0Markup = "

    hello

    "; + + final Paragraph paragraph1 = new Paragraph(List.of( + new Strong(List.of( + new Text("1"), + new Strikeout(List.of( + new Text("2"), + new Emphasis(List.of( + new Text("3"), + new Text("4") + )), + new Text("5") + )), + new Text("6") + )) + )); + final String paragraph1Markup = "

    123456

    "; + + final Paragraph paragraph2 = new Paragraph(List.of(new Strong(List.of( + new Text("sdq"), + new Strikeout(List.of(new Emphasis(List.of(new Text("r"))), new Text("vavc"))), + new Text("zg"))) + )); + final String paragraph2Markup = "

    sdqrvavczg

    "; + + checker.test(paragraph0, paragraph0Markup); + checker.test(paragraph1, paragraph1Markup); + checker.test(paragraph2, paragraph2Markup); + + final ListItem li1 = new ListItem(List.of(new Paragraph(List.of(new Text("1.1"))), new Paragraph(List.of(new Text("1.2"))))); + final String li1Markup = "

    1.1

    1.2

    "; + final ListItem li2 = new ListItem(List.of(new Paragraph(List.of(new Text("2"))))); + final String li2Markup = "

    2

    "; + final ListItem pli1 = new ListItem(List.of(paragraph1)); + final ListItem pli2 = new ListItem(List.of(paragraph2)); + + final ListItem nestedUl = new ListItem(List.of(ul(li1, li2))); + final String nestedUlMarkup = ul(li1Markup, li2Markup); + + checker.test(ul(li1), ul(li1Markup)); + checker.test(ul(li2), ul(li2Markup)); + checker.test(ul(pli1), ul(paragraph1Markup)); + checker.test(ul(pli2), ul(paragraph2Markup)); + checker.test(ul(li1, li2), nestedUlMarkup); + checker.test(ul(pli1, pli2), ul(paragraph1Markup, paragraph2Markup)); + checker.test(ul(nestedUl), ul(nestedUlMarkup)); + + final ListItem nestedOl = new ListItem(List.of(ol(li1, li2))); + final String nestedOlMarkup = ol(li1Markup, li2Markup); + checker.test(ol(li1), ol(li1Markup)); + checker.test(ol(li2), ol(li2Markup)); + checker.test(ol(pli1), ol(paragraph1Markup)); + checker.test(ol(pli2), ol(paragraph2Markup)); + checker.test(ol(li1, li2), nestedOlMarkup); + checker.test(ol(pli1, pli2), ol(paragraph1Markup, paragraph2Markup)); + checker.test(ol(nestedOl), ol(nestedOlMarkup)); + + checker.test(ul(nestedUl, nestedOl), ul(nestedUlMarkup, nestedOlMarkup)); + checker.test(ol(nestedUl, nestedOl), ol(nestedUlMarkup, nestedOlMarkup)); + + checker.test( + ul(nestedUl, nestedOl, pli1, pli2), + ul(nestedUlMarkup, nestedOlMarkup, paragraph1Markup, paragraph2Markup) + ); + checker.test( + ol(nestedUl, nestedOl, pli1, pli2), + ol(nestedUlMarkup, nestedOlMarkup, paragraph1Markup, paragraph2Markup) + ); + + checker.test( + new Paragraph(List.of(new Strikeout(List.of(new Strong(List.of(new Strikeout(List.of(new Emphasis(List.of(new Strikeout(List.of(new Text("е"), new Text("г"), new Text("ц"))), new Strong(List.of(new Text("щэш"), new Text("игепы"), new Text("хм"))), new Strikeout(List.of(new Text("б"), new Text("е"))))), new Strong(List.of(new Strong(List.of(new Text("ю"), new Text("дърб"), new Text("еи"))), new Emphasis(List.of(new Text("зр"), new Text("дуаужш"), new Text("ш"))), new Strong(List.of(new Text("рб"), new Text("щ"))))), new Text("a"))), new Strikeout(List.of(new Text("no"), new Text("ddw"), new Strong(List.of(new Emphasis(List.of(new Text("щ"), new Text("ча"), new Text("эгфш"))), new Strikeout(List.of(new Text("фяи"), new Text("штел"), new Text("н"))), new Strikeout(List.of(new Text("ту"), new Text("ьъг"))))))), new Emphasis(List.of(new Emphasis(List.of(new Text("tc"), new Strong(List.of(new Text("щ"), new Text("э"), new Text("то"))), new Strong(List.of(new Text("а"), new Text("ц"))))), new Emphasis(List.of(new Text("hld"), new Emphasis(List.of(new Text("ыо"), new Text("яще"), new Text("лэ"))), new Text("i"))), new Text("tm"))))), new Emphasis(List.of(new Text("q"), new Emphasis(List.of(new Text("zn"), new Strong(List.of(new Text("mnphd"), new Strong(List.of(new Text("г"), new Text("вй"), new Text("шш"))), new Strong(List.of(new Text("з"), new Text("ввъ"))))), new Strikeout(List.of(new Emphasis(List.of(new Text("у"), new Text("в"), new Text("у"))), new Strikeout(List.of(new Text("лдяр"), new Text("зоъ"), new Text("эн"))), new Strikeout(List.of(new Text("в"), new Text("м"))))))), new Strikeout(List.of(new Text("cqqzbhtn"), new Text("i"), new Strong(List.of(new Text("i"), new Strikeout(List.of(new Text("э"), new Text("як"))), new Text("i"))))))), new Text("ef"))), new Strikeout(List.of(new Strikeout(List.of(new Strong(List.of(new Emphasis(List.of(new Strong(List.of(new Text("шец"), new Text("ю"), new Text("дрк"))), new Strikeout(List.of(new Text("е"), new Text("мь"), new Text("б"))), new Strong(List.of(new Text("еп"), new Text("ряэк"))))), new Strong(List.of(new Text("t"), new Emphasis(List.of(new Text("сы"), new Text("в"), new Text("к"))), new Text("rf"))), new Text("x"))), new Emphasis(List.of(new Emphasis(List.of(new Emphasis(List.of(new Text("юд"), new Text("чх"), new Text("яжюи"))), new Emphasis(List.of(new Text("и"), new Text("п"), new Text("вх"))), new Text("mf"))), new Emphasis(List.of(new Strong(List.of(new Text("шб"), new Text("вс"), new Text("е"))), new Strong(List.of(new Text("т"), new Text("шж"), new Text("ину"))), new Strong(List.of(new Text("ыа"), new Text("ьскю"))))), new Text("x"))), new Strikeout(List.of(new Emphasis(List.of(new Strong(List.of(new Text("в"), new Text("зыйгг"), new Text("о"))), new Strikeout(List.of(new Text("ок"), new Text("уч"), new Text("л"))), new Text("v"))), new Emphasis(List.of(new Strong(List.of(new Text("н"), new Text("ъчжфзтодг"), new Text("кыч"))), new Strikeout(List.of(new Text("вд"), new Text("лпбзс"), new Text("гщ"))), new Emphasis(List.of(new Text("ъ"), new Text("й"))))), new Text("n"))))), new Strong(List.of(new Strong(List.of(new Emphasis(List.of(new Strong(List.of(new Text("ю"), new Text("сдям"), new Text("ш"))), new Strong(List.of(new Text("ц"), new Text("еящж"), new Text("шн"))), new Text("upg"))), new Text("d"), new Strikeout(List.of(new Text("xu"), new Strikeout(List.of(new Text("кл"), new Text("еок"), new Text("с"))), new Strong(List.of(new Text("а"), new Text("ь"))))))), new Strong(List.of(new Strikeout(List.of(new Text("zn"), new Text("syb"), new Strong(List.of(new Text("ъзюкмц"), new Text("ндюз"))))), new Strong(List.of(new Strikeout(List.of(new Text("н"), new Text("с"), new Text("ь"))), new Strikeout(List.of(new Text("зьуес"), new Text("к"), new Text("и"))), new Strong(List.of(new Text("тв"), new Text("у"))))), new Strikeout(List.of(new Strong(List.of(new Text("ы"), new Text("г"), new Text("гм"))), new Strong(List.of(new Text("сыр"), new Text("я"), new Text("т"))), new Emphasis(List.of(new Text("ь"), new Text("махыы"))))))), new Text("k"))), new Text("q"))), new Strikeout(List.of(new Text("b"), new Text("o"), new Emphasis(List.of(new Strong(List.of(new Strikeout(List.of(new Strong(List.of(new Text("х"), new Text("йз"), new Text("ж"))), new Text("udlh"), new Strikeout(List.of(new Text("чъ"), new Text("с"))))), new Strong(List.of(new Strong(List.of(new Text("ю"), new Text("т"), new Text("яъайл"))), new Strong(List.of(new Text("х"), new Text("ри"), new Text("в"))), new Strong(List.of(new Text("щ"), new Text("вт"))))), new Text("m"))), new Text("vzb"), new Strong(List.of(new Text("oi"), new Text("r"), new Text("inpz"))))))))), + "

    егцщэшигепыхмбеюдърбеизрдуаужшшрбщanoddwщчаэгфшфяиштелнтуьъгtcщэтоацhldыоящелэitmqznmnphdгвйшшзввъувулдярзоъэнвмcqqzbhtniiэякiefшецюдркемьбепряэкtсывкrfxюдчхяжюиипвхmfшбвсетшжинуыаьскюxвзыйггоокучлvнъчжфзтодгкычвдлпбзсгщъйnюсдямшцеящжшнupgdxuклеоксаьznsybъзюкмцндюзнсьзьуескитвуыггмсырятьмахыыkqboхйзжudlhчъсютяъайлхривщвтmvzboirinpz

    " + ); + + checker.test( + new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("е"))), new Paragraph(List.of(new Text("х"))))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()), new Paragraph(List.of(new Text("эш"))))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("цць"))))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("м"))))))), new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("ю"))), new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))))), new Paragraph(List.of(new Emphasis(List.of(new Emphasis(List.of(new Text("узр"))), new Text("i"), new Emphasis(List.of(new Text("аужш"))), new Text("ш"))), new Strong(List.of(new Text("c"), new Strikeout(List.of(new Text("щ"))), new Text("a"), new Text("з"))), new Strong(List.of(new Emphasis(List.of(new Text("ь"))), new Text("ddw"), new Text("зщ"), new Text("ча"))), new Emphasis(List.of(new Strong(List.of(new Text("гфш"))), new Strikeout(List.of(new Text("фяи"))), new Text("штел"), new Text("н"))))), new OrderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("юцщ"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("э"))))))))), new ListItem(List.of(new OrderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new Paragraph(List.of(new Text("ж"))))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ыеж"))), new Paragraph(List.of(new Text("ыо"))))), new ListItem(List.of(new Paragraph(List.of(new Text("ще"))), new Paragraph(List.of(new Text("щш"))))), new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()))))), new OrderedList(List.of(new ListItem(List.of(new Paragraph(List.of(new Text("щосз"))), new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("сс"))), new UnorderedList(List.of()))))), new Paragraph(List.of(new Text("yu"), new Text("w"), new Text("ghtry"), new Emphasis(List.of(new Strikeout(List.of(new Text("прф"))), new Emphasis(List.of(new Text("р"))), new Text("я"), new Text("я"))))), new Paragraph(List.of(new Text("w"), new Strong(List.of(new Text("k"), new Emphasis(List.of(new Text("н"))), new Strikeout(List.of(new Text("в"))), new Text("м"))), new Strikeout(List.of(new Text("cqqzbhtn"), new Text("i"), new Text("м"), new Text("ю"))), new Strikeout(List.of(new Strong(List.of(new Text("ш"))), new Strong(List.of(new Text("к"))), new Text("ж"), new Text("б"))))))), new ListItem(List.of(new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("е"))), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("ед"))), new UnorderedList(List.of()))))), new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()), new Paragraph(List.of(new Text("п"))))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("э"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("к"))))))), new Paragraph(List.of(new Strong(List.of(new Strong(List.of(new Text("с"))), new Text("x"), new Emphasis(List.of(new Text("йюд"))), new Text("чх"))), new Strikeout(List.of(new Strong(List.of(new Text("жюи"))), new Emphasis(List.of(new Text("и"))), new Strong(List.of(new Text("ьмт"))), new Text("йц"))), new Emphasis(List.of(new Strong(List.of(new Text("шб"))), new Strong(List.of(new Text("еф"))), new Text("ут"), new Text("шж"))), new Emphasis(List.of(new Emphasis(List.of(new Text("ну"))), new Strong(List.of(new Text("ыа"))), new Text("ьскю"), new Text("чз"))))))), new ListItem(List.of(new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()), new Paragraph(List.of(new Text("ыйгг"))))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()), new Paragraph(List.of(new Text("ф"))))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ч"))))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))))), new Paragraph(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("э"))), new Text("amqcfdzrg"), new Emphasis(List.of(new Text("т"))), new Text("з"))), new Text("b"), new Emphasis(List.of(new Strikeout(List.of(new Text("энфны"))), new Strikeout(List.of(new Text("гщ"))), new Text("ы"), new Text("шя"))), new Text("uvpqzhn"))), new UnorderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ящж"))), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("цлл"))))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ъ"))))))), new Paragraph(List.of(new Strong(List.of(new Strong(List.of(new Text("ъ"))), new Strikeout(List.of(new Text("кл"))), new Strikeout(List.of(new Text("счи"))), new Text("ра"))), new Strong(List.of(new Strikeout(List.of(new Text("ь"))), new Text("zn"), new Text("ъ"), new Text("умъъзюкмц"))), new Strikeout(List.of(new Emphasis(List.of(new Text("дюз"))), new Strong(List.of(new Text("эы"))), new Text("и"), new Text("р"))), new Emphasis(List.of(new Strong(List.of(new Text("ьуес"))), new Strikeout(List.of(new Text("йгтв"))), new Text("у"), new Text("еы"))))))))), + "
        1. е

          х

            1. эш

              • цць

                • м

                      1. ю

                                  узрiаужшшcщьddwзщчагфшфяиштелн

                                          1. юцщ

                                                  1. э

                                                      1. ж

                                                        1. ыеж

                                                          ыо

                                                        2. ще

                                                          щш

                                                            1. щосз

                                                                          • сс

                                                                            yuwghtryпрфряя

                                                                            wkнвмcqqzbhtniмюшкжб

                                                                                                        • е

                                                                                                              • ед

                                                                                                                    1. п

                                                                                                                      • э

                                                                                                                              1. к

                                                                                                                              сxйюдчхжюииьмтйцшбефутшжнуыаьскючз

                                                                                                                                  1. ыйгг

                                                                                                                                      • ф

                                                                                                                                        1. ч

                                                                                                                                            эamqcfdzrgтзbэнфныгщышяuvpqzhn

                                                                                                                                                    1. ящж

                                                                                                                                                        1. цлл

                                                                                                                                                          1. ъ

                                                                                                                                                          ъклсчираьznъумъъзюкмцдюзэыирьуесйгтвуеы

                                                                                                                                                        " + ); + + checker.test( + new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("е"))))), new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("нцйцць"))), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("м"))))))), new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ю"))))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("щ"))))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))), new Paragraph(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("зр"))), new Text("i"), new Text("и"), new Text("г"), new Text("с"))), new Strong(List.of(new Strong(List.of(new Text("шмрб"))), new Strong(List.of(new Text("ь"))), new Text("з"), new Text("з"), new Text("фь"))), new Text("ddw"), new Strong(List.of(new Emphasis(List.of(new Text("щ"))), new Strong(List.of(new Text("втъп"))), new Text("ш"), new Text("ч"), new Text("фяи"))), new Strong(List.of(new Emphasis(List.of(new Text("тел"))), new Text("н"), new Text("ь"), new Text("ддзюцщ"), new Text("пт"))))), new Paragraph(List.of(new Text("n"), new Text("zi"), new Strong(List.of(new Emphasis(List.of(new Text("ж"))), new Text("t"), new Text("ыеж"), new Text("ч"), new Text("г"))), new Text("kwt"), new Strong(List.of(new Strong(List.of(new Text("э"))), new Text("нх"), new Text("уи"), new Text("о"), new Text("п"))))), new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("ж"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("сс"))))), new ListItem(List.of(new Paragraph(List.of(new Text("т"))))))))), new ListItem(List.of(new UnorderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("щу"))))))), new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("ир"))))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("зоъ"))), new Paragraph(List.of(new Text("е"))))), new ListItem(List.of(new Paragraph(List.of(new Text("в"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))), new OrderedList(List.of(new ListItem(List.of(new Paragraph(List.of(new Text("сснюпия"))), new Paragraph(List.of(new Text("щ"))))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("э"))), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new Paragraph(List.of(new Text("м"))), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("е"))), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))))), new ListItem(List.of(new Paragraph(List.of(new Strong(List.of(new Emphasis(List.of(new Text("п"))), new Text("l"), new Text("р"), new Text("п"), new Text("уерсы"))), new Strikeout(List.of(new Strikeout(List.of(new Text("к"))), new Text("rf"), new Text("екйюд"), new Text("чх"), new Text("яжюи"))), new Emphasis(List.of(new Strikeout(List.of(new Text("кьмт"))), new Strikeout(List.of(new Text("рщюереф"))), new Text("ут"), new Text("шж"), new Text("ину"))), new Strong(List.of(new Strong(List.of(new Text("дгб"))), new Emphasis(List.of(new Text("кю"))), new Text("чз"), new Text("мв"), new Text("зыйгг"))), new Strong(List.of(new Strikeout(List.of(new Text("ш"))), new Text("ф"), new Text("я"), new Text("ч"), new Text("ме"))))), new Paragraph(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("э"))), new Text("amqcfdzrg"), new Text("кыч"), new Text("к"), new Text("я"))), new Strikeout(List.of(new Strong(List.of(new Text("нфны"))), new Strikeout(List.of(new Text("гщ"))), new Text("ы"), new Text("шя"), new Text("е"))), new Strong(List.of(new Strong(List.of(new Text("ъю"))), new Emphasis(List.of(new Text("яхе"))), new Text("б"), new Text("бц"), new Text("еящж"))), new Text("cn"), new Emphasis(List.of(new Strong(List.of(new Text("як"))), new Text("въ"), new Text("оде"), new Text("кл"), new Text("еок"))))), new Paragraph(List.of(new Strikeout(List.of(new Strong(List.of(new Text("а"))), new Strong(List.of(new Text("иь"))), new Text("аш"), new Text("ъ"), new Text("умъъзюкмц"))), new Strikeout(List.of(new Emphasis(List.of(new Text("дюз"))), new Strong(List.of(new Text("эы"))), new Text("и"), new Text("р"), new Text("зьуес"))), new Strikeout(List.of(new Strikeout(List.of(new Text("и"))), new Strong(List.of(new Text("тв"))), new Text("у"), new Text("еы"), new Text("г"))), new Text("atsui"), new Strikeout(List.of(new Text("y"), new Text("щз"), new Text("н"), new Text("е"), new Text("э"))))), new Paragraph(List.of(new Emphasis(List.of(new Text("o"), new Text("rz"), new Text("к"), new Text("к"), new Text("б"))), new Emphasis(List.of(new Strong(List.of(new Text("ьх"))), new Emphasis(List.of(new Text("ил"))), new Text("ф"), new Text("пмгр"), new Text("и"))), new Emphasis(List.of(new Text("lhovy"), new Emphasis(List.of(new Text("ъайл"))), new Text("ь"), new Text("э"), new Text("п"))), new Strikeout(List.of(new Strong(List.of(new Text("щщ"))), new Strong(List.of(new Text("х"))), new Text("б"), new Text("е"), new Text("к"))), new Emphasis(List.of(new Strikeout(List.of(new Text("чяя"))), new Text("х"), new Text("я"), new Text("р"), new Text("ю"))))), new Paragraph(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("йл"))), new Emphasis(List.of(new Text("змл"))), new Text("б"), new Text("аж"), new Text("ъ"))), new Strong(List.of(new Strong(List.of(new Text("энян"))), new Emphasis(List.of(new Text("ю"))), new Text("п"), new Text("ымы"), new Text("ешьи"))), new Emphasis(List.of(new Strong(List.of(new Text("к"))), new Strikeout(List.of(new Text("яэ"))), new Text("п"), new Text("юзщ"), new Text("я"))), new Text("w"), new Emphasis(List.of(new Text("se"), new Text("о"), new Text("ъязе"), new Text("гзко"), new Text("ъ"))))))), new ListItem(List.of(new OrderedList(List.of(new ListItem(List.of(new Paragraph(List.of(new Text("ч"))), new Paragraph(List.of(new Text("пз"))))), new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("й"))))), new ListItem(List.of(new Paragraph(List.of(new Text("лчж"))), new Paragraph(List.of(new Text("чв"))))), new ListItem(List.of(new Paragraph(List.of(new Text("с"))), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new Paragraph(List.of(new Text("ь"))), new Paragraph(List.of(new Text("ъ"))))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("вп"))), new Paragraph(List.of(new Text("р"))))), new ListItem(List.of(new OrderedList(List.of()))))), new Paragraph(List.of(new Text("ds"), new Emphasis(List.of(new Strikeout(List.of(new Text("дйгып"))), new Emphasis(List.of(new Text("и"))), new Text("сэ"), new Text("е"), new Text("юо"))), new Emphasis(List.of(new Strikeout(List.of(new Text("бвщ"))), new Text("d"), new Text("ъ"), new Text("ит"), new Text("бщ"))), new Emphasis(List.of(new Text("w"), new Strikeout(List.of(new Text("гсщ"))), new Text("ъ"), new Text("срцч"), new Text("хе"))), new Text("m"))), new OrderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("е"))), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("оото"))), new OrderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))))))), new ListItem(List.of(new Paragraph(List.of(new Emphasis(List.of(new Emphasis(List.of(new Text("я"))), new Strong(List.of(new Text("сшъ"))), new Text("лм"), new Text("ы"), new Text("рц"))), new Emphasis(List.of(new Strikeout(List.of(new Text("я"))), new Strikeout(List.of(new Text("ъ"))), new Text("п"), new Text("дхдэ"), new Text("щэ"))), new Emphasis(List.of(new Text("dtt"), new Emphasis(List.of(new Text("дрм"))), new Text("в"), new Text("яешц"), new Text("йшй"))), new Strong(List.of(new Strong(List.of(new Text("мив"))), new Text("u"), new Text("у"), new Text("к"), new Text("б"))), new Strikeout(List.of(new Text("c"), new Text("э"), new Text("м"), new Text("п"), new Text("о"))))), new UnorderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("х"))), new Paragraph(List.of(new Text("й"))))), new ListItem(List.of(new Paragraph(List.of(new Text("эя"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("ф"))))), new ListItem(List.of(new OrderedList(List.of()))))), new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new Paragraph(List.of(new Text("щ"))))), new ListItem(List.of(new Paragraph(List.of(new Text("чи"))), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("к"))), new OrderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()), new OrderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("ф"))))))), new OrderedList(List.of(new ListItem(List.of(new OrderedList(List.of()), new UnorderedList(List.of()))), new ListItem(List.of(new Paragraph(List.of(new Text("м"))), new Paragraph(List.of(new Text("щцс"))))), new ListItem(List.of(new Paragraph(List.of(new Text("вус"))), new Paragraph(List.of(new Text("я"))))), new ListItem(List.of(new Paragraph(List.of(new Text("кр"))))), new ListItem(List.of(new UnorderedList(List.of()))))), new UnorderedList(List.of(new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("я"))))), new ListItem(List.of(new UnorderedList(List.of()), new Paragraph(List.of(new Text("гр"))))), new ListItem(List.of(new Paragraph(List.of(new Text("ж"))), new UnorderedList(List.of()))), new ListItem(List.of(new UnorderedList(List.of()))), new ListItem(List.of(new OrderedList(List.of()))))))))), + "
                                                                                                                                                            1. е

                                                                                                                                                                    1. нцйцць

                                                                                                                                                                      1. м

                                                                                                                                                                            1. ю

                                                                                                                                                                              • щ

                                                                                                                                                                                    зрiигсшмрбьззфьddwщвтъпшчфяителньддзюцщпт

                                                                                                                                                                                    nziжtыежчгkwtэнхуиоп

                                                                                                                                                                                        1. ж

                                                                                                                                                                                              • сс

                                                                                                                                                                                              • т

                                                                                                                                                                                                              • щу

                                                                                                                                                                                                                1. ир

                                                                                                                                                                                                                    1. зоъ

                                                                                                                                                                                                                      е

                                                                                                                                                                                                                    2. в

                                                                                                                                                                                                                        1. сснюпия

                                                                                                                                                                                                                          щ

                                                                                                                                                                                                                            • э

                                                                                                                                                                                                                                    • м

                                                                                                                                                                                                                                                            1. е

                                                                                                                                                                                                                                                                1. пlрпуерсыкrfекйюдчхяжюикьмтрщюерефутшжинудгбкючзмвзыйггшфячме

                                                                                                                                                                                                                                                                  эamqcfdzrgкычкянфныгщышяеъюяхеббцеящжcnяквъодеклеок

                                                                                                                                                                                                                                                                  аиьашъумъъзюкмцдюзэыирзьуеситвуеыгatsuiyщзнеэ

                                                                                                                                                                                                                                                                  orzккбьхилфпмгриlhovyъайльэпщщхбекчяяхярю

                                                                                                                                                                                                                                                                  йлзмлбажъэнянюпымыешьикяэпюзщяwseоъязегзкоъ

                                                                                                                                                                                                                                                                  1. ч

                                                                                                                                                                                                                                                                    пз

                                                                                                                                                                                                                                                                    1. й

                                                                                                                                                                                                                                                                    2. лчж

                                                                                                                                                                                                                                                                      чв

                                                                                                                                                                                                                                                                    3. с

                                                                                                                                                                                                                                                                        • ь

                                                                                                                                                                                                                                                                          ъ

                                                                                                                                                                                                                                                                                • вп

                                                                                                                                                                                                                                                                                  р

                                                                                                                                                                                                                                                                                  dsдйгыписэеюобвщdъитбщwгсщъсрцчхеm

                                                                                                                                                                                                                                                                                              • е

                                                                                                                                                                                                                                                                                                      • оото

                                                                                                                                                                                                                                                                                                              • ясшълмырцяъпдхдэщэdttдрмвяешцйшймивuукбcэмпо

                                                                                                                                                                                                                                                                                                                    1. х

                                                                                                                                                                                                                                                                                                                      й

                                                                                                                                                                                                                                                                                                                    2. эя

                                                                                                                                                                                                                                                                                                                        • ф

                                                                                                                                                                                                                                                                                                                            1. щ

                                                                                                                                                                                                                                                                                                                            2. чи

                                                                                                                                                                                                                                                                                                                              1. к

                                                                                                                                                                                                                                                                                                                                    1. ф

                                                                                                                                                                                                                                                                                                                                        • м

                                                                                                                                                                                                                                                                                                                                          щцс

                                                                                                                                                                                                                                                                                                                                        • вус

                                                                                                                                                                                                                                                                                                                                          я

                                                                                                                                                                                                                                                                                                                                        • кр

                                                                                                                                                                                                                                                                                                                                            • я

                                                                                                                                                                                                                                                                                                                                              • гр

                                                                                                                                                                                                                                                                                                                                              • ж

                                                                                                                                                                                                                                                                                                                                                  " + ); + + checkTypes(); + } + + private static OrderedList ol(final ListItem... items) { + return new OrderedList(List.of(items)); + } + + private static String ol(final String... items) { + return list("ol", items); + } + + private static UnorderedList ul(final ListItem... items) { + return new UnorderedList(List.of(items)); + } + + private static String ul(final String... items) { + return list("ul", items); + } + + private static String list(final String type, final String[] items) { + return "<" + type + ">" + Stream.of(items).map(item -> "
                                                                                                                                                                                                                                                                                                                                                • " + item + "
                                                                                                                                                                                                                                                                                                                                                • ").collect(Collectors.joining()) + ""; + } + + private static Class loadClass(final String name) { + try { + return Class.forName(name); + } catch (final ClassNotFoundException e) { + throw Asserts.error("Cannot find class %s: %s", name, e); + } + } + + private static Map> loadClasses(final String... names) { + return Arrays.stream(names) + .collect(Collectors.toUnmodifiableMap(Function.identity(), name -> loadClass("markup." + name))); + } + + private static void checkTypes() { + final Map> classes = loadClasses("Text", "Emphasis", "Strikeout", "Strong", "Paragraph", "OrderedList", "UnorderedList", "ListItem"); + final String[] inlineClasses = {"Text", "Emphasis", "Strikeout", "Strong"}; + + checkConstructor(classes, "OrderedList", "ListItem"); + checkConstructor(classes, "UnorderedList", "ListItem"); + checkConstructor(classes, "ListItem", "OrderedList", "UnorderedList", "Paragraph"); + Stream.of("Paragraph", "Emphasis", "Strong", "Strikeout") + .forEach(parent -> checkConstructor(classes, parent, inlineClasses)); + } + + private static void checkConstructor(final Map> classes, final String parent, final String... children) { + new TypeChecker(classes, parent, children).checkConstructor(); + } + + private static class TypeChecker { + private final Map> classes; + private final Set> children; + private final Class parent; + + public TypeChecker(final Map> classes, final String parent, final String[] children) { + this.classes = classes; + this.children = Arrays.stream(children).map(classes::get).collect(Collectors.toUnmodifiableSet()); + this.parent = Objects.requireNonNull(classes.get(parent)); + } + + private void checkClassType(final Class classType) { + final Predicate> isAssignableFrom = classType::isAssignableFrom; + checkType(parent, Predicate.not(isAssignableFrom), "not ", children.stream()); + checkType(parent, isAssignableFrom, "", classes.values().stream().filter(Predicate.not(children::contains))); + } + + private static void checkType(final Class parent, final Predicate> predicate, final String not, final Stream> children) { + children.filter(predicate).findAny().ifPresent(child -> { + throw Asserts.error("%s is %scompatible with child of type %s", parent, not, child); + }); + } + + @SuppressWarnings("ChainOfInstanceofChecks") + private void checkParametrizedType(final ParameterizedType type) { + final Type actualType = type.getActualTypeArguments()[0]; + if (actualType instanceof Class) { + checkClassType((Class) actualType); + } else if (actualType instanceof WildcardType) { + for (final Type boundType : ((WildcardType) actualType).getUpperBounds()) { + if (boundType instanceof Class) { + checkClassType((Class) boundType); + } else { + throw Asserts.error("Unsupported wildcard bound type in %s(List<...>): %s", parent, boundType); + } + } + } else { + throw Asserts.error("Unsupported type argument type in %s(List<...>): %s", parent, actualType); + } + } + + @SuppressWarnings("ChainOfInstanceofChecks") + private void checkConstructor() { + try { + final Type argType = parent.getConstructor(List.class).getGenericParameterTypes()[0]; + if (argType instanceof ParameterizedType) { + checkParametrizedType((ParameterizedType) argType); + } else if (argType instanceof Class) { + throw Asserts.error("Raw List type in %s(List)", parent.getName()); + } else { + throw Asserts.error("Unsupported argument type in %s(List<...>): %s", parent.getName(), argType); + } + } catch (final NoSuchMethodException e) { + throw Asserts.error("Missing %s(List<...>) constructor: %s", parent.getName(), e); + } + } + } + + public static void main(final String... args) { + MarkupTest.main(args); + SELECTOR.main(args); + } +} diff --git a/java/markup/MarkupTest.java b/java/markup/MarkupTest.java new file mode 100644 index 0000000..9574b13 --- /dev/null +++ b/java/markup/MarkupTest.java @@ -0,0 +1,97 @@ +package markup; + +import base.Selector; +import base.TestCounter; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class MarkupTest { + private static final Consumer MARKDOWN = MarkupTest.variant( + "Markdown", Map.of( + "&[", "", "&]", "", + "<", "", ">", "" + ) + ); + + private static final Consumer HTML = MarkupTest.variant( + "Html", Map.of( + "&[", "

                                                                                                                                                                                                                                                                                                                                                  ", "&]", "

                                                                                                                                                                                                                                                                                                                                                  ", + "*<", "", "*>", "", + "__<", "", "__>", "", + "~<", "", "~>", "" + ) + ); + + public static final Selector SELECTOR = new Selector(MarkupTest.class) + .variant("Base", MARKDOWN) + .variant("3637", MARKDOWN) + .variant("3839", MARKDOWN) + .variant("3435", HTML) + .variant("3233", HTML) + .variant("4142", MARKDOWN) + .variant("4749", MARKDOWN) + + ; + + public static Consumer variant(final String name, final Map mapping) { + return MarkupTester.variant(MarkupTest::test, name, mapping); + } + + private MarkupTest() { + } + + public static void test(final MarkupTester.Checker checker) { + test(checker, new Paragraph(List.of(new Text("Hello"))), "Hello"); + test(checker, new Paragraph(List.of(new Emphasis(List.of(new Text("Hello"))))), "*"); + test(checker, new Paragraph(List.of(new Strong(List.of(new Text("Hello"))))), "__"); + test(checker, new Paragraph(List.of(new Strikeout(List.of(new Text("Hello"))))), "~"); + + final Paragraph paragraph = new Paragraph(List.of( + new Strong(List.of( + new Text("1"), + new Strikeout(List.of( + new Text("2"), + new Emphasis(List.of( + new Text("3"), + new Text("4") + )), + new Text("5") + )), + new Text("6") + )) + )); + test(checker, paragraph, "__<1~<2*<34*>5~>6__>"); + test( + checker, + new Paragraph(List.of(new Strong(List.of( + new Text("sdq"), + new Strikeout(List.of(new Emphasis(List.of(new Text("r"))), new Text("vavc"))), + new Text("zg") + )))), + "__vavc~>zg__>" + ); + test( + checker, + new Paragraph(List.of(new Strikeout(List.of(new Strong(List.of(new Strikeout(List.of(new Text("е"), new Text("е"), new Text("г"))), new Text("ftje"), new Strong(List.of(new Text("йцць"), new Text("р"))))), new Strong(List.of(new Strikeout(List.of(new Text("д"), new Text("б"), new Text("е"))), new Strong(List.of(new Text("лъ"), new Text("шщ"))), new Strong(List.of(new Text("б"), new Text("еи"))))), new Emphasis(List.of(new Emphasis(List.of(new Text("м"), new Text("к"))), new Emphasis(List.of(new Text("уаужш"), new Text("ш"))), new Strong(List.of(new Text("рб"), new Text("щ"))))))), new Text("a"), new Strikeout(List.of(new Text("no"), new Text("ddw"), new Strong(List.of(new Emphasis(List.of(new Text("щ"), new Text("ча"))), new Emphasis(List.of(new Text("ъп"), new Text("ш"))), new Text("psk"))))))), + "~<__<~<еег~>ftje__<йццьр__>__>__<~<дбе~>__<лъшщ__>__<беи__>__>*<*<мк*>*<уаужшш*>__<рбщ__>*>~>a~*<ъпш*>psk__>~>" + ); + test( + checker, + new Paragraph(List.of(new Strikeout(List.of(new Strong(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("об"))), new Strikeout(List.of(new Text("ц"))), new Text("зснцйцць"), new Text("р"), new Text("а"))), new Strikeout(List.of(new Strikeout(List.of(new Text("б"))), new Strikeout(List.of(new Text("ялъ"))), new Text("шщ"), new Text("ф"), new Text("м"))), new Emphasis(List.of(new Emphasis(List.of(new Text("узр"))), new Text("i"), new Text("и"), new Text("г"), new Text("с"))), new Strong(List.of(new Strong(List.of(new Text("шмрб"))), new Strong(List.of(new Text("ь"))), new Text("з"), new Text("з"), new Text("фь"))), new Text("ddw"))), new Strong(List.of(new Emphasis(List.of(new Emphasis(List.of(new Text("ввтъп"))), new Strong(List.of(new Text("ш"))), new Text("хте"), new Text("чюе"), new Text("х"))), new Text("g"), new Strikeout(List.of(new Strikeout(List.of(new Text("ддзюцщ"))), new Strong(List.of(new Text("к"))), new Text("йщ"), new Text("э"), new Text("то"))), new Strong(List.of(new Emphasis(List.of(new Text("ж"))), new Text("t"), new Text("ыеж"), new Text("ч"), new Text("г"))), new Text("kwt"))), new Strong(List.of(new Strong(List.of(new Emphasis(List.of(new Text("ш"))), new Strong(List.of(new Text("х"))), new Text("уи"), new Text("о"), new Text("п"))), new Emphasis(List.of(new Text("zn"), new Strong(List.of(new Text("нш"))), new Text("диуьг"), new Text("вй"), new Text("шш"))), new Strong(List.of(new Emphasis(List.of(new Text("ьмша"))), new Emphasis(List.of(new Text("у"))), new Text("в"), new Text("у"), new Text("ир"))), new Emphasis(List.of(new Strikeout(List.of(new Text("я"))), new Strikeout(List.of(new Text("зоъ"))), new Text("эн"), new Text("ъ"), new Text("ьо"))), new Text("cqqzbhtn"))), new Text("i"), new Strong(List.of(new Text("i"), new Strikeout(List.of(new Strong(List.of(new Text("ш"))), new Strong(List.of(new Text("к"))), new Text("ж"), new Text("б"), new Text("ащ"))), new Strikeout(List.of(new Strikeout(List.of(new Text("пян"))), new Emphasis(List.of(new Text("ц"))), new Text("ю"), new Text("дрк"), new Text("лщ"))), new Strong(List.of(new Text("xywa"), new Text("ряэк"), new Text("п"), new Text("э"), new Text("т"))), new Strong(List.of(new Strikeout(List.of(new Text("е"))), new Text("чб"), new Text("зс"), new Text("екйюд"), new Text("чх"))))))), new Strikeout(List.of(new Strong(List.of(new Strong(List.of(new Strong(List.of(new Text("юи"))), new Emphasis(List.of(new Text("и"))), new Text("п"), new Text("вх"), new Text("ф"))), new Strong(List.of(new Strong(List.of(new Text("щюереф"))), new Text("otvic"), new Text("ж"), new Text("уыа"), new Text("ьскю"))), new Text("x"), new Strikeout(List.of(new Emphasis(List.of(new Text("ж"))), new Strikeout(List.of(new Text("зыйгг"))), new Text("о"), new Text("ш"), new Text("ф"))), new Text("zf"))), new Emphasis(List.of(new Text("a"), new Strikeout(List.of(new Emphasis(List.of(new Text("э"))), new Text("amqcfdzrg"), new Text("кыч"), new Text("к"), new Text("я"))), new Strikeout(List.of(new Strong(List.of(new Text("нфны"))), new Strikeout(List.of(new Text("гщ"))), new Text("ы"), new Text("шя"), new Text("е"))), new Strong(List.of(new Strong(List.of(new Text("ъю"))), new Emphasis(List.of(new Text("яхе"))), new Text("б"), new Text("бц"), new Text("еящж"))), new Text("cn"))), new Emphasis(List.of(new Strong(List.of(new Strong(List.of(new Text("л"))), new Text("wl"), new Text("оде"), new Text("кл"), new Text("еок"))), new Strikeout(List.of(new Strikeout(List.of(new Text("яяиь"))), new Strong(List.of(new Text("ик"))), new Text("юью"), new Text("ь"), new Text("э"))), new Emphasis(List.of(new Strikeout(List.of(new Text("жп"))), new Emphasis(List.of(new Text("ц"))), new Text("ндюз"), new Text("ч"), new Text("н"))), new Text("r"), new Strikeout(List.of(new Strikeout(List.of(new Text("зьуес"))), new Text("к"), new Text("и"), new Text("к"), new Text("й"))))), new Strikeout(List.of(new Emphasis(List.of(new Strikeout(List.of(new Text("еы"))), new Emphasis(List.of(new Text("б"))), new Text("сйсыр"), new Text("я"), new Text("т"))), new Emphasis(List.of(new Emphasis(List.of(new Text("з"))), new Strong(List.of(new Text("ахыы"))), new Text("х"), new Text("м"), new Text("п"))), new Strikeout(List.of(new Text("b"), new Text("o"), new Text("шьх"), new Text("йз"), new Text("ж"))), new Text("udlh"), new Strikeout(List.of(new Strikeout(List.of(new Text("п"))), new Text("хъфоз"), new Text("е"), new Text("ыф"), new Text("ю"))))), new Text("z"))), new Text("hy"), new Strong(List.of(new Text("tyv"), new Text("x"), new Strikeout(List.of(new Text("vzb"), new Strong(List.of(new Text("oi"), new Text("r"), new Text("ю"), new Text("с"), new Text("еппзмл"))), new Text("r"), new Emphasis(List.of(new Strikeout(List.of(new Text("игс"))), new Emphasis(List.of(new Text("нян"))), new Text("ю"), new Text("с"), new Text("цлъ"))), new Text("rptq"))), new Emphasis(List.of(new Strong(List.of(new Text("u"), new Strong(List.of(new Text("кще"))), new Text("пхте"), new Text("у"), new Text("з"))), new Text("zbmflu"), new Strikeout(List.of(new Strong(List.of(new Text("л"))), new Emphasis(List.of(new Text("ко"))), new Text("ъ"), new Text("щ"), new Text("жч"))), new Strong(List.of(new Strong(List.of(new Text("ж"))), new Strikeout(List.of(new Text("еъ"))), new Text("в"), new Text("ф"), new Text("йб"))), new Text("kvuf"))), new Strikeout(List.of(new Text("azn"), new Strikeout(List.of(new Strong(List.of(new Text("ъ"))), new Emphasis(List.of(new Text("ре"))), new Text("йч"), new Text("н"), new Text("ир"))), new Emphasis(List.of(new Emphasis(List.of(new Text("с"))), new Strong(List.of(new Text("щ"))), new Text("ъсбчиюзи"), new Text("сэ"), new Text("е"))), new Strikeout(List.of(new Emphasis(List.of(new Text("о"))), new Text("г"), new Text("бвщ"), new Text("пр"), new Text("йвъч"))), new Text("c"))))), new Strong(List.of(new Strikeout(List.of(new Strikeout(List.of(new Emphasis(List.of(new Text("жбфц"))), new Strong(List.of(new Text("рцч"))), new Text("хе"), new Text("ж"), new Text("ы"))), new Strikeout(List.of(new Emphasis(List.of(new Text("я"))), new Emphasis(List.of(new Text("мн"))), new Text("яе"), new Text("е"), new Text("дхпг"))), new Emphasis(List.of(new Emphasis(List.of(new Text("нй"))), new Text("gf"), new Text("и"), new Text("хю"), new Text("ця"))), new Strong(List.of(new Emphasis(List.of(new Text("о"))), new Emphasis(List.of(new Text("ъ"))), new Text("лм"), new Text("ы"), new Text("рц"))), new Emphasis(List.of(new Strikeout(List.of(new Text("я"))), new Text("ыл"), new Text("г"), new Text("я"), new Text("эй"))))), new Text("qi"), new Emphasis(List.of(new Text("dtt"), new Emphasis(List.of(new Strong(List.of(new Text("пв"))), new Text("i"), new Text("яешц"), new Text("йшй"), new Text("щмив"))), new Text("u"), new Text("d"), new Strikeout(List.of(new Strikeout(List.of(new Text("о"))), new Text("иов"), new Text("к"), new Text("кои"), new Text("яс"))))), new Strikeout(List.of(new Emphasis(List.of(new Text("j"), new Strong(List.of(new Text("эя"))), new Text("шыф"), new Text("дрн"), new Text("щ"))), new Text("j"), new Strong(List.of(new Emphasis(List.of(new Text("ю"))), new Strikeout(List.of(new Text("чцин"))), new Text("сф"), new Text("з"), new Text("юэи"))), new Emphasis(List.of(new Emphasis(List.of(new Text("цс"))), new Text("ювус"), new Text("ъ"), new Text("щэны"), new Text("б"))), new Emphasis(List.of(new Text("cbogf"), new Text("э"), new Text("ж"), new Text("ш"), new Text("м"))))), new Strikeout(List.of(new Strong(List.of(new Strong(List.of(new Text("ф"))), new Text("w"), new Text("цеъ"), new Text("н"), new Text("ем"))), new Strikeout(List.of(new Strikeout(List.of(new Text("л"))), new Strong(List.of(new Text("э"))), new Text("лд"), new Text("эд"), new Text("л"))), new Emphasis(List.of(new Emphasis(List.of(new Text("уг"))), new Strikeout(List.of(new Text("зп"))), new Text("юб"), new Text("сгы"), new Text("шю"))), new Strikeout(List.of(new Emphasis(List.of(new Text("рйей"))), new Text("с"), new Text("зюй"), new Text("р"), new Text("в"))), new Emphasis(List.of(new Text("p"), new Text("у"), new Text("на"), new Text("б"), new Text("х"))))))))), + "~<__<~<*<об*>~<ц~>зснцйццьра~>~<~<б~>~<ялъ~>шщфм~>*<*<узр*>iигс*>__<__<шмрб__>__<ь__>ззфь__>ddw__>__<*<*<ввтъп*>__<ш__>хтечюех*>g~<~<ддзюцщ~>__<к__>йщэто~>__<*<ж*>tыежчг__>kwt__>__<__<*<ш*>__<х__>уиоп__>*диуьгвйшш*>__<*<ьмша*>*<у*>вуир__>*<~<я~>~<зоъ~>энъьо*>cqqzbhtn__>i____<к__>жбащ~>~<~<пян~>*<ц*>юдрклщ~>____<~<е~>чбзсекйюдчх__>__>~>~<__<__<__<юи__>*<и*>пвхф__>__<__<щюереф__>otvicжуыаьскю__>x~<*<ж*>~<зыйгг~>ошф~>zf__>*amqcfdzrgкычкя~>~<__<нфны__>~<гщ~>ышяе~>__<__<ъю__>*<яхе*>ббцеящж__>cn*>*<__<__<л__>wlодеклеок__>~<~<яяиь~>__<ик__>юьюьэ~>*<~<жп~>*<ц*>ндюзчн*>r~<~<зьуес~>кикй~>*>~<*<~<еы~>*<б*>сйсырят*>*<*<з*>__<ахыы__>хмп*>~udlh~<~<п~>хъфозеыфю~>~>z~>hy__r*<~<игс~>*<нян*>юсцлъ*>rptq~>*<__пхтеуз__>zbmflu~<__<л__>*<ко*>ъщжч~>__<__<ж__>~<еъ~>вфйб__>kvuf*>~*<ре*>йчнир~>*<*<с*>__<щ__>ъсбчиюзисэе*>~<*<о*>гбвщпрйвъч~>c~>__>__<~<~<*<жбфц*>__<рцч__>хежы~>~<*<я*>*<мн*>яеедхпг~>*<*<нй*>gfихюця*>__<*<о*>*<ъ*>лмырц__>*<~<я~>ылгяэй*>~>qi*iяешцйшйщмив*>ud~<~<о~>иовккоияс~>*>~<*шыфдрнщ*>j__<*<ю*>~<чцин~>сфзюэи__>*<*<цс*>ювусъщэныб*>*~>~<__<__<ф__>wцеънем__>~<~<л~>__<э__>лдэдл~>*<*<уг*>~<зп~>юбсгышю*>~<*<рйей*>сзюйрв~>*~>__>" + ); + } + + private static void test(final MarkupTester.Checker checker, final Paragraph paragraph, final String template) { + checker.test(paragraph, String.format("&[%s&]", template)); + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/markup/MarkupTester.java b/java/markup/MarkupTester.java new file mode 100644 index 0000000..4d93d97 --- /dev/null +++ b/java/markup/MarkupTester.java @@ -0,0 +1,71 @@ +package markup; + +import base.Asserts; +import base.BaseChecker; +import base.TestCounter; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class MarkupTester { + private final Map mapping; + private final String toString; + + private MarkupTester(final Map mapping, final String toString) { + this.mapping = mapping; + this.toString = toString; + } + + public static Consumer variant(final Consumer checker, final String name, final Map mapping) { + return counter -> test(checker).accept(new MarkupTester(mapping, "to" + name), counter); + } + + public static BiConsumer test(final Consumer tester) { + return (checker, counter) -> tester.accept(checker.new Checker(counter)); + } + + @Override + public String toString() { + return toString; + } + + public class Checker extends BaseChecker { + public Checker(final TestCounter counter) { + super(counter); + } + + private MethodHandle findMethod(final T value) { + try { + return MethodHandles.publicLookup().findVirtual(value.getClass(), toString, MethodType.methodType(void.class, StringBuilder.class)); + } catch (final NoSuchMethodException | IllegalAccessException e) { + throw Asserts.error("Cannot find method 'void %s(StringBuilder)' for %s", toString, value.getClass()); + } + } + + public void test(final T value, String expectedTemplate) { + final MethodHandle method = findMethod(value); + for (final Map.Entry entry : mapping.entrySet()) { + expectedTemplate = expectedTemplate.replace(entry.getKey(), entry.getValue()); + } + + final String expected = expectedTemplate; + counter.println("Test " + counter.getTestNo()); + counter.test(() -> { + final StringBuilder sb = new StringBuilder(); + try { + method.invoke(value, sb); + } catch (final Throwable e) { + throw Asserts.error("%s(StringBuilder) for %s thrown exception: %s", toString, value.getClass(), e); + } + Asserts.assertEquals("Result", expected, sb.toString()); + }); + } + } +} diff --git a/java/markup/package-info.java b/java/markup/package-info.java new file mode 100644 index 0000000..fdef1bc --- /dev/null +++ b/java/markup/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Markup homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package markup; \ No newline at end of file diff --git a/java/md2html/Md2HtmlTest.java b/java/md2html/Md2HtmlTest.java new file mode 100644 index 0000000..78b186f --- /dev/null +++ b/java/md2html/Md2HtmlTest.java @@ -0,0 +1,71 @@ +package md2html; + +import base.Selector; + +import java.util.function.Consumer; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class Md2HtmlTest { + // === 3637 + private static final Consumer INS = tester -> tester + .test("<<вставка>>", "

                                                                                                                                                                                                                                                                                                                                                  вставка

                                                                                                                                                                                                                                                                                                                                                  ") + .test("Это <<вставка>>, вложенная в текст", "

                                                                                                                                                                                                                                                                                                                                                  Это вставка, вложенная в текст

                                                                                                                                                                                                                                                                                                                                                  ") + .spoiled("Это не <<вставка>>", "

                                                                                                                                                                                                                                                                                                                                                  Это не <<вставка>>

                                                                                                                                                                                                                                                                                                                                                  ", "<", ">") + .spoiled("Это не <<вставка>> 2", "

                                                                                                                                                                                                                                                                                                                                                  Это не <<вставка>> 2

                                                                                                                                                                                                                                                                                                                                                  ", "<", ">") + .addElement("ins", "<<", ">>"); + private static final Consumer DEL = tester -> tester + .test("}}удаление{{", "

                                                                                                                                                                                                                                                                                                                                                  удаление

                                                                                                                                                                                                                                                                                                                                                  ") + .test("Это }}удаление{{, вложенное в текст", "

                                                                                                                                                                                                                                                                                                                                                  Это удаление, вложенное в текст

                                                                                                                                                                                                                                                                                                                                                  ") + .spoiled("Это не }}удаление{{", "

                                                                                                                                                                                                                                                                                                                                                  Это не }}удаление{{

                                                                                                                                                                                                                                                                                                                                                  ", "{") + .spoiled("Это не }}удаление{{ 2", "

                                                                                                                                                                                                                                                                                                                                                  Это не }}удаление{{ 2

                                                                                                                                                                                                                                                                                                                                                  ", "{") + .addElement("del", "}}", "{{"); + + // === 3839 + private static final Consumer PRE = tester -> tester + .test("```код __без__ форматирования```", "

                                                                                                                                                                                                                                                                                                                                                  код __без__ форматирования

                                                                                                                                                                                                                                                                                                                                                  ") + .test( + "Это не `\\``код __без__ форматирования``\\`", + "

                                                                                                                                                                                                                                                                                                                                                  Это не `код без форматирования`

                                                                                                                                                                                                                                                                                                                                                  " + ) + .addElement("pre", "```", (checker, markup, input, output) -> { + final String contentS = checker.generateInput(markup).replace("`", ""); + + input.append("```").append(contentS).append("```"); + output.append("
                                                                                                                                                                                                                                                                                                                                                  ").append(contentS.replace("<", "<").replace(">", "")).append("
                                                                                                                                                                                                                                                                                                                                                  "); + }); + + // === 3435 + private static final Consumer SAMP = tester -> tester + .test("!!пример!!", "

                                                                                                                                                                                                                                                                                                                                                  пример

                                                                                                                                                                                                                                                                                                                                                  ") + .test("Это !!пример!!, вложенный в текст", "

                                                                                                                                                                                                                                                                                                                                                  Это пример, вложенный в текст

                                                                                                                                                                                                                                                                                                                                                  ") + .spoiled("Это не !!пример!!", "

                                                                                                                                                                                                                                                                                                                                                  Это не !!пример!!

                                                                                                                                                                                                                                                                                                                                                  ", "!") + .spoiled("Это не !!пример!! 2", "

                                                                                                                                                                                                                                                                                                                                                  Это не !!пример!! 2

                                                                                                                                                                                                                                                                                                                                                  ", "!") + .addElement("samp", "!!"); + + // === 3233 + private static final Consumer VAR = tester -> tester + .test("%переменная%", "

                                                                                                                                                                                                                                                                                                                                                  переменная

                                                                                                                                                                                                                                                                                                                                                  ") + .test("Это %переменная%, вложенная в текст", "

                                                                                                                                                                                                                                                                                                                                                  Это переменная, вложенная в текст

                                                                                                                                                                                                                                                                                                                                                  ") + .spoiled("Это не %переменная%", "

                                                                                                                                                                                                                                                                                                                                                  Это не %переменная%

                                                                                                                                                                                                                                                                                                                                                  ", "%") + .spoiled("Это не %переменная% 2", "

                                                                                                                                                                                                                                                                                                                                                  Это не %переменная% 2

                                                                                                                                                                                                                                                                                                                                                  ", "%") + .addElement("var", "%"); + + // === Common + + public static final Selector SELECTOR = Selector.composite(Md2HtmlTest.class, c -> new Md2HtmlTester(), Md2HtmlTester::test) + .variant("Base") + .variant("3637", INS, DEL) + .variant("3839", PRE) + .variant("3435", SAMP) + .variant("3233", VAR) + .selector(); + + private Md2HtmlTest() { + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/md2html/Md2HtmlTester.java b/java/md2html/Md2HtmlTester.java new file mode 100644 index 0000000..9538e81 --- /dev/null +++ b/java/md2html/Md2HtmlTester.java @@ -0,0 +1,355 @@ +package md2html; + +import base.*; + +import java.util.*; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class Md2HtmlTester { + private static final Map ESCAPES = Map.of("<", "<", ">", ">"); + + private final Map elements = new HashMap<>(); + private final Map> tags = new LinkedHashMap<>(); + private final List> tests = new ArrayList<>(); + + public Md2HtmlTester() { + addElement("em", "*"); + addElement("em", "_"); + addElement("strong", "**"); + addElement("strong", "__"); + addElement("s", "--"); + addElement("code", "`"); + + test( + "# Заголовок первого уровня\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Заголовок первого уровня

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "## Второго\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Второго

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "### Третьего ## уровня\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Третьего ## уровня

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "#### Четвертого\n# Все еще четвертого\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Четвертого\n# Все еще четвертого

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Этот абзац текста\nсодержит две строки.", + "

                                                                                                                                                                                                                                                                                                                                                  Этот абзац текста\nсодержит две строки.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + " # Может показаться, что это заголовок.\nНо нет, это абзац, начинающийся с `#`.\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  # Может показаться, что это заголовок.\nНо нет, это абзац, начинающийся с #.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "#И это не заголовок.\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  #И это не заголовок.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "###### Заголовки могут быть многострочными\n(и с пропуском заголовков предыдущих уровней)\n\n", + "
                                                                                                                                                                                                                                                                                                                                                  Заголовки могут быть многострочными\n(и с пропуском заголовков предыдущих уровней)
                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Мы все любим *выделять* текст _разными_ способами.\n**Сильное выделение**, используется гораздо реже,\nно __почему бы и нет__?\nНемного --зачеркивания-- еще никому не вредило.\nКод представляется элементом `code`.\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Мы все любим выделять текст разными способами.\nСильное выделение, используется гораздо реже,\nно почему бы и нет?\nНемного зачеркивания еще никому не вредило.\nКод представляется элементом code.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Обратите внимание, как экранируются специальные\nHTML-символы, такие как `<`, `>` и `&`.\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Обратите внимание, как экранируются специальные\nHTML-символы, такие как <, > и &.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Экранирование должно работать во всех местах: <>&.\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Экранирование должно работать во всех местах: <>&.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Знаете ли вы, что в Markdown, одиночные * и _\nне означают выделение?\nОни так же могут быть заэкранированы\nпри помощи обратного слэша: \\*.", + "

                                                                                                                                                                                                                                                                                                                                                  Знаете ли вы, что в Markdown, одиночные * и _\nне означают выделение?\nОни так же могут быть заэкранированы\nпри помощи обратного слэша: *.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "\n\n\nЛишние пустые строки должны игнорироваться.\n\n\n\n", + "

                                                                                                                                                                                                                                                                                                                                                  Лишние пустые строки должны игнорироваться.

                                                                                                                                                                                                                                                                                                                                                  " + ); + test( + "Любите ли вы *вложенные __выделения__* так,\nкак __--люблю--__ их я?", + "

                                                                                                                                                                                                                                                                                                                                                  Любите ли вы вложенные выделения так,\nкак люблю их я?

                                                                                                                                                                                                                                                                                                                                                  " + ); + + test( + """ + # Заголовок первого уровня + + ## Второго + + ### Третьего ## уровня + + #### Четвертого + # Все еще четвертого + + Этот абзац текста + содержит две строки. + + # Может показаться, что это заголовок. + Но нет, это абзац, начинающийся с `#`. + + #И это не заголовок. + + ###### Заголовки могут быть многострочными + (и с пропуском заголовков предыдущих уровней) + + Мы все любим *выделять* текст _разными_ способами. + **Сильное выделение**, используется гораздо реже, + но __почему бы и нет__? + Немного --зачеркивания-- еще никому не вредило. + Код представляется элементом `code`. + + Обратите внимание, как экранируются специальные + HTML-символы, такие как `<`, `>` и `&`. + + Знаете ли вы, что в Markdown, одиночные * и _ + не означают выделение? + Они так же могут быть заэкранированы + при помощи обратного слэша: \\*. + + + + Лишние пустые строки должны игнорироваться. + + Любите ли вы *вложенные __выделения__* так, + как __--люблю--__ их я? + """, + """ +

                                                                                                                                                                                                                                                                                                                                                  Заголовок первого уровня

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Второго

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Третьего ## уровня

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Четвертого + # Все еще четвертого

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Этот абзац текста + содержит две строки.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  # Может показаться, что это заголовок. + Но нет, это абзац, начинающийся с #.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  #И это не заголовок.

                                                                                                                                                                                                                                                                                                                                                  +
                                                                                                                                                                                                                                                                                                                                                  Заголовки могут быть многострочными + (и с пропуском заголовков предыдущих уровней)
                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Мы все любим выделять текст разными способами. + Сильное выделение, используется гораздо реже, + но почему бы и нет? + Немного зачеркивания еще никому не вредило. + Код представляется элементом code.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Обратите внимание, как экранируются специальные + HTML-символы, такие как <, > и &.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Знаете ли вы, что в Markdown, одиночные * и _ + не означают выделение? + Они так же могут быть заэкранированы + при помощи обратного слэша: *.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Лишние пустые строки должны игнорироваться.

                                                                                                                                                                                                                                                                                                                                                  +

                                                                                                                                                                                                                                                                                                                                                  Любите ли вы вложенные выделения так, + как люблю их я?

                                                                                                                                                                                                                                                                                                                                                  + """ + ); + + test("# Без перевода строки в конце", "

                                                                                                                                                                                                                                                                                                                                                  Без перевода строки в конце

                                                                                                                                                                                                                                                                                                                                                  "); + test("# Один перевод строки в конце\n", "

                                                                                                                                                                                                                                                                                                                                                  Один перевод строки в конце

                                                                                                                                                                                                                                                                                                                                                  "); + test("# Два перевода строки в конце\n\n", "

                                                                                                                                                                                                                                                                                                                                                  Два перевода строки в конце

                                                                                                                                                                                                                                                                                                                                                  "); + test( + "Выделение может *начинаться на одной строке,\n а заканчиваться* на другой", + "

                                                                                                                                                                                                                                                                                                                                                  Выделение может начинаться на одной строке,\n а заканчиваться на другой

                                                                                                                                                                                                                                                                                                                                                  " + ); + test("# *Выделение* и `код` в заголовках", "

                                                                                                                                                                                                                                                                                                                                                  Выделение и код в заголовках

                                                                                                                                                                                                                                                                                                                                                  "); + } + + protected void addElement(final String tag, final String markup) { + addElement(tag, markup, markup); + } + + protected void addElement(final String tag, final String begin, final String end) { + addElement(tag, begin, (checker, markup, input, output) -> { + checker.space(input, output); + input.append(begin); + open(output, tag); + + checker.word(input, output); + checker.generate(markup, input, output); + checker.word(input, output); + + input.append(end); + close(output, tag); + checker.space(input, output); + }); + } + + public void addElement(final String tag, final String begin, final Generator generator) { + Asserts.assertTrue("Duplicate element " + begin, elements.put(begin, generator) == null); + tags.computeIfAbsent(tag, k -> new ArrayList<>()).add(begin); + } + + private final Runner runner = Runner.packages("md2html").files("Md2Html"); + + protected Md2HtmlTester test(final String input, final String output) { + tests.add(Pair.of(input, output)); + return this; + } + + protected Md2HtmlTester spoiled(final String input, final String output, final String... spoilers) { + for (final String spoiler : spoilers) { + final Indexer in = new Indexer(input, spoiler); + final Indexer out = new Indexer(output, ESCAPES.getOrDefault(spoiler, spoiler)); + while (in.next() && out.next()) { + tests.add(Pair.of(in.cut(), out.cut())); + tests.add(Pair.of(in.escape(), output)); + } + } + return this; + } + + private static class Indexer { + private final String string; + private final String spoiler; + private int index = - 1; + + public Indexer(final String string, final String spoiler) { + this.string = string; + this.spoiler = spoiler; + } + + public boolean next() { + index = string.indexOf(spoiler, index + 1); + return index >= 0; + } + + public String cut() { + return string.substring(0, index) + string.substring(index + spoiler.length()); + } + + public String escape() { + return string.substring(0, index) + "\\" + string.substring(index); + } + } + + private static void open(final StringBuilder output, final String tag) { + output.append("<").append(tag).append(">"); + } + + private static void close(final StringBuilder output, final String tag) { + output.append(""); + } + + public void test(final TestCounter counter) { + counter.scope("Testing " + String.join(", ", tags.keySet()), () -> new Checker(counter).test()); + } + + public class Checker extends BaseChecker { + public Checker(final TestCounter counter) { + super(counter); + } + + protected void test() { + for (final Pair test : tests) { + test(test); + } + + for (final String markup : elements.keySet()) { + randomTest(3, 10, List.of(markup)); + } + + final int d = TestCounter.DENOMINATOR; + for (int i = 0; i < 10; i++) { + randomTest(100, 1000, randomMarkup()); + } + randomTest(100, 100_000 / d, randomMarkup()); + } + + private void test(final Pair test) { + runner.testEquals(counter, Arrays.asList(test.first().split("\n")), Arrays.asList(test.second().split("\n"))); + } + + private List randomMarkup() { + return Functional.map(tags.values(), random()::randomItem); + } + + private void randomTest(final int paragraphs, final int length, final List markup) { + final StringBuilder input = new StringBuilder(); + final StringBuilder output = new StringBuilder(); + emptyLines(input); + final List markupList = new ArrayList<>(markup); + for (int i = 0; i < paragraphs; i++) { + final StringBuilder inputSB = new StringBuilder(); + paragraph(length, inputSB, output, markupList); + input.append(inputSB); + emptyLines(input); + } + test(Pair.of(input.toString(), output.toString())); + } + + private void paragraph(final int length, final StringBuilder input, final StringBuilder output, final List markup) { + final int h = random().nextInt(0, 6); + final String tag = h == 0 ? "p" : "h" + h; + if (h > 0) { + input.append(new String(new char[h]).replace('\0', '#')).append(" "); + } + + open(output, tag); + while (input.length() < length) { + generate(markup, input, output); + final String middle = random().randomString(ExtendedRandom.ENGLISH); + input.append(middle).append("\n"); + output.append(middle).append("\n"); + } + output.setLength(output.length() - 1); + close(output, tag); + + output.append("\n"); + input.append("\n"); + } + + private void space(final StringBuilder input, final StringBuilder output) { + if (random().nextBoolean()) { + final String space = random().nextBoolean() ? " " : "\n"; + input.append(space); + output.append(space); + } + } + + public void generate(final List markup, final StringBuilder input, final StringBuilder output) { + word(input, output); + if (markup.isEmpty()) { + return; + } + final String type = random().randomItem(markup); + + markup.remove(type); + elements.get(type).generate(this, markup, input, output); + markup.add(type); + } + + protected void word(final StringBuilder input, final StringBuilder output) { + final String word = random().randomString(random().randomItem(ExtendedRandom.ENGLISH, ExtendedRandom.GREEK, ExtendedRandom.RUSSIAN)); + input.append(word); + output.append(word); + } + + private void emptyLines(final StringBuilder sb) { + while (random().nextBoolean()) { + sb.append('\n'); + } + } + + String generateInput(final List markup) { + final StringBuilder sb = new StringBuilder(); + generate(markup, sb, new StringBuilder()); + return sb.toString() + .replace("<", "") + .replace(">", "") + .replace("]", ""); + } + } + + @FunctionalInterface + public interface Generator { + void generate(Checker checker, List markup, StringBuilder input, StringBuilder output); + } +} diff --git a/java/md2html/package-info.java b/java/md2html/package-info.java new file mode 100644 index 0000000..05f3608 --- /dev/null +++ b/java/md2html/package-info.java @@ -0,0 +1,8 @@ +/** + * Tests for Markdown to HTML homework + * of Introduction to Programming course. + * + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package md2html; \ No newline at end of file diff --git a/java/reverse/FastReverseTest.java b/java/reverse/FastReverseTest.java new file mode 100644 index 0000000..1695d36 --- /dev/null +++ b/java/reverse/FastReverseTest.java @@ -0,0 +1,87 @@ +package reverse; + +import base.ExtendedRandom; +import base.Named; +import base.Selector; +import base.TestCounter; +import wordStat.WordStatTest; + +import java.util.Arrays; +import java.util.function.BiFunction; +import java.util.function.LongBinaryOperator; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class FastReverseTest { + // === 3637 + + private static final Named> OCT = Named.of("", + (r, i) -> r.nextBoolean() ? Integer.toString(i) : Integer.toOctalString(i) + (r.nextBoolean() ? "o" : "O") + ); + private static final Named> DEC = Named.of("", (r, i) -> Integer.toString(i)); + + private static final Named PUNCT = Named.of( + "", + IntStream.range(0, Character.MAX_VALUE) + .filter(ch -> ch == ' ' || Character.getType(ch) == Character.START_PUNCTUATION || Character.getType(ch) == Character.END_PUNCTUATION) + .filter(ch -> ch != 13 && ch != 10) + .mapToObj(Character::toString) + .collect(Collectors.joining()) + ); + + public static final Named MIN_C = Named.of("MinC", scan2((a, b) -> b)); + public static final Named MIN = Named.of("Min", scan2(Math::min)); + + private static ReverseTester.Op scan2(final LongBinaryOperator reduce) { + return ints -> { + // This code is intentionally obscure + final int length = Arrays.stream(ints).mapToInt(r -> r.length).max().orElse(0); + final long[] cs = new long[length]; + final long[] cc = new long[length + 1]; + Arrays.fill(cs, Integer.MAX_VALUE); + Arrays.fill(cc, Integer.MAX_VALUE); + //noinspection NestedAssignment + final long[][] rows = range(ints.length).mapToObj(i -> { + range(ints[i].length).forEachOrdered(j -> cc[j] = reduce.applyAsLong( + cc[j + 1], + cs[j] = Math.min(cs[j], ints[i][j]) + )); + return Arrays.copyOf(cc, ints[i].length); + }) + .toArray(long[][]::new); + return range(ints.length).mapToObj(i -> rows[i]).toArray(long[][]::new); + }; + } + + private static IntStream range(final int length) { + return IntStream.iterate(length - 1, i -> i >= 0, i -> i - 1); + } + + + // === Common + + public static final int MAX_SIZE = 1_000_000 / TestCounter.DENOMINATOR / TestCounter.DENOMINATOR; + + public static final Selector SELECTOR = new Selector(FastReverseTest.class) + .variant("Base", ReverseTester.variant(MAX_SIZE, ReverseTest.REVERSE)) + .variant("3637", ReverseTester.variant(MAX_SIZE, "", MIN_C, OCT, DEC, PUNCT)) + .variant("3839", ReverseTester.variant(MAX_SIZE, "", MIN, OCT, DEC, PUNCT)) + .variant("3435", ReverseTester.variant(MAX_SIZE, ReverseTest.ROTATE, PUNCT)) + .variant("3233", ReverseTester.variant(MAX_SIZE, ReverseTest.EVEN, PUNCT)) + .variant("4142", ReverseTester.variant(MAX_SIZE, ReverseTest.AVG, PUNCT)) + .variant("4749", ReverseTester.variant(MAX_SIZE, ReverseTest.SUM, PUNCT)) + + ; + + + private FastReverseTest() { + } + + public static void main(final String... args) { + SELECTOR.main(args); + WordStatTest.main(args); + } +} diff --git a/java/reverse/ReverseTest.java b/java/reverse/ReverseTest.java new file mode 100644 index 0000000..de1bced --- /dev/null +++ b/java/reverse/ReverseTest.java @@ -0,0 +1,165 @@ +package reverse; + +import base.Named; +import base.Selector; +import base.TestCounter; +import reverse.ReverseTester.Op; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.IntToLongFunction; +import java.util.function.LongBinaryOperator; +import java.util.stream.IntStream; + +/** + * Tests for {@code Reverse} homework. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ReverseTest { + // === Base + public static final Named REVERSE = Named.of("", ReverseTester::transform); + + + // === Max + + public static final Named MAX_C = Named.of("MaxC", scan2((a, b) -> b)); + public static final Named MAX = Named.of("Max", scan2(Math::max)); + + private static Op scan2(final LongBinaryOperator reduce) { + return ints -> { + // This code is intentionally obscure + final int length = Arrays.stream(ints).mapToInt(r -> r.length).max().orElse(0); + final long[] cs = new long[length]; + final long[] cc = new long[length + 1]; + Arrays.fill(cs, Integer.MIN_VALUE); + Arrays.fill(cc, Integer.MIN_VALUE); + //noinspection NestedAssignment + final long[][] rows = range(ints.length).mapToObj(i -> { + range(ints[i].length).forEachOrdered(j -> cc[j] = reduce.applyAsLong( + cc[j + 1], + cs[j] = Math.max(cs[j], ints[i][j]) + )); + return Arrays.copyOf(cc, ints[i].length); + }) + .toArray(long[][]::new); + return range(ints.length).mapToObj(i -> rows[i]).toArray(long[][]::new); + }; + } + + private static IntStream range(final int length) { + return IntStream.iterate(length - 1, i -> i >= 0, i -> i - 1); + } + + + // === Rotate + public static final Named ROTATE = Named.of("Rotate", ints -> { + final List rows = new ArrayList<>(List.of(ints)); + return IntStream.range(0, Arrays.stream(ints).mapToInt(r -> r.length).max().orElse(0)) + .mapToObj(c -> { + rows.removeIf(r -> r.length <= c); + return range(rows.size()).mapToObj(rows::get).mapToLong(r -> r[c]).toArray(); + }) + .toArray(long[][]::new); + }); + + + // === Even + public static final Named EVEN = Named.of( + "Even", + ints -> ReverseTester.transform(IntStream.range(0, ints.length) + .mapToObj(i -> IntStream.range(0, ints[i].length) + .filter(j -> (i + j) % 2 == 0) + .map(j -> ints[i][j])) + .map(IntStream::toArray).toArray(int[][]::new)) + ); + + // Sum + @FunctionalInterface + interface LongTernaryOperator { + long applyAsLong(long a, long b, long c); + } + + public static final Named SUM = cross("Sum", 0, Long::sum, (r, c, v) -> r + c - v); + + private static long[][] cross( + final int[][] ints, + final IntToLongFunction map, + final LongBinaryOperator reduce, + final int zero, + final LongTernaryOperator get + ) { + // This code is intentionally obscure + final long[] rt = Arrays.stream(ints) + .map(Arrays::stream) + .mapToLong(row -> row.mapToLong(map).reduce(zero, reduce)) + .toArray(); + final long[] ct = new long[Arrays.stream(ints).mapToInt(r -> r.length).max().orElse(0)]; + Arrays.fill(ct, zero); + Arrays.stream(ints).forEach(r -> IntStream.range(0, r.length) + .forEach(i -> ct[i] = reduce.applyAsLong(ct[i], map.applyAsLong(r[i])))); + return IntStream.range(0, ints.length) + .mapToObj(r -> IntStream.range(0, ints[r].length) + .mapToLong(c -> get.applyAsLong(rt[r], ct[c], ints[r][c])) + .toArray()) + .toArray(long[][]::new); + } + + private static Named cross( + final String name, + final int zero, + final LongBinaryOperator reduce, + final LongTernaryOperator get + ) { + return Named.of(name, ints -> cross(ints, n -> n, reduce, zero, get)); + } + + public static final Named AVG = avg( + "Avg", + ints -> cross(ints, n -> n, Long::sum, 0, (r, c, v) -> r + c - v), + ints -> cross(ints, n -> 1, Long::sum, 0, (r1, c1, v1) -> r1 + c1 - 1) + ); + + private static Named avg( + final String name, + final Op fs, + final Op fc + ) { + return Named.of(name, ints -> avg(ints, fs.apply(ints), fc.apply(ints))); + } + + private static long[][] avg(final int[][] ints, final long[][] as, final long[][] ac) { + return IntStream.range(0, ints.length).mapToObj(i -> IntStream.range(0, ints[i].length) + .mapToLong(j -> as[i][j] / ac[i][j]) + .toArray()) + .toArray(long[][]::new); + } + + + // === Common + + public static final int MAX_SIZE = 10_000 / TestCounter.DENOMINATOR; + + public static final Selector SELECTOR = selector(ReverseTest.class, MAX_SIZE); + + private ReverseTest() { + // Utility class + } + + public static Selector selector(final Class owner, final int maxSize) { + return new Selector(owner) + .variant("Base", ReverseTester.variant(maxSize, REVERSE)) + .variant("3637", ReverseTester.variant(maxSize, MAX_C)) + .variant("3839", ReverseTester.variant(maxSize, MAX)) + .variant("3435", ReverseTester.variant(maxSize, ROTATE)) + .variant("3233", ReverseTester.variant(maxSize, EVEN)) + .variant("4142", ReverseTester.variant(maxSize, AVG)) + .variant("4749", ReverseTester.variant(maxSize, SUM)) + ; + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/reverse/ReverseTester.java b/java/reverse/ReverseTester.java new file mode 100644 index 0000000..134f0fd --- /dev/null +++ b/java/reverse/ReverseTester.java @@ -0,0 +1,299 @@ +package reverse; + +import base.*; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class ReverseTester { + + public static final Named TRANSFORM = Named.of("", ReverseTester::transform); + public static final Named SPACE = Named.of("", " "); + + @FunctionalInterface + public interface Op extends Function {} + + private static final int[] DIVISORS = {100, 10, 1}; + + private final Op transform; + private final BiFunction inputToString; + private final BiFunction outputToString; + private final String name; + private final String spaces; + + private ReverseTester(final String className, final Op transform, final String spaces) { + this(className, transform, spaces, (r, i) -> Integer.toString(i), (r, i) -> Long.toString(i)); + } + + private ReverseTester( + final String className, + final Op transform, + final String spaces, + final BiFunction inputToString, + final BiFunction outputToString + ) { + name = className; + this.transform = transform; + this.spaces = spaces; + this.inputToString = inputToString; + this.outputToString = outputToString; + } + + private static Consumer variant(final int maxSize, final Supplier tester) { + return counter -> tester.get().run(counter, maxSize); + } + + public static Consumer variant(final int maxSize, final Named transform) { + return variant(maxSize, transform, SPACE); + } + + + public static Consumer variant(final int maxSize, final Named transform, final Named spaces) { + Objects.requireNonNull(transform); + Objects.requireNonNull(spaces); + return variant( + maxSize, + () -> new ReverseTester("Reverse" + transform.name() + spaces.name(), transform.value(), spaces.value()) + ); + } + + public static Consumer variant( + final int maxSize, + final String suffix, + final Named> input, + final Named> output + ) { + return variant(maxSize, suffix, TRANSFORM, input, output); + } + + public static Consumer variant( + final int maxSize, + final String suffix, + final Named op, + final Named> input, + final Named> output + ) { + return variant(maxSize, suffix, op, input, output, SPACE); + } + + public static Consumer variant( + final int maxSize, + final String suffix, + final Named op, + final Named> input, + final Named> output, + final Named spaces + ) { + final String out = input.name().contains(output.name()) ? "" : output.name(); + return variant(maxSize, () -> new ReverseTester( + "Reverse" + op.name() + input.name() + out + suffix + spaces.name(), + op.value(), + spaces.value(), + input.value(), + output.value() + )); + } + + private void run(final TestCounter counter, final int maxSize) { + new Checker(counter, maxSize, Runner.packages("", "reverse").std(name), spaces).test(); + } + + @Override + public String toString() { + return name; + } + + public static long[][] transform(final int[][] ints) { + return IntStream.range(1, ints.length + 1) + .mapToObj(i -> ints[ints.length - i]) + .map(is -> IntStream.range(1, is.length + 1).mapToLong(i -> is[is.length - i]).toArray()) + .toArray(long[][]::new); + } + + /** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ + private class Checker extends BaseChecker { + private final int maxSize; + private final Runner runner; + private final String spaces; + private final Set manualTests = new HashSet<>(); + + Checker(final TestCounter counter, final int maxSize, final Runner runner, final String spaces) { + super(counter); + this.maxSize = maxSize; + this.runner = runner; + this.spaces = spaces; + } + + public void manualTest(final int[][] ints) { + for (final List permutation : permutations(new ArrayList<>(Arrays.asList(ints)))) { + final int[][] input = permutation.toArray(int[][]::new); + final String[][] lines = toString(input, inputToString); + if (manualTests.add(Arrays.deepToString(lines))) { + test(lines, toString(transform.apply(input), outputToString)); + } + } + } + + public void test(final int[][] ints) { + test(toString(ints, inputToString), toString(transform.apply(ints), outputToString)); + } + + public void test(final String[][] input, final String[][] output) { + final List inputLines = toLines(input, random().randomString(spaces, 1, 10)); + final List outputLines = toLines(output, " "); + runner.testEquals(counter, inputLines, outputLines); + } + + private String[][] toString(final int[][] ints, final BiFunction toString) { + return Arrays.stream(ints) + .map(row -> Arrays.stream(row).mapToObj(i -> toString.apply(random(), i)).toArray(String[]::new)) + .toArray(String[][]::new); + } + + private String[][] toString(final long[][] ints, final BiFunction toString) { + return Arrays.stream(ints) + .map(row -> Arrays.stream(row).mapToObj(i -> toString.apply(random(), (int) i)).toArray(String[]::new)) + .toArray(String[][]::new); + } + + private List toLines(final String[][] data, final String delimiter) { + if (data.length == 0) { + return Collections.singletonList(""); + } + return Arrays.stream(data) + .map(row -> String.join(delimiter, row)) + .collect(Collectors.toList()); + } + + public int[][] random(final int[] profile) { + final int col = random().nextInt(Arrays.stream(profile).max().orElse(0)); + final int row = random().nextInt(profile.length); + final int m = random().nextInt(5) - 2; + final int[][] ints = Arrays.stream(profile).mapToObj(random().getRandom()::ints).map(IntStream::toArray).toArray(int[][]::new); + Arrays.stream(ints).filter(r -> col < r.length).forEach(r -> r[col] = Math.abs(r[col]) / 2 * m); + ints[row] = Arrays.stream(ints[row]).map(Math::abs).map(v -> v / 2 * m).toArray(); + return ints; + } + + public void test() { + manualTest(new int[][]{ + {1} + }); + manualTest(new int[][]{ + {1, 2}, + {3} + }); + manualTest(new int[][]{ + {1, 2, 3}, + {4, 5}, + {6} + }); + manualTest(new int[][]{ + {1, 2, 3}, + {}, + {4, 5}, + {6} + }); + manualTest(new int[][]{ + {1, 2, 3}, + {-4, -5}, + {6} + }); + manualTest(new int[][]{ + {1, -2, 3}, + {}, + {4, -5}, + {6} + }); + manualTest(new int[][]{ + {1, 2, 0}, + {1, 0}, + {0}, + }); + manualTest(new int[][]{ + {1}, + {1, 3}, + {1, 2, 3}, + }); + manualTest(new int[][]{ + {-1}, + {-1, -2}, + {-1, -2, -3}, + }); + manualTest(new int[][]{ + {}, + }); + manualTest(new int[][]{ + {}, + {}, + {}, + }); + testRandom(tweakProfile(constProfile(10, 10), new int[][]{})); + testRandom(tweakProfile(constProfile(100, 100), new int[][]{})); + testRandom(randomProfile(100, maxSize)); + testRandom(randomProfile(maxSize / 10, maxSize)); + testRandom(randomProfile(maxSize, maxSize)); + for (final int d : DIVISORS) { + final int size = maxSize / d; + testRandom(tweakProfile(constProfile(size / 2, 0), new int[][]{{size / 2, 0}})); + testRandom(tweakProfile(randomProfile(size, size / 2), new int[][]{{size / 2, 0}})); + testRandom(tweakProfile(constProfile(size / 2, 0), new int[][]{{size / 2, size / 2 - 1}})); + testRandom(tweakProfile(constProfile(size / 3, 1), new int[][]{{size / 3, size / 6, size / 3 - 1}})); + } + } + + private int[] randomProfile(final int length, final int values) { + final int[] profile = new int[length]; + for (int i = 0; i < values; i++) { + profile[random().nextInt(0, length - 1)]++; + } + return profile; + } + + private void testRandom(final int[] profile) { + test(random(profile)); + } + + private int[] constProfile(final int length, final int value) { + final int[] profile = new int[length]; + Arrays.fill(profile, value); + return profile; + } + + private int[] tweakProfile(final int[] profile, final int[][] mods) { + for (final int[] mod : mods) { + Arrays.stream(mod).skip(1).forEach(i -> profile[i] = mod[0]); + } + return profile; + } + } + + private static List> permutations(final List elements) { + final List> result = new ArrayList<>(); + permutations(new ArrayList<>(elements), result, elements.size() - 1); + return result; + } + + private static void permutations(final List elements, final List> result, final int n) { + if (n == 0) { + result.add(List.copyOf(elements)); + } else { + for (int i = 0; i < n; i++) { + permutations(elements, result, n - 1); + if (n % 2 == 1) { + Collections.swap(elements, i, n); + } else { + Collections.swap(elements, 0, n); + } + } + permutations(elements, result, n - 1); + } + } +} diff --git a/java/reverse/package-info.java b/java/reverse/package-info.java new file mode 100644 index 0000000..86e0e5d --- /dev/null +++ b/java/reverse/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Reverse homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package reverse; \ No newline at end of file diff --git a/java/sum/SumTest.java b/java/sum/SumTest.java new file mode 100644 index 0000000..bdd37b3 --- /dev/null +++ b/java/sum/SumTest.java @@ -0,0 +1,178 @@ +package sum; + +import base.*; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Locale; +import java.util.function.*; +import java.util.stream.Collectors; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class SumTest { + // === Base + + @FunctionalInterface + /* package-private */ interface Op extends UnaryOperator> {} + + private static final BiConsumer TO_STRING = (expected, out) -> Asserts.assertEquals("Sum", expected.toString(), out); + + private static final Named>> BASE = Named.of("", () -> new SumTester<>( + Integer::sum, n -> (int) n, (r, max) -> r.nextInt() % max, TO_STRING, + 10, 100, Integer.MAX_VALUE + )); + + /* package-private */ static Named> plain() { + return Named.of("", test -> test); + } + + + // === DoubleHex + + private static BiConsumer approximate(final Function parser, final double precision) { + return (expected, out) -> + Asserts.assertEquals("Sum", expected.doubleValue(), parser.apply(out).doubleValue(), precision); + } + + private static final Named>> DOUBLE = Named.of("Double", () -> new SumTester<>( + Double::sum, n -> (double) n, (r, max) -> (r.getRandom().nextDouble() - 0.5) * 2 * max, + approximate(Double::parseDouble, 1e-10), + 10.0, 0.01, 1e20, 1e100, Double.MAX_VALUE / 10000) + .test(5, "2.5 2.5") + .test(0, "1e100 -1e100") + .testT(2e100, "1.5e100 0.5e100")); + + private static Named> hexFull(final Function toHex) { + final Function toHexSpoiled = toHex.andThen(s ->s.chars() + .map(ch -> ((ch ^ s.hashCode()) & 1) == 0 ? Character.toLowerCase(ch) : Character.toUpperCase(ch)) + .mapToObj(Character::toString) + .collect(Collectors.joining())); + return Named.of("Hex", test -> test + .test(toHex, 1) + .test(toHex, 0x1a) + .test(toHexSpoiled, 0xA2) + .test(toHexSpoiled, 0X0, 0X1, 0XF, 0XF, 0x0, 0x1, 0xF, 0xf) + .test(toHexSpoiled, 0x12345678) + .test(toHexSpoiled, 0x09abcdef) + .test(toHexSpoiled, 0x3CafeBab) + .test(toHexSpoiled, 0x3DeadBee) + + .test(toHex, Integer.MAX_VALUE) + .test(toHex, Integer.MIN_VALUE) + .setToString(number -> { + final int hashCode = number.hashCode(); + if ((hashCode & 1) == 0) { + return number.toString(); + } + + return toHexSpoiled.apply(number); + }) + ); + } + + // === Octal + private static Named> octal(final Function toOctal) { + //noinspection OctalInteger,StringConcatenationMissingWhitespace + return Named.of("Octal", test -> test + .test(1, "1o") + .test(017, "17o") + .testSpaces(6, " 1o 2o 3O ") + .test(01234567, "1234567O") + + .test(Integer.MIN_VALUE, "-0" + String.valueOf(Integer.MIN_VALUE).substring(1)) + .test(Integer.MAX_VALUE, "0" + Integer.MAX_VALUE) + .test(Integer.MAX_VALUE, Integer.toOctalString(Integer.MAX_VALUE) + "o") + .test(Integer.MAX_VALUE, "0" + Integer.toOctalString(Integer.MAX_VALUE) + "O") + .setToString(number -> { + final int hashCode = number.hashCode(); + if ((hashCode & 1) == 0) { + return number.toString(); + } + + final String lower = toOctal.apply(number).toLowerCase(Locale.ROOT) + "o"; + return (hashCode & 2) == 0 ? lower : lower.toUpperCase(Locale.ROOT); + }) + ); + } + + // === Long + + private static final Named>> LONG = Named.of("Long", () -> new SumTester<>( + Long::sum, n -> n, (r, max) -> r.getRandom().nextLong() % max, TO_STRING, + 10L, 100L, (long) Integer.MAX_VALUE, Long.MAX_VALUE) + .test(12345678901234567L, " +12345678901234567 ") + .test(0L, " +12345678901234567 -12345678901234567") + .test(0L, " +12345678901234567 -12345678901234567")); + + // === BigInteger + + private static final Named>> BIG_INTEGER = Named.of("BigInteger", () -> new SumTester<>( + BigInteger::add, BigInteger::valueOf, (r, max) -> new BigInteger(max.bitLength(), r.getRandom()), TO_STRING, + BigInteger.TEN, BigInteger.TEN.pow(10), BigInteger.TEN.pow(100), BigInteger.TWO.pow(1000)) + .test(0, "10000000000000000000000000000000000000000 -10000000000000000000000000000000000000000")); + + + // === BigDecimalHex + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + private static final Named>> BIG_DECIMAL = Named.of("BigDecimal", () -> new SumTester<>( + BigDecimal::add, BigDecimal::valueOf, + (r, max) -> { + final BigInteger unscaled = new BigInteger((max.precision() - max.scale() + 2) * 3, r.getRandom()); + return new BigDecimal(unscaled, 3); + }, + TO_STRING, + BigDecimal.TEN, BigDecimal.TEN.pow(10), BigDecimal.TEN.pow(100), BigDecimal.ONE.add(BigDecimal.ONE).pow(1000)) + .testT(BigDecimal.ZERO.setScale(3), "10000000000000000000000000000000000000000.123 -10000000000000000000000000000000000000000.123")); + + private static String bigDecimalToString(final BigDecimal number) { + final int scale = number.scale(); + return "0x" + number.unscaledValue().toString(16) + (scale == 0 ? "" : "s" + Integer.toString(scale, 16)); + } + + + // === Hex + + private static Named> hex(final Function toHex) { + return hexFull(v -> "0x" + toHex.apply(v)); + } + + + // === Common + + /* package-private */ static Consumer variant( + final Named> runner, + final Named>> test, + final Named, ? extends SumTester>> modifier + ) { + return counter -> modifier.value().apply(test.value().get()) + .test("Sum" + test.name() + modifier.name() + runner.name(), counter, runner.value()); + } + + /* package-private */ static final Named> RUNNER = + Named.of("", Runner.packages("", "sum")::args); + + public static final Selector SELECTOR = selector(SumTest.class, RUNNER); + + private SumTest() { + // Utility class + } + + public static Selector selector(final Class owner, final Named> runner) { + return new Selector(owner) + .variant("Base", variant(runner, BASE, plain())) + .variant("3637", variant(runner, DOUBLE, hexFull(value -> value == value.intValue() && value > 0 ? "0x" + Integer.toHexString(value.intValue()) : Double.toHexString(value)))) + .variant("3839", variant(runner, BIG_DECIMAL, hexFull(SumTest::bigDecimalToString))) + .variant("3435", variant(runner, BASE, hex(Integer::toHexString))) + .variant("3233", variant(runner, DOUBLE, plain())) + .variant("4749", variant(runner, LONG, octal(Long::toOctalString))) + .variant("4142", variant(runner, BIG_INTEGER, octal(number -> number.toString(8)))) + ; + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/sum/SumTester.java b/java/sum/SumTester.java new file mode 100644 index 0000000..404e4b3 --- /dev/null +++ b/java/sum/SumTester.java @@ -0,0 +1,189 @@ +package sum; + +import base.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public class SumTester { + private static final List SPACES = List.of( + " \t\n\u000B\u2029\f", + IntStream.rangeClosed(0, Character.MAX_VALUE) + .filter(Character::isWhitespace) + .mapToObj(Character::toString) + .collect(Collectors.joining()) + ); + + private final BinaryOperator add; + private final LongFunction fromLong; + private BiFunction toString; + private final BiFunction randomValue; + private final BiConsumer verifier; + private List spaces; + private final List limits; + + private final List> tests = new ArrayList<>(); + + @SafeVarargs + public SumTester( + final BinaryOperator add, + final LongFunction fromLong, + final BiFunction randomValue, + final BiConsumer verifier, + final T... limits + ) { + this.add = add; + this.fromLong = fromLong; + this.randomValue = randomValue; + this.verifier = verifier; + this.limits = List.of(limits); + + setSpaces(SPACES); + setToString(String::valueOf); + + test(1, "1"); + test(6, "1", "2", "3"); + test(1, " 1"); + test(1, "1 "); + test(1, " 1 "); + test(12345, " 12345 "); + test(60, "010", "020", "030"); + testSpaces(1368, " 123 456 789 "); + test(-1, "-1"); + test(-6, "-1", "-2", "-3"); + test(-12345, " -12345 "); + testSpaces(-1368, " -123 -456 -789 "); + test(1, "+1"); + test(6, "+1", "+2", "+3"); + test(12345, " +12345 "); + testSpaces(1368, " +123 +456 +789 "); + test(0); + testSpaces(0, " ", " "); + } + + protected SumTester setSpaces(final List spaces) { + this.spaces = spaces; + return this; + } + + protected SumTester addSpaces(final String... spaces) { + this.spaces = Stream.concat(this.spaces.stream(), Arrays.stream(spaces)).toList(); + return this; + } + + public SumTester setToString(final Function toString) { + return setToString((r, n) -> toString.apply(n)); + } + + public SumTester setToString(final BiFunction toString) { + this.toString = toString; + return this; + } + + protected SumTester test(final Function toString, final long... input) { + return testT( + fromLong.apply(LongStream.of(input).sum()), + LongStream.of(input).mapToObj(fromLong).map(toString).collect(Collectors.joining(" ")) + ); + } + + protected SumTester test(final long result, final String... input) { + return testT(fromLong.apply(result), input); + } + + protected SumTester testT(final T result, final String... input) { + return testT(result, Arrays.asList(input)); + } + + private SumTester testT(final T result, final List input) { + tests.add(checker -> checker.test(result, input)); + return this; + } + + public SumTester testSpaces(final long result, final String... input) { + final T res = fromLong.apply(result); + tests.add(checker -> spaces.stream() + .flatMapToInt(String::chars) + .forEach(space -> checker.test( + res, + Functional.map(Arrays.asList(input), s -> s.replace(' ', (char) space)) + )) + ); + return this; + } + + public void test(final String name, final TestCounter counter, final Function runner) { + new Checker(counter, runner.apply(name)).test(); + } + + private class Checker extends BaseChecker { + private final Runner runner; + + public Checker(final TestCounter counter, final Runner runner) { + super(counter); + this.runner = runner; + } + + public void test() { + tests.forEach(test -> test.accept(this)); + + for (final T limit : limits) { + for (int n = 10; n <= 10_000 / TestCounter.DENOMINATOR; n *= 10) { + randomTest(n, limit); + } + } + } + + private void test(final T result, final List input) { + counter.test(() -> { + final List out = runner.run(counter, input); + Asserts.assertEquals("Single line expected", 1, out.size()); + verifier.accept(result, out.get(0)); + }); + } + + private void randomTest(final int numbers, final T max) { + for (final String spaces : spaces) { + randomTest(numbers, max, spaces); + } + } + + private void randomTest(final int numbers, final T max, final String spaces) { + final List values = new ArrayList<>(); + for (int i = 0; i < numbers; i++) { + values.add(randomValue.apply(random(), max)); + } + testRandom(values.stream().reduce(fromLong.apply(0), add), values, spaces); + } + + private void testRandom(final T result, final List args, final String spaces) { + final List spaced = args.stream() + .map(n -> toString.apply(random(), n)) + .map(value -> randomSpace(spaces) + value + randomSpace(spaces)) + .toList(); + final List argsList = new ArrayList<>(); + for (final Iterator i = spaced.listIterator(); i.hasNext(); ) { + final StringBuilder next = new StringBuilder(i.next()); + while (i.hasNext() && random().nextBoolean()) { + next.append(randomSpace(spaces)).append(i.next()); + } + argsList.add(next.toString()); + } + test(result, argsList); + } + + private String randomSpace(final String spaces) { + return random().randomString(spaces); + } + } +} diff --git a/java/sum/package-info.java b/java/sum/package-info.java new file mode 100644 index 0000000..04ce7fb --- /dev/null +++ b/java/sum/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Sum homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package sum; \ No newline at end of file diff --git a/java/wordStat/WordStatChecker.java b/java/wordStat/WordStatChecker.java new file mode 100644 index 0000000..cc19c83 --- /dev/null +++ b/java/wordStat/WordStatChecker.java @@ -0,0 +1,127 @@ +package wordStat; + +import base.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class WordStatChecker extends BaseChecker { + public static final String DASH = "-֊־‒–—―⸗⸚⸺〰゠︱︲﹘﹣-'"; + public static final String SIMPLE_DELIMITERS = " \t"; + public static final String ADVANCED_DELIMITERS = " \t!\"#%&()*+,./:;<=>?@[\\]^`{|}~ ¡¦§¨©«¬\u00AD®¯°±²³´¶·¸¹»¼½¾¿×÷˂˃˄˅˒˓˔˕˖˗˘˙˚˛˜˝"; + public static final String ALL = ExtendedRandom.RUSSIAN + ExtendedRandom.ENGLISH + ExtendedRandom.GREEK + DASH; + private static final Pattern PATTERN = Pattern.compile("[^\\p{IsLetter}'\\p{Pd}]+"); + public static final Runner.Packages RUNNER = Runner.packages("", "wordstat", "wspp"); + + private final Function>> processor; + + private final MainChecker main; + + private WordStatChecker( + final String className, + final Function>> processor, + final TestCounter counter + ) { + super(counter); + main = new MainChecker(RUNNER.files(className)); + this.processor = processor; + } + + public static void test( + final TestCounter counter, + final String className, + final Function>> processor, + final Consumer tests + ) { + tests.accept(new WordStatChecker(className, processor, counter)); + } + + public void test(final String... lines) { + test(PATTERN, lines); + } + + public void test(final Pattern pattern, final String... lines) { + final String[][] data = Arrays.stream(lines) + .map(line -> Arrays.stream(pattern.split(line)).filter(Predicate.not(String::isEmpty)).toArray(String[]::new)) + .toArray(String[][]::new); + test(lines, processor.apply(data)); + } + + private void randomTest( + final int wordLength, + final int totalWords, + final int wordsPerLine, + final int lines, + final String chars, + final String delimiters, + final Function>> processor + ) { + final String[] words = generateWords(wordLength, totalWords, chars); + final String[][] text = generateTest(lines, words, wordsPerLine); + test(input(text, delimiters), processor.apply(text)); + } + + public void randomTest( + final int wordLength, + final int totalWords, + final int wordsPerLine, + final int lines, + final String chars, + final String delimiters + ) { + randomTest(wordLength, totalWords, wordsPerLine, lines, chars, delimiters, processor::apply); + } + + private void test(final String[] text, final List> expected) { + final List expectedList = expected.stream() + .map(p -> p.first() + " " + p.second()) + .collect(Collectors.toList()); + main.testEquals(counter, Arrays.asList(text), expectedList); + } + + public void test(final String[][] text, final String delimiters, final List> answer) { + test(input(text, delimiters), answer); + } + + private String[] generateWords(final int wordLength, final int totalWords, final String chars) { + final String allChars = chars.chars().anyMatch(Character::isUpperCase) + ? chars : chars + chars.toUpperCase(Locale.ROOT); + return IntStream.range(0, totalWords) + .mapToObj(i -> random().randomString(allChars, wordLength / 2, wordLength)) + .toArray(String[]::new); + } + + private String[][] generateTest(final int lines, final String[] words, final int wordsPerLine) { + final String[][] text = new String[lines][]; + for (int i = 0; i < text.length; i++) { + text[i] = new String[random().nextInt(wordsPerLine / 2, wordsPerLine)]; + for (int j = 0; j < text[i].length; j++) { + text[i][j] = random().randomItem(words); + } + } + return text; + } + + private String[] input(final String[][] text, final String delimiters) { + final String[] input = new String[text.length]; + for (int i = 0; i < text.length; i++) { + final String[] line = text[i]; + final StringBuilder sb = new StringBuilder(random().randomString(delimiters)); + for (final String word : line) { + sb.append(word).append(random().randomString(delimiters)); + } + input[i] = sb.toString(); + } + return input; + } +} diff --git a/java/wordStat/WordStatTest.java b/java/wordStat/WordStatTest.java new file mode 100644 index 0000000..151991e --- /dev/null +++ b/java/wordStat/WordStatTest.java @@ -0,0 +1,70 @@ +package wordStat; + +import base.Named; +import base.Selector; + +import java.util.Comparator; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Tests for Word Statistics homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class WordStatTest { + // === Base + private static final Named>> ID = Named.of("", Stream::of); + private static final WordStatTester.Variant BASE = new WordStatTester.Variant("", false, Comparator.comparingInt(p -> 0)); + + + // === 3637 + public static final int SIZE = 3; + private static final WordStatTester.Variant LENGTH = new WordStatTester.Variant("Length", false, Comparator.comparingInt(p -> p.first().length())); + private static final Named>> MIDDLE = + size("Middle", SIZE * 2 + 1, s -> Stream.of(s.substring(SIZE, s.length() - SIZE))); + + static Named>> size( + final String name, + final int length, + final Function> f + ) { + return Named.of(name, s -> s.length() >= length ? f.apply(s) : Stream.empty()); + } + + // === 3839 + private static final Named>> AFFIX = size( + "Affix", + 2, + s -> Stream.of(s.substring(0, s.length() / 2), s.substring(s.length() - s.length() / 2)) + ); + + // === 3536 + private static final Named>> SUFFIX = + size("Suffix", 2, s -> Stream.of(s.substring(s.length() - s.length() / 2))); + + // === 4749 + private static final Named>> PREFIX = + size("Prefix", 2, s -> Stream.of(s.substring(0, s.length() / 2))); + + // === Common + public static final Selector SELECTOR = new Selector(WordStatTester.class) + .variant("Base", BASE.with(ID)) + .variant("3637", LENGTH.with(MIDDLE)) + .variant("3839", LENGTH.with(AFFIX)) + .variant("3435", LENGTH.with(SUFFIX)) + .variant("3233", LENGTH.with(ID)) + .variant("4142", LENGTH.with(MIDDLE)) + .variant("4749", LENGTH.with(PREFIX)) + + ; + + private WordStatTest() { + // Utility class + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/wordStat/WordStatTester.java b/java/wordStat/WordStatTester.java new file mode 100644 index 0000000..5534c14 --- /dev/null +++ b/java/wordStat/WordStatTester.java @@ -0,0 +1,100 @@ +package wordStat; + +import base.ExtendedRandom; +import base.Named; +import base.Pair; +import base.TestCounter; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class WordStatTester { + public static final String PRE_LOWER = chars() + .filter(s -> s.toLowerCase(Locale.ROOT).length() == 1) + .collect(Collectors.joining()); + public static final String POST_LOWER = chars() + .collect(Collectors.joining()) + .toLowerCase(); + + private WordStatTester() { + } + + private static Stream chars() { + return IntStream.range(' ', Character.MAX_VALUE) + .filter(ch -> !Character.isSurrogate((char) ch)) + .filter(ch -> Character.getType(ch) != Character.NON_SPACING_MARK) + .filter(ch -> Character.getType(ch) != Character.DIRECTIONALITY_NONSPACING_MARK) + .mapToObj(Character::toString); + } + + /* package-private */ record Variant(String name, boolean reverse, Comparator> c) { + public Consumer with(final Named>> split) { + return counter -> WordStatChecker.test( + counter, + "WordStat" + name + split.name(), + text -> answer(split.value(), text), + checker -> { + checker.test("To be, or not to be, that is the question:"); + checker.test("Monday's child is fair of face.", "Tuesday's child is full of grace."); + checker.test("Шалтай-Болтай", "Сидел на стене.", "Шалтай-Болтай", "Свалился во сне."); + checker.test( + "27 октября — 300-й день григорианскому календарю. До конца года остаётся 65 дней.", + "До 15 октября 1582 года — 27 октября по юлианскому календарю, с 15 октября 1582 года — 27 октября по григорианскому календарю.", + "В XX и XXI веках соответствует 14 октября по юлианскому календарю[1].", + "(c) Wikipedia" + ); + checker.test("23 октября — Всемирный день психического здоровья", "Тема 2025 года: Психическое здоровье на рабочем месте"); + + checker.randomTest(3, 10, 10, 3, ExtendedRandom.ENGLISH, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(10, 3, 5, 5, ExtendedRandom.RUSSIAN, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(4, 10, 10, 3, ExtendedRandom.GREEK, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(4, 10, 10, 3, WordStatChecker.DASH, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(3, 10, 10, 3, ExtendedRandom.ENGLISH, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10, 3, 5, 5, ExtendedRandom.RUSSIAN, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(3, 10, 10, 3, ExtendedRandom.GREEK, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(3, 10, 10, 3, WordStatChecker.DASH, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(3, 10, 10, 10, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + + final int d = TestCounter.DENOMINATOR; + final int d2 = TestCounter.DENOMINATOR; + checker.randomTest(10, 10000 / d, 10, 10, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10, 1, 10, 10, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10, 1000 / d, 100 / d2, 100 / d2, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(4, 1000 / d, 10, 3000 / d, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(4, 1000 / d, 3000 / d, 10, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10000 / d, 20, 10, 5, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(1000000 / d, 2, 2, 1, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + + checker.test(PRE_LOWER); + checker.test(POST_LOWER); + } + ); + } + + private List> answer(final Function> split, final String[][] text) { + final List parts = Arrays.stream(text) + .flatMap(Arrays::stream) + .filter(Predicate.not(String::isEmpty)) + .flatMap(split) + .peek(s -> {assert !s.isBlank();}) + .collect(Collectors.toList()); + if (reverse()) { + Collections.reverse(parts); + } + return parts.stream() + .collect(Collectors.toMap(String::toLowerCase, v -> 1, Integer::sum, LinkedHashMap::new)) + .entrySet().stream() + .map(Pair::of) + .sorted(c) + .toList(); + } + } +} diff --git a/java/wordStat/package-info.java b/java/wordStat/package-info.java new file mode 100644 index 0000000..b54b5f6 --- /dev/null +++ b/java/wordStat/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Word Statistics homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package wordStat; \ No newline at end of file diff --git a/java/wspp/WsppTest.java b/java/wspp/WsppTest.java new file mode 100644 index 0000000..16cdebc --- /dev/null +++ b/java/wspp/WsppTest.java @@ -0,0 +1,51 @@ +package wspp; + +import base.Named; +import base.Selector; + +import java.util.Comparator; +import java.util.Map; +import java.util.function.IntFunction; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class WsppTest { + // === Base + private static final Named>> INPUT = Named.of("", Comparator.comparingInt(e -> 0)); + private static final Named> ALL = Named.of("", size -> IntStream.range(0, size)); + private static final Named> WSPP = Named.of("", (r, l, L, g, G) -> g); + private static final Named NONE = Named.of("", ""); + + // === 3637 + private static final Named>> LENGTH = Named.of("", + Map.Entry.comparingByKey(Comparator.comparingInt(String::length))); + private static final Named> LAST = Named.of("Last", size -> IntStream.of(size - 1)); + private static final Named JAVA = Named.of("", "XHB7TmR9JF8="); + + // === 3839 + private static final Named> MIDDLE = Named.of("Middle", size -> IntStream.of(size / 2)); + + // === 3435 + public static final WsppTester.Extractor POSITION = (r, l, L, g, G) -> r + ":" + (G - g + 1); + + + // === Common + public static final Selector SELECTOR = new Selector(WsppTester.class) + .variant("Base", WsppTester.variant(INPUT, ALL, WSPP, NONE)) + .variant("3637", WsppTester.variant(LENGTH, LAST, WSPP, JAVA)) + .variant("3839", WsppTester.variant(LENGTH, MIDDLE, WSPP, JAVA)) + .variant("3435", WsppTester.variant(LENGTH, ALL, Named.of("Position", POSITION), JAVA)) + .variant("3233", WsppTester.variant(INPUT, ALL, Named.of("Pos", POSITION), JAVA)) + .variant("4142", WsppTester.variant(LENGTH, LAST, WSPP, JAVA)) + .variant("4749", WsppTester.variant(LENGTH, ALL, Named.of("Position", POSITION), JAVA)) + ; + + private WsppTest() { + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/wspp/WsppTester.java b/java/wspp/WsppTester.java new file mode 100644 index 0000000..ba571a5 --- /dev/null +++ b/java/wspp/WsppTester.java @@ -0,0 +1,139 @@ +package wspp; + +import base.ExtendedRandom; +import base.Named; +import base.Pair; +import base.TestCounter; +import wordStat.WordStatChecker; +import wordStat.WordStatTester; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class WsppTester { + private WsppTester() { + } + + public static Consumer variant( + final Named>> comparator, + final Named> selector, + final Named> extractor, + final Named extra + ) { + // Stream "magic" code. You do not expect to understand it + return counter -> WordStatChecker.test( + counter, + "Wspp" + comparator.name() + selector.name() + extractor.name() + extra.name(), + text -> { + final Map totals = Arrays.stream(text) + .flatMap(Arrays::stream) + .map(word -> word.toLowerCase(Locale.ROOT)) + .collect(Collectors.toMap(Function.identity(), k -> 1, Integer::sum, LinkedHashMap::new)); + final int[] lengths = Arrays.stream(text).mapToInt(a -> a.length).toArray(); + final int[] sizes = new int[lengths.length + 1]; + int start = 0; + for (int i = 0; i < lengths.length; i++) { + sizes[i] = start; + start += lengths[i]; + } + sizes[lengths.length] = start; + + final Map selected = IntStream.range(0, text.length).boxed() + .flatMap(r -> { + final String[] line = text[r]; + return IntStream.range(0, line.length).boxed() + .collect(Collectors.groupingBy( + w -> line[w].toLowerCase(Locale.ROOT), + Collectors.collectingAndThen( + Collectors.mapping( + w -> extractor.value().select( + r + 1, + w + 1, + line.length, + sizes[r] + w + 1, + sizes[lengths.length] + ), + Collectors.toUnmodifiableList() + ), + list -> selector.value() + .apply(list.size()) + .mapToObj(list::get) + .toList() + ) + )) + .entrySet().stream(); + } + ) + .collect(Collectors.groupingBy( + Map.Entry::getKey, + Collectors.flatMapping( + e -> e.getValue().stream(), + Collectors.mapping( + String::valueOf, + Collectors.mapping(" "::concat, Collectors.joining()) + ) + ) + )); + return totals.entrySet().stream() + .sorted(comparator.value()) + .map(e -> Pair.of(e.getKey(), e.getValue() + selected.get(e.getKey()))) + .collect(Collectors.toList()); + }, + checker -> { + final Pattern pattern = Pattern.compile(new String(Base64.getDecoder().decode("W15ccHtJc0xldHRlcn0nXHB7UGR9" + extra.value()), StandardCharsets.US_ASCII) + "]+"); + final String good = String.join("", pattern.split(WordStatTester.POST_LOWER)); + + checker.test(pattern, "To be, or not to be, that is the question:"); + checker.test( + pattern, + "Monday's child is fair of face.", + "Tuesday's child is full of grace." + ); + checker.test( + pattern, + "Шалтай-Болтай", + "Сидел на стене.", + "Шалтай-Болтай", + "Свалился во сне." + ); + + checker.randomTest(3, 10, 10, 3, ExtendedRandom.ENGLISH, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(10, 3, 5, 5, ExtendedRandom.RUSSIAN, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(3, 10, 10, 3, ExtendedRandom.GREEK, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(3, 10, 10, 3, WordStatChecker.DASH, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(3, 10, 10, 3, ExtendedRandom.ENGLISH, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10, 3, 5, 5, ExtendedRandom.RUSSIAN, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(3, 10, 10, 3, ExtendedRandom.GREEK, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(3, 10, 10, 3, WordStatChecker.DASH, WordStatChecker.ADVANCED_DELIMITERS); + + checker.randomTest(10, 20, 10, 3, good, WordStatChecker.SIMPLE_DELIMITERS); + checker.randomTest(10, 20, 10, 3, good, WordStatChecker.ADVANCED_DELIMITERS); + + final int d = TestCounter.DENOMINATOR; + final int d2 = TestCounter.DENOMINATOR2; + checker.randomTest(100, 1000 / d, 1000 / d2, 1000 / d2, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(10, 1000 / d, 1000 / d2, 1000 / d2, good, WordStatChecker.ADVANCED_DELIMITERS); + + checker.randomTest(10000 / d, 20, 10, 5, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + checker.randomTest(1000000 / d, 2, 2, 1, WordStatChecker.ALL, WordStatChecker.ADVANCED_DELIMITERS); + + checker.test(pattern, WordStatTester.PRE_LOWER); + checker.test(pattern, WordStatTester.POST_LOWER); + } + ); + } + + @FunctionalInterface + public interface Extractor { + T select(int l, int li, int lt, int gi, int gt); + } +} diff --git a/java/wspp/package-info.java b/java/wspp/package-info.java new file mode 100644 index 0000000..117ede7 --- /dev/null +++ b/java/wspp/package-info.java @@ -0,0 +1,7 @@ +/** + * Tests for Word Statistics++ homework + * of Introduction to Programming course. + * + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +package wspp; \ No newline at end of file diff --git a/test-rules.md b/test-rules.md new file mode 100644 index 0000000..6e0fdf5 --- /dev/null +++ b/test-rules.md @@ -0,0 +1,113 @@ +--- +gitea: none +include_toc: true +--- + +# Зачёт по дисциплине «Введение в программирование» + +## Расписание + +Досрочная сдача зачёта: + + * M3132-35: 30 декабря с 11:30, ауд. 2137 + * M3136-39: 30 декабря с 11:30, ауд. 2137 + +Сдача зачёта в сессию: + +* M3132-35: 10 января с 11:30, ауд. 1229 +* M3136-39: 20 января с 11:30, ауд. 2137 + + +Пересдача зачёта пройдёт: + +* M3132-39: TBA +* M3136-39: TBA + + +## Сдача зачёта + +Если вас устраивают ваши баллы, то зачёт можно не сдавать. +Вы можете сдавать зачёт либо досрочно, либо во время сессии, по вашему выбору. + +Для того, чтобы записаться на сдачу зачёта надо заполнить +[форму](https://docs.google.com/forms/d/e/1FAIpQLScjft8hZCjlfoeVicSJHnX_uMW7xpA5RxSMQwqhy6aXGZWCGw/viewform). +Для заполнения вам потребуются логин и пароль от репозитория. +Если не указано обратного, то на сдачу надо записаться до 9:00 дня сдачи. +Если вы не записались на сдачу вовремя, то вы можете прийти на зачёт, +но вы будете обработаны в ручном режиме после решения проблем всех записавшихся вовремя. +Время на обработку компенсировано не будет. + +Если у вас больше 59 баллов, то их округлят до 60 если вы заполните форму +и пообещаете не сдавать зачёт (это требуется отметить в форме). + + +## Формат заданий + +Вам будет выдано задание, связанное с пройденными темами. +В условии описано, что вам надо сделать и набор требований, +которым должно удовлетворять решение (примерно как в домашних заданиях). + +Некоторые задания рассчитаны на то, что вы адаптируте или скомпануете +код одного или нескольких ДЗ. +Если вы не сдали соответствующие ДЗ, то вы можете написать весь код с нуля, +но это будет сложнее. + +Если вам что-то не ясно в задании (например, оно кажется подозрительно простым), +то вы можете задать вопрос по условию. +Принимаются только вопросы о том, что надо сделать. +Как именно делать остаётся на ваше усмотрение. + +По решению должно быть очевидно как его запускать. +Например, назвать основной класс `Main` или по имени задания — +хорошая идея, а `FooBazFactoryProvider` — нет. + +Если вы хотете передать проверяющему какую-то дополнительную информацию +(например, о выбранном компромиссе между памятью и временем исполнения), +то напишите её либо в комментариях к коду, либо положите рядом с кодом +`README.md`. + +В решении вы можете использовать ваш код из ДЗ и код, +написанный преподавателями на лекциях. + + +## Порядок сдачи + + 1. Заранее проверьте, что у вас работает всё, необходимое для сдачи. + Претензии вида «у меня не работал компилятор/IDE/git/браузер/интернет» не принимаются. + 1. За 10 минут до начала зачёта сбор студентов в соответствующей аудитории. + Позаботьтесь, что бы вам хватило заряда ноутбука или принесите с собой удлинитель-тройник. + 1. Организационные вопросы. + В том числе, можно отказаться от участия, если записались по ошибке, + или вам больше не актуально. + 1. В момент *T* объявляется начало и выдаются билеты с заданиями. + 1. Ответы на вопросы по билетам до *T*+30 минут. + 1. Вы пишите решение и записываете его в каталог `java-solutions` зачётного репозитория + `https://www.kgeorgiy.info/git-students/year2025//prog-intro-exam`. + 1. В момент времени *T*+3 часа фиксируется состояние репозиториев. + 1. Проверяется код на состояние *T*+3 часа. Это может занять несколько дней. + 1. Результаты проверки отображаются в табличке, + комментарии по проверке загружаются в репозитории. + + +## Система оценки + +В репозитории есть код, решающий поставленную задачу (возможно не всю): +`20` баллов минус баллы за проблемы: + + * `#` — большая проблема (обычно не выполнено одно из требований задания): `−5` баллов + * `*` — средняя проблема (обычно нарушение неоднократно обсуждавшихся рекомендаций, + например, утечка ресурсов, если это не является основной целью задания): `−2` балла + * `-` — маленькая проблема (например, однократное нарушение правил оформления кода): `−1` балл + +Код (почти) отсутствует/не имеет отношения к заданию, преподаватель не нашёл ваш код: `−5` баллов + +Код не компилируется/синтаксически некорректен: `−10` баллов, дальнейшая проверка не осуществляется. + +Код списан: `−20` баллов + + +## Полезные ссылки + + 1. [Форма для записи на зачет](https://docs.google.com/forms/d/e/1FAIpQLScjft8hZCjlfoeVicSJHnX_uMW7xpA5RxSMQwqhy6aXGZWCGw/viewform) + 1. [Результаты записи на зачет](https://docs.google.com/spreadsheets/d/1g1XA_62KxWQHjXHsGoEAg0nisYQGZ_amLPVWXH_ftEA/edit?gid=1533773578#gid=1533773578) + 1. Экзаменационный репозиторий: `https://www.kgeorgiy.info/git-students/year2025//prog-intro-exam`