From ff08afe5e02fb0ed800a45e1ade9db4823d26fea Mon Sep 17 00:00:00 2001 From: vandechat96 Date: Tue, 11 Oct 2022 22:27:09 +0200 Subject: [PATCH] add miller arbin test + fast modular exponent --- rust/Cargo.toml | 5 ++- rust/algo.md | 24 +++++++++- rust/pictures/mrp.png | Bin 0 -> 52395 bytes rust/src/lib.rs | 2 +- rust/src/main.rs | 12 ----- rust/src/prime.rs | 100 +++++++++++++++++++++++++++++++++++++----- rust/src/utils.rs | 28 +++++++++++- 7 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 rust/pictures/mrp.png diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9c8fa07..f2fce95 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "TPNSM" +name = "crypto_test" version = "0.1.0" edition = "2021" @@ -7,4 +7,5 @@ edition = "2021" [dependencies] num = "0.4" -rayon = "1.5.3" \ No newline at end of file +rayon = "1.5" +rand = "0.8" \ No newline at end of file diff --git a/rust/algo.md b/rust/algo.md index b79d918..6466e89 100644 --- a/rust/algo.md +++ b/rust/algo.md @@ -1,2 +1,22 @@ -Elliptic_curve_primality -![image](./pictures/ecp.png) \ No newline at end of file +# Primality tests + +
+Elliptic curve + + +![image](./pictures/ecp.png) +
+ +
+Miller–Rabin + + +![image](./pictures/mrp.png) +
+ + +Baillie–PSW + +AKS + +Adleman–Pomerance–Rumely \ No newline at end of file diff --git a/rust/pictures/mrp.png b/rust/pictures/mrp.png new file mode 100644 index 0000000000000000000000000000000000000000..27689a2f58733495148f578e8f2c1fe4a24a2c27 GIT binary patch literal 52395 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

(_TB3_o>l->2&7fJs&CjoFR=3uW zt98@II#zp4vVY`vGEn?+e?EPro8-ueFn+F z^a5{rb(dkw0)k(xapN`?bvs+is)E1;>I~%H^$1LTMR@~Vaqw7ak%f`RKcM}|&?LFg zy)T&ZBA(1`+vv2K7;A2BsW(50<;9?I#80%JE98HV*iP?YvIX0ga7AN!n$h*m&sY8V zSI_#S;3ne@j2+x7K3C+5TRNG(P|_+&+M%3o8B0YkBAQH7D3HX&6*D;bepC`6w~AGu~b+eMSo~_ zarom;t`i$FT)m}(hxfOaufP(qv9SYw2$;||Va*KoYn=lP%~`|s7`cq4n{#Jl4x?ta z=Lr=ZpXY@fA>Qiyr)u5=owYa{+tEQnql^y%H1`I3EoQiJoAX|HN4)#)dvdmjfr7x! zE9!PXJt}LQ!RD#gti0Wb8R*v@eAeN+KUp&ocrM)L}8tp6ikhY zDzaLs<}hb0qy2hwzKiY4_Y|V?RfxLBqoXq4X(iboA9l4_-m&&h$|AiYp_MNkPh|bI z$8UdYm%KUp{PL7ZfJB-1V8EwtTV56j4Q26(q`NO=V#l%j-21sXxWhKuYyzx1?*i|A zU;HwayMG>Af;Y_pvCA~E$7>KLET z7kaJ+>MR91vB6{&=J9qOz247@9GOHJI?|53iRLm}MyKVB>Wl!&n=p{=-amx5 z(=uDIlNx_2*USLRF~?z-F901Cb(>SaThIUQ%-0BCGw#C}yzjPDEaWFI2lj&yw8yzFoU z(@lkK;c_X(Lo`!H)$?aAbW;%?*^BnsoFpWpjl|*mOxVuW?%}hoXUkLeiO^wUb8gYQ zrwNp^*_pyy=Sca3KDdUF{YXwu-fT5l>+}upX8PE-w4}8K&q}>B**hTXG#&vZTwgRR z2OO_8@b-WmSf{l&7}-_7)2SgeT|wnds}mCcT0R zhmB|&UK4mRVDr=wm?8lRLP?Cp;AoJF+3s{JQv{oAxd|CPS`5O#-Usp) zaCr2Za0~5iMk7(-uFUgKa>=BzhFZ*a$5;ro_pg3*tlwf-P*OE{g0ufIUaEz#y@}5% zhl{Sbg-k+1i4Zn%G1sa-28?BPFjfNNL$U2GGHOCa0QM;OsY^uRNnV!_J3r5KCvHM4 zS{Ni8$TGvEzN_i`!yzs;*jBZpc6Q9}ZT}}X+o5{T2W!7tnhkiOwpu}ZcO-21G9u*o z!@iP+%`WUS!Ew~Hj3W5E+mn(3{~+2wT)AkWiqRj2^1?BZyATC~7EK~eSLvTErz1kT zcWYp+C%s&xZ`8?ZShuKOviBMk`E-U$t}3Oe+cdjI-I> zmf-@Y?`JfqUt5K5IowJ*mLd=u+`6NFLy8?fxSBug^Z>;~%83pYn`zRSfL8-JJU*YvK2!@Dn;w)aIWBXO&dB} z#DEFaA`yV&V~0uga02}E3m$`r=y+5mAgAr@HV4PP?<`BvDwakR0ogqpbS^dZ5Bqsv z0XKeAqw~g8Sn_r|*c3_So`XaBr*|BwOiDKZ!5dOwH&(^+lvFO?lTM<5q5desIJrTS zZCwyaaYVqcbNRBFEtVyW`te^uLIgo-jIdp86;ahhyet!4KJ2e5_*aP-W`Wu%d<)eY zOd4`Rn9@*F+bQjKLZ%=2M>PMdp#BPgVDRYC3$PlR{pA72%ff<-!g_Zej{E079#pc8Q=dGV{5u_4>;9nk^?8_kcIcKY=5~-aW z_lL)FSK1&l6RL9r$|@?{_`~7IB7>9+>Z3b2A?Gx`Igk7 zqRD;%=n)TmeMe3BUquK4_`+b(JHaDKF*h!s2K)QvIC}AkVR;hm!#5ob-F6s1)E<#YYyy$}mQoorOw%*mSTYvK@6_f6bOm(PV%|{#loQm#2tGx$ZTMgGijSJNTV!;p&CjipR?z z%xT~xT;t!Qs49OGFD(sNZK_mDc-)c53*6vMrw>jll2UVNuSp*nE`6bV(M(!W5*b*= zTP|-_c%?f%p$f;N)A*!)P)0@?$fcX8#s9qulmG>n5>{Boynk_~6@Y&d@f#BebnLaj zzdT;UF?bZgp^Au1b`Y+&G;63cYUUp9SXgqoW;`)^OeP7@jwG{A2p(-^O6h~JpE$X> zQTKN%5$_o@-&tM@a$=F07L*Y?gHP9J( z1&(`R>2CM?o|1{}M*x(gb0}V5am5edRo!QpWVT;bN}ADj$-+-``w{FEm*Z0L^S2m^yb^ww%2|Njaf7Uka!1+nBQ z%h&;Zqo;BGBqZ~H5B`b|P-{LYCZo;#x6kxfC;f>*4r}o941Xf^zr{5H0a68w)$>v8 zKW`Z#G9w0T0yC`!?{xlq(TY$4l~5K28xiw=9}F3iK?NMuVD*s4=jQPSnr@2>Sp4Q# zG(kMC*NNfq(B2h)N4t~2@R@$r2AL>OS`u44s5)T0DIdP@^&fSap7C$x)KpPXQ&K9` zDQWb9^~Aj2o6L+^Md6(iPFzV!ScA1cdBxo1H6-J!f2#r*b{(hOJ()wm}Hy9obn8I`q6xN2vyNOzz z>8W7HaO~sR3cA+OTJ>s}fJJH(aivxA#MkT$E;;!ak@Bnx(0%9?#P*K#;1JrGc(vcP z&()E{hTG^l0&i4hB?<}S0&A{-<2AA@%v#rqy@STL#LNm#QUgC=6>vw$SPKIAc zdU&YsXF|N3>Wj7K9ykGt>q4;I#-X>9&Ad&<<>lpKXpuAnFFM}HPi;b5 zdP@&1bY;(I;8d$}8g`#0q7zL!b<$WDN7J>JfjP13=lvsqMHij;Jq&g=J)6CSaTs(& zax%{A440McWX`D%)xCy_%GX;AQBHp@WMapv_W2(@gy9Y>>Go#hkDA$nOyMAIP6Bi@ zFY43yn3$kCU=pkA#V_3FDkt?U9fDmZH3&V7PDX-IUotFVYA33o1TZkXV}{L`h}Y^s zaP?yCrKV&mH}KGKKIRbo0+am#{ktLY7{mH(*ZH4%{Hhv*i%VT%j|(gn5}vu=4$c|E z7K>7GXndN;6h{AC9^X(Q%ap+P68g;^(i247hLy3G)E<>u@7KRvG~^R+o`ivhWKB0kxvN$k? ztyUT>xy`QW={YHY0knU1I5$SG_QY56irMtUBS`g`ykPhii0g>+R?(TTD@%ul| z$$R`B!e<6%?wNb<>ssr3t=$?jHB@OjU*O96j_%6Vpj-DuX24!Zk&c$OX13eCXUSK( zE$i;;qXLkG{7PY7zJ@@{E5GloVU{qI{OuG$ptmK2Uf_5Lw^kYz4uWNmZ_=etXU*x{ z4Ra6^_|L~1SUvGC;@r-wtrmYe;^N_D@~=Je?Z~~=TgHls3e@W1NGAn#$j6W5xvJBR z;n;=q!?fPTwZfUyQGe7ImfkmSHW}eUv4e&iI~_A6i??wKx3=(ClGfZ^ZF(%L(?h~T z+tx))7S?{Eealu2Ye26j3^%uUhP}{OESCv0h)%O;{Fe0ZeVzfit3oET zs5o-$rfsZq+vgGV!#f%#en<}^V=HY!MBT8Co1IDL(!%s=JA3qQINzPr#GZ?Lu7H5J z792_POWxbv-L=|eLE6QmcIzAO;D#D}_)Kn6Qkr&hA}WFiUB?J6{SS8Nd#lJzqLrBq z71xt*(lwn?GYWtjJ9Q)RDdt(T*9{_`nMq zSEPaD?ntl<+rX|U6h;IA9^OE`<@nd@N$W*2K95e;H_M94 z2d&cG8wekZGm7*p>ui7=)5;1Bl-?) zH#pg0*so6RX|oq?Oib*l55(bHcjz zsJLqse&XrCij^blyOAWrU@9_*MkKHD=CXr0>W2iM zOM*tocoLiTdUM|oN2IWV)9I?)r3Ed>Knt!NZ;jURuhsH$==+-`VSzkjK0{oxV<`D! zzIQMKsf;Qz=-;42Lqj29$=t?dc#!LD^b1u1cbnan>CupM@)BNz%q2c8%9kX;$U_G& z3kHlgt=x%!r8Znr1VYIc&ME6n1}?dl;Nsi`3JK>KJT%*(P!p40PW~JwBf~;NJExUR zJCWU9$@x{S@NJn}iiCNrK<#wAP{lX?)hTDD`hRch&5KFg0E*$T|8{p^;?lrnQw{eo z!qF86{`cExgx~vlhgJsn|H{tmaH!{39tfZ7|7YfALxaL##0K94{eP8#0JRXZKst2J z!N2|j0&ObfgVV@12cHD{=lcO389fMz_8T-7%IE!G^+5Ey=%8@co85@OhmkQg%fpvE zbltC%TJ3tEjda|ZqAVqq`}b&HDu3`g8W#(=2V^LPRldJ(r*0yR$@QIUl1ZI$J>Ro*RJ`p^wI@*imQJ~&m2(&g7z{Gkq=Y(D9B%VGRixi$s>}c>;WKIsBlH|aS`Pj@ z$)+hfDQ{&wJuNq?tv1K>b`)u@zX76-V@SE_4Y zs3%D<5){S$6oT>+83c7(g(`*BUgvMhqCIc!xNm--fSu>b0^~C}4#Jh`*!Y+b!RhL4 z6mymYH@D3CLE-dMgR7>@4?=V|%S^zC2@Nu@+f(DkN|!jiPH=5VUN-=mb8Kr=>|=61bonz>GM_~;)~vi)nTM@JIDI})85D^h#Gr8?!~{7%IC3lMMMA+v3fx;#p@pI>mEdJ305B3^eWJX?&Oh$b-`st7`NF z-iEH!qVv)aa@07Nn{jnR&c0&gYh3)dUmCKO%l&T?JQ+`l)dBmx_%Sv^SQJMaD95zDzey$*zP1AT*EarD8ge!WU83@&WG ziOE;X{vjXkkCv$mmP#b0u8Gp4DMn5cee9QN#3)VnC`a@3VEc^Az9^4$KXqY&(`tGF zjnQ31IGQlbsG^LX%Xh{TKsZXei#fM%lc?)fs?)uS=I3PlR(Jh~4wW$5XmiU!kPY>j zPo>7>z!EW^%T$w!ZSM+j3PuRupRqSvFH>_7Y3Kf-G{;jRU8xyx_Of<{O!c?Ewqs19iTew0(kg zztF{YxY~;Uj6>re@q?l6k|rz<4~`Wd$?15+?znFk$Z9S?E>Kk5 zX@hTP$3Vddj`t(Hq=UnKS=inMpQNp(_7R6qaO_X7@68PJL$Zy!Hg!|{fyI)Q?PWls z-{YB-F4hXX`}w6#x&RX0toO>|V@rZ|uz2a`<&dth)ObO+yUx+KHdF)Jhl{j!lWL4M z2s-)ib8?AxZ0bZ;IjnO(N9v=!1fq+ErfrOI?e zHe0_30`ptbC^8fhC>PT6bCgC0)1n2Fl$ciLvL4Ilho8SMDZoIGtinSO{cs;@p0{ho zuGjy-^L2(4JpHXdE-`{7CAF>FkOT>1YchCSW=n)?P5$I8p8~zaz2?=GMwOY8liU58 zDY}(mLY(wtdF(cy_q&9*gwW7inVt>RbE3{RuMHpsMZk4l)@r@&idf<#6oy^VK1Mp` zqL>_#3;yUK$fl^JRjhe!7o&Pa0MT}^oe%xoL1Q&l_ON^9!nIt-dH0y2Iuwx{+sDvB z&PP&ON;`+AT(#NtE|#GPs~d_q)wnicDn38ol&eRVrB>(YJ6}|IXZPkHb)B8Zn4F4x zdcxJQ!IM~vqeXkPEpmGO68tpZywKmFEBPk`gH(B6kre&)D4=Bv+zZG$&q6Pd&d$}UQG0BTWY16G-o|DLDH{BnO3<=fJ@ zIhfS!8eX_KXmWor9uyUtbB1*HSbF84JnOut(snL29BDx3|>6L3Ox3!IJ=hPh zy=jGG`ijH{4Ji^`%Q`%cCD8x=ky!?ot72iwrX4tyz?;c;;+R^G_7<4sl??nXB1mJ> zi6fJ#JMEkif+84=n4?qPO;zicfsGhawc@}0pGUCp=!$0sZF|&4l ziWnk8ewG9fL5Ty^A;Q7A46by3e9%1zH?hn0d%X9aQRFqz*&tKpvL5iOtFQ*?UKN{1~B(>c8x{_9I zX-1I@Not?XC9H@LML0=GxomuqSlZwBB?WNf#f^`y;R(;Fs~|Rm6q5U>ruu(sRU9G! zE~22B5rzC$@zU{uiWi@3g6+wFb*h{RWQfosQ}`Y1|3t08U?{btz+m=>{}~eh-+##4 zBU_ulUZ)PzCk0Fb)3E)MkbV8cfp|vKZl97W;#XpmsEV*f@yG^`!LHYYF`CwlC0&gmD@9I$V{&6JXGz|9x!}peV$!6|b zJ>{Bcz3T>pGsuLy+D(z0lqvl)0dM|1i2^Q^4(2_Un62jed6xwmRGhY$> z@slu!Mj}W?k0lW>L)}^~X6Q$w4L5$a`3g?PIW1zhH;&$yZ65SWnEO6{^G3%bgrm7$ zvX5349PpKOE71}iK&jo`jx196CZld&K*IdF zP;@46+3jNFBp$n!-cVbz>QxrY%gsgV?19UzExf!u=mr3kqdnd<1P7*iH(e3P}b9CfY zyfi-H2dXaH?{l6eq8gU&b8q8iYQwjKsW1okiW>9O%*`2FL@3y<@YOSX9fy2gv<1pSG zdan?inR_bXDk+Vll2xD_keU0eCUaVMg-=*6R`&;n#S1Y`WR>FG`1;P4SQ2B#I-5)$ zv}RJ%^C_=x-EWMCMb}iOzL2iy@}>@S@ViZ(EQk-v`!)15?e6y z^-LD1i9+qo*3`Z`Mna-kynWt@8}@=%0$KyL>EB)Q@bWh!8%XIho06}}HK?Sdlo2jG zi%fC z_Cpt2mMsz0Yl<52!e<+|8P2M+2w&k_e~&s{)ib^xe_b+%a?l|TkCKizna5jc@lf9^ zU;P|?-+%{r;@|GW4{Ydsg`@VG-zSijZD5JUpD|x+o6r?x3=8uN{A?}6SmeF?$pw+1 zj0`X`G_v)zzhU3yXpIOr4~SV-Bp@o(^S@bquaNmsuWUu#g(Cc9f0olFd6_|oHpUy5 zii{7Azizk_X(N~B4q5v|z+2&DbaQY^SiXGX@T|k1PoF?-G!skG&?( zZjSU+)Gz??738yv`y><QUV!*fIGzUi4g^qRiFUNFu4q&G zSxh=v2gFpFj=q8GA36%DF4nMITnUVJ-y?81JG)-CS}fTI*+7_x!TgA6(hXz|u{zB| zX3K(yBl%geS+Hr}WB<%uI8j23hU8StN7c*BUb74&G4u0LQ5}sK0enHY*>kq~aUkiK z8}KfS^YZttd5n$_b%?=y4phtrX57; zjWXQa@dyb?@4rf1T7mL!7403esV>n!o*_f^_e_LH{l0F8zf-p^ASgOwx8V7A&fntm zIO9aO|0mvMxKIfpHn;`6O zt3_4vHMtMQ%>h|XL-;N{JY0RF*{&$<)l(xw!--<2$Lm#E{j+Ep9i^Ow7_m>-lnwu} z04Qo0GNZW@DoV^X-`Sbc!hHm|x$7+4@t#{O6y<+vbGgh3%Uoq1s%3WL__Fc;unNy5 zGN$Ubgz?=?77u_?dvQ4V4IpzDqaS>#UwK$z#Rl7e=kSr8ZGsiEkd02YUwOMgXn|oWT*IbT1{jD?Ile&>pSMY-RqCf zpBmUk?PtII{N^odjK3AU3oYPd;YP3aq{RKRx9sBpyRTMS#M&eNGE{SZ?gpqJRGAyk zAo#V8w=$7vQh%)_5uqC_I&D0@%3IL9{QC0TeyfLh^&@tPW>c4aa0Yu*6CY6S)Ya~I zzBJ?E;J6&K$kHoPhG5v*gw1^!a7r5nO*k>9|MmK_QqK#%?^g3Xxb(`>y%ZIt0G{fa zc3qowKFqu&1}RiJO&N_=T19N#R{uepBADt`CJ*PcHGnK!pNjrlMN+_JlmAw4GEX=9 z6F-{R^9FAtBO`+`7xTpu28>ibX=Cse?U#<2|56;?N%4VGLsYBZ;73f|sg}*bLcP)3 zr^UY{;JxYBoTQ~umCMcc-4aJw21ZH!tOG&U-aZsxqFGg*5|fZ{b-t^D$h&)XCXEO+ zsDg=jl$ohVlE^=I4mg3w2mVpuYeyISyW_ztO4;Yz*71MJ8sPNkJQ1G6ZjStQXJkaX z$txn(#YE?!nrmDefaL8V4h}gJ*~c#~jb4?!&6qzPHl92HWmc8tXp%S1PxDMqU|EW| z9v&@Jx^~o0O~sF<8dq#Q4mb-(@(orMa#*T5?}Q6l+>oN+$hXzI-t0R)9^wxWXBoEBoK_Y#0or|2?@( zmj8@{j1b^AG>0q-B? zKK|_VM0gvyw4NAce;>n1 zDfRco`?kN%Ajl~&Dg#~i;rQ;=Q3xUxo#=7aS`V{M_BVz;;v?g3nB;-xdES`VSTcUE zn8L)d($b{jJ6R-2tM8V7M1r3c-4EjmJ5`M3}1ohCWl~wG>o16WqD#Y!3{>h?|F}V)7rK#>M z#?_TCFLhis=-f-5o>KmUzD>kWwd}hv1H*vZ;@Gf`8`4m$B}S}vm=S`+kLtKwuitRf0pq=+O{X5$WN2UBIj zu!@u!dcgZ%?~O;{a)0QKI;8k9JW1T21Vq+_U-p(FA}}$VqEr1w0x8I@>WJ+BTFEOx zxX|IK=Xuk|WZCSxU>>TSfJ9&?jFW251_2}K%Q-QJ)D@{OQ=b}%uK%)SOnGKq4%!9c z%FKYXErW)#Ozp+!rtC^UogHR#+eqr>%FT@>6W%{*hAI3d-+a3P5AV_{rG)`OdGA{a(rJF=WsgGG?o< z_sVL@{dnDeQ*T`5Go+-)lcB^&JSTaFwz>0^Sw-0n4{dI4B3ZMEp$oAdm5!E+N> zOLMJFMq-z|PfL~cmC1FFyLGiY<#8dIuWtD>j=AlmFsHw5YEn|t(S~IpGOmjg)3t!c zPW10}0BDcEVr+4=|MmGf>?XHNlKt4?f_7R)BrcgW7dpkPRBIl9(zmy(636u^9II&F zsucAp69n4CU6N-rlvLaF#tItMKl8e~`Wd16-B8LRh$QvH!^=-KF|9hkEzZV8%@ock(&7e>^%p3t3Wxi=F)=YNH%YA4!d6;mTt30#s3<7L z^VMd_=uJ@WwzD+R4B47DdK%^Jg3S( z7~v6o`P<_D<_mi@G*mWL$)+5b2rO=$wLV_+!uID#Xi=?a)z-&;^vz~*FL>N;_*Sjv z_L}rR7B&@+OQtS|d@EEbJpdeG(H@CQlH6}x`1jkjrjiT)x|OMW)*T-79H|G6$F#OO ztI2XeT73qGxMU zKiGImHL@jQSNZKo-2dAh4AiavU~hEU{=PqE>he(R#vTaN5zp(pXTO`9cEvwr7BGYv zA)ggX=!ZuKuUQBj|9ljG;kH13A?EK(D2Q@I|9=|OjA+i3jP^oFs*l*+=!c8cv??5o z`zS?dJvSUhT1vX}3f=!U2cR4xYf~jWe3pyY8UIbQ^>XaO0Z>V}M7>7F5@^$E<|ujD zZdGOp7+i}1w>o<)i({Q3^dE)YZsKj#XrYP)GjtY<*5S3@e{-_zQs8adv$j${KA@odqt}G~xbM0~MuvT0vM^u0IH|omI~&wLn|;_emHr}@MQI0H^596* ziSQ^{sL~lGAjcxFt zgyKG(bv!_qI`c4DECI7P7=!}I&=BZYq<^GeXC~G)!59&n2tSH z0Lu-m+CX@bC$F18bO3_cIika*NXM53cTmEKaMSSF2(^SGVZ_G9@_H|S-5pt{nD|v~ z|KhKoH2Ay!^XOXa>eXKfupK%WOq0m{mh-Eprwm#e6^&%i0;fQ^$ypGmiZz!Tm5j?r zDelis#9%Tp1UK`m-^dFXaKc^4Z_Y12evF3BL6bIYTr?!uoq{9b-*BdX>b{b={#LV4 zxfoSF2~>g7&SlnPWzd7jN=k~L`9s?GIzEX@NKlnq>rdxRmLOA_N>wb?MoZx*7o=gg zp22yhgj>+uFLyMdR z)(HdCFRld8sF_Qd0*>Olh$*~LQpzQoAK4RGN0B6;3}^}TFrZn4s%wPehC7cPcPBt6 zMwr8C6|otF&s$qph7AQ1FGhma-XTCiD=2 z_2?2>#UQh&*O1P$+Tzv0_-bYgpIJ_l-F%E|+nsB74|vWIv>L6S(D%pl4T3+sW3S-H zGDcgLkB*B|t23kiO!wwX9mCE-)mt%FS2Ah=0m{rIk*XI;1-i-<8MmA1-Y=AjdQ7KF zby~bYC1$9+hq=5NMJ^@v)8v(A*ajG4%~?_U+6fLJ1hes?49K+M#0@Unp&7)S$F`RX z_o4`Bd$SFbZ5#c*%BF(!x>3};-C)q6t7=zx3T`aF z7*!)0jog7X0g1K|E1|t?`;@OZI+}Orz=SLrYx_$TJqeyoDrXPZD&nVC7sxK=W_hbw zRMvb%FRyx)UT}^NL7H(-7}%?t3o?Q`J2707j}Wh*V`5Zi!2qO))GAS?ht2I^^1jFM zp6P|vR7Ugnm?zj8OFI*#&ZVid)nFE%SOeN>u3w`@^pK3Z3^5sYuhK3hD)dd$Q3yol zxiZb`mD5e8jjnh;buI|Lxzxz}S1(1WD z(n-BgsCUS1A82kHOfMptA;p)I$$1m6|14d=`$U!mhUZA%oosn_X>aO0B=0#?@OoH-d7Qs96J;FS^1QZ$d0hYp3>FT<=PU^ zvz&hL6E%PRQ&>;PJtx~^o&4c+lM&Mg)<-A2=i8LV59aEaVYhMYRd&f7g9Tq9Ph`-RzH#-6i}Q34jf+( zmQ6PoQZg~1YgeL3PD$CFJV3xellwWyztZ+hi3$*O;nLQL@-70FwXIj%@Ax9h6=-yL zzK!^#q%I}s8k%=R{88bCF*XcK`Or@Dd3v6G*cdEF7_a|og?CtRJ#ar|r?5u7XqJkU zAH5c|cd?jN>fS%wLhbYurrcj}ZXKJlnSXH^f{2kwI|q7Rl5X^0Ts`F3>`=otwXO2^ z3$Tb-xAWzqylCv~pD7$X0M|G)3b@A3bA(f#msDTHDn5XL?y!h31@n=BSR4{-3_KF+ z8nbCC78c~@W6%TfI3F^U(0}gmvtCd<1rU(tA_|(AP=ie^c{`*xo*r=&4;y=$?zNcM zss%9ygbAN>aMUw_31-H!DA(gY$!_!D{!QEqC!jTrtkXjTga#2-&oh6N7Yg0!UfZp& zY9onk9gCd7I&k1Yrpn{E-38=aa6^zgM?H(+Xb-Fxn>$V+z@Pq{0{rRdbPN4(T9`81 zwo%0Y%|=}BH`IXP2L@F!}L!lnH(Nw9C@#h2p^Tv0_*N%Bk{n+8+=PT zxMw?9uzcJC{J?{Y<5$;%f-XmU30I^@ZVMX~tk0goU)$SIp^Js3b`m~T&;`RxD5{K- zv(}4TpMrMRS#?Yv3lB@gl8 zU6$YaD|KuOXJp&WyN$3CCm~T%^h1`oU9hSBm$Tq7UJw1HG@ncO3@qi`Aq<~oqd>#W zNlgZraQrDbLxcfaNuZmT@ITF@_qS%!>m0T5ue26|fX56h{eTkv@vle?5Rw-MYxGx- zOOu2De&s^|yzemRpwNG@J`mrpzdIrDm=m7MSE;;|TGHAY3jYHzeT0UUkN*1C6w|-R zg^9*CU=hzG#92Xs96+e~&|_%FzEzoD&A<|~f!xws!6M*)@~RvE`!v*rK$E-g82i#^ zoye1?f0_^=onJw4Ea7zo{ZaR$8ilAQ3MDYgy3ok-;3wp5`(Ak)T5+QnSnKX_8GH#L zxZ38eG>VMor5Or2b@lk@z zoo$%|y9b@uWC`l2y;#?DheRah@XKTbRg>HEeYAFBn?<{m}f5B$i*o*oSWt3DICY>H=uQ*s2tydDDpT+IB~hB*qP7S8x< zZWdoZVzlc%VfG6!oCi!@6b!ULe#?S+qA1)G+%;w?M|Sq1942Uch1j@X@jkB)#4KK3 z(1ElU`GpKG&$sp!!&weVW@fd0w<^Z995~<74kQ(Y>xg7i%3PK`fVAyJDmXsj7E?~S zi#+2t7+IXbof3;DO_tokq!1*~A~8 zx!y=a6*_8Ei@de~V`oemV8}@q6Gi3xU|Q5Udm!N$TaEj3-Z;)F2-2IawmN4ySZ_Ji zD#}f81ZI!x#T-D>b#sL^6n&YU4q)&qVZt`d0O4H7 z+P}jO;L8T-V|5bgMT)za9mH%!TPYe`2?s*WBUu_MQ^yJ6OclJ5Yr?`=g2M^N?Me!A zTXXr;bdxe^0v!|Ulu1o$!ivYh6Yak>yPuk zxx4NjB&V|;%^e3mb*7rIDe@rSE8X`UukjSCtQ^M%qfVO)WKX#}PRI&5Zvb2=-LOi{ zS_`_@kLH+p-d9Ihjg{sT*0s)pY`b^En{6F_>nJU9kob>fPQgY|NF5z|OkOWzEf%C% zX*P@|(DVO66hg>Y(LC@Np-Sj98}>mMBt;pT%eR+_wEZ3?U-}H;pxIrN%R4VWsu`>Z zcs_JKtxz`F_x%R<;B-~bsNGCO1#1mAb}FqVKf>^@AUrBuNK22G>MaP0 z#WJj$Ob?DUYB_dBorH79Sm%W`mZLk;K1&{{?rmsL;$uTYOeJe0>xI+e#E_b=fA@O` z^mAdM9h^p{S2kQ7NhWnIuaRpR5LiL0%5!1{!KuUEFgWW3q6z=>ezQW-q^8ghq|~opvl<+B z2(XF6%KWP>)v4bcj2HIF`O{?BTTit*?XaCG6n=QO95Dd&ELz#yZ@ETkRUJ}L#=I-Y zHC8h|T7hMxi}j9$QRJ~{65_~%Xk=mHXv9>NM~^g`6*QWpFO|!gptH;WnF1&9(D{(> z#LUvOMh`7ukMD^2z{^XCh!U=%2|jh(!v}wWD{xw>HkjYy0m!7{TNJfbiiRsl-YluH zKAvgRn_u>9usj{ovFyLYgna6V%$7Y&i|!tPZG&T4#QGemaKY<*&~6DudtDsv!>&o8O^Tn_u9K3# z%6Ng8z?$|_m+S#dFEqJ1Nen^|QuIHl>nH_sbL-wvyZO9BLVxVUAP31jeFEQG_Ghb` z-4D}3;$hwm;a0ziUjB+wjwA#}o+hkoicn_z{BAzxsBin9ze1q`kqm^l2E0IO_r`pa zIesJ7@x#Hq`)7&=6k#WjDA+#o?yao%#U8i9vh>ss$B(gD{1!TY$4tH+b@ea3TbLD( zBnS$rIQ5M$S*$HLdC>g4`jI7|tXvr!?L^AsA_sC3>xAkwg}WG0?OkUhQz6dIWfi`+ z_sTt%0P|z>U2(otH88d`)e|60TD-iHO8J!42xeRp-I|nsfm2O0;gG#1(>(WJd~bk5 zuCQNZ^d|mdy2|)%SB19=<@Z6$VZXPVaBWs?lr^-88Y^?8lb}ycw9r2TW?Z_U7lq_d zuhR>Zs*YF)Hd>cbf$AsIUGw+>2{ExXWoCHj%%XX;Sq@GCBUUY2P8hq z_iAJi{0?$#aXkHGh=5V$uczFueRM>8G9C~tsQY|9P~%++6M9M+Xy00ir%huXI}J=h z(1aLIhWq8=<`qtQ!0t2Ve>RvPm}XKt()9&a+i&Di@H^Af`oz_}fK2CG7wQ&s=zi## z8Ql~wWu<;rk}Vzgi>=7brHWpk?E{geXM8OS4Jv~*kFuiiO0_<3P|v#2w`-53gk$1AFaRSQU_5~Al(#Kz z3w1JRrP2LRK!wT1k)@2JWK0cTkQgyxgg1DgrIZ9^c za*FUoL1&K6=wr6$&sk-6vbv+J5rUWQ&vze_BH94dLb10-70Y5v!cF%PBRC{sd@>#l%{K+%7>Zv+%QXit1pgrVs0OeX&1vYvc>av%4IZDFD3{(T4wpY9tx z%3@oI#>dBn_F-!cjeZqr*N~C=p&-6UH;(zQ@RJ*Ytm(FOglcUZWD0&@%TsuvT*TF8 z@`P`s%!HYNjw3n|P!G&YbFLnm8`Y4f%uMI4??X8g7S-9TtIhh|RF^H#Y!E^;Q2$ld z@UApVdnv9cnS0F0ZbH~oVIRJ5^sSp)>Y@AFE&7VC)QkKMTyR;{2No!mzDXE2xCNeH zDMJcYuoqscRYIv_@@(_$%`Is_m}BEhby-}IH*?cC#!D=wS;TWU`byC+g<1rfWN*`r zW^RT)Wr1)E5in`Vn9k*vCzTZx&g}U;ke!%_7T&+|Mx}_3JUzNq9MKC==_Y5&~8?Mg0}T zz2#`}$x(CGS;17ERdNCwqCJ6SEG(BS4KRaB_Ftd{4mu1soy)S6CJM6tX1}z3nSv7_ z@*26B#l*y1wx1>P%sw-Wn*%hrLJmpJ^V)^XXo{bbi^3r>RdE5&MVXjE`m&(=Hkr1FyNWgK>V*aA(FD&&q3pyEyz zi|CPvwal%X8YLdw&vMiO@v$YLEZ(}G{BqiJkmuF^W;fwr>g0~XTjG-8P~T{FWQ2!@ zt{^}2)-AGIm-bTqq~y!-E*nnqL($si;G&q&S^zOy1z>Skc>-Zc+|Fzf_FuX$KZgpB z(~C<`PU2o* z&oRT%2SCk1QlMUC%xA9J7DX~d=9drSm}|&DqlDQ`PC5Amk->d0fGN)gxyEKom zRIlQ5oe`X6;(1Aqj#NVb67v-rNqvQyp4d`Lou|YS($;V>y$HNQOAd>kX3_Z^0Z3w* zeJ=km?W2`km;xVJ+t6z$(B@I(?f$g0k606BZa6jW{h3-u94@Z8ZL!dMbPYU@H89!} zG_o_2Z$QbIpjM-sWW2iano!=QY_iU@EQ5Plt&KAMit-bETab{F*!dWt@n@m^rpTzx zf*>@~6>=MNE{YvY_s@hC4@OEhP;f?Ddwb%CO&;r|!?r;Y^ZsRD$&VP0W=gUs!#8gJlj~E$BNuh9v3|IhTk%qFV;OaVPrM87NWRnX`kpp!jR5%lIlhaH&v%W6)nY-ew>aek|)I*?ei{RB?8=qH=OMN_^(VbwV ze|~XtvVvUjilw!-!)WvCo24mPN2tTd81yQlC(cz^)@I$;{Yd$Uh{4qea2X#BP@kKJ z7nS6TxtBJwt9x2}VDB{Hbq*LCQyV#V@920(69z%&7xr3EGBjbUJuRK#e?cX(?^RKj z;oX|OKLGViZ8D|Y>GocnT4z7%qt{t&+jHzlQWI2ek$m`31%-lxh0zcD$Y}Fh+tahR z{Z6S{J(6DU7zneFj}#BMu>%74yG9inAxP-T$gl&1e49R(ofYVtPhi}Frc0iy1o&WL z!iebNB5{3OGWbfZFhE0pdw0h{n<9`iYPnVJ2H}}|Kf=OtZYu8*$G=RSl~tN#+}pvH zaD9E#)kXHSvzt1pA6xo)Vcv^oJqYqlWINxY+~*uw3D2(cl$ZQJ4XaSz@C$TK+rNN$ zYyA4@%<%QJaqM7lH6GDIUZ4&qj_sbLjC8Hd1M0d{BhlBJ^77;Gyj7MbE8fODij#K)XYn_C%UqbFC{e zZ0BKhmQ#^4|zzwylhi~*jlkwop-Awxv-N}TJI_EQbr`{ z8dMtRn5@h@Jt5O+(vdys{DRtkENEL2Z-djJOLE&|Db@ye_bi{y!jg;2mMJaok2!o3 zu28<&KLVzulUsc~nG@LbJzry_hQ_TwD=yWsqZv|@g6Z|uJBj;!aX}UD31hxxX;(Fl z5q&nQZEe~?OkAT8Tzw0>g#S@T3Y=rul3#nO2vy9Knai5++^L&YH?!fCO_Rz_beN%` zy1!X6g2eVVvqOSQNY}@%OU9VN4NA>(%~A4#4SwZoe9TAiFCfsAWqWWbuaAB;FPbsr ziPaF44aFB_X{DcX5$(U)t419f=`wES)fzg;I|f5CNgh_YxL(ahbuyt}ztza%E~N(D zqEFZ!(|6Ciy?RB}@$YL92r0rvltuDzbL*{cZxT_BZ+=O7JqrVi)cbM%xp(b%ikz5| z1Hsoe_zCr&J$I7>)6rl31{!?ohw2ct8-ZE=VnueaWo&66^Ox1n-anPLq}beZf(g j<^A6u16l<|mpI!54QL_!LD`^BfRYlEd0ir+>-T>EI_XiG literal 0 HcmV?d00001 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 94a8f2f..edcf95f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,2 +1,2 @@ pub mod prime; -pub mod utils; \ No newline at end of file +pub mod utils; diff --git a/rust/src/main.rs b/rust/src/main.rs index 8a2b537..e7a11a9 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,15 +1,3 @@ -use TPNSM::prime::{prime_classical, prime_dumb_rayon}; - fn main() { println!("Hello, world!"); - - let max = 1000000; - find_prime(prime_classical(max)); - //find_prime(prime_dumb_rayon(max)); } - -fn find_prime(primes: Vec) { - //println!("{:?}", primes); - println!("{:?}", primes.last()); - println!("{}", primes.len()); -} \ No newline at end of file diff --git a/rust/src/prime.rs b/rust/src/prime.rs index a3afcc2..a9bb46a 100644 --- a/rust/src/prime.rs +++ b/rust/src/prime.rs @@ -1,17 +1,60 @@ +use crate::utils::fast_exp; use num::integer::sqrt; +use rand::{thread_rng, Rng}; use rayon::prelude::*; +pub fn miller_rabbin_gen(max: u32, acr: u32) -> Vec { + let method = |n| miller_rabbin_test(n, acr); + prime_gen(max, method) +} + +pub fn miller_rabbin_test(n: u32, acr: u32) -> bool { + if n % 2 == 0 { + false + } else { + let (k, q) = find_2_k_q(n - 1); + let mut random = thread_rng(); + + 'l: for _ in 0..acr { + let a = random.gen_range(1..n - 1); + let mut x = fast_exp(a, q, n); + + if x != 1 && x != n - 1 { + for _ in 0..k - 1 { + x = fast_exp(x, 2, n); + if x == n - 1 { + continue 'l; + } + } + return false; + } + } + true + } +} + +fn find_2_k_q(mut n: u32) -> (u32, u32) { + let mut k = 0; + while n % 2 == 0 { + n /= 2; + k += 1; + } + (k, n) +} + fn prime_gen(max: u32, method: F) -> Vec - where F: Fn(u32) -> bool { +where + F: Fn(u32) -> bool, +{ let mut primes = vec![2_u32]; - let mut i = 1; + let mut i = 3; while i < max { - i += 2; let prime = method(i); if prime { primes.push(i); } + i += 2; } primes } @@ -19,7 +62,7 @@ fn prime_gen(max: u32, method: F) -> Vec pub fn prime_classical(max: u32) -> Vec { let mut primes = vec![2_u32]; let mut i = 1; - 'l: while i < max { + 'l: while i + 2 < max { i += 2; for p in &primes { if i % p == 0 { @@ -35,16 +78,53 @@ pub fn prime_classical(max: u32) -> Vec { pub fn prime_dumb_rayon(max: u32) -> Vec { let mut primes = vec![2_u32]; - let mut i = 1; + let mut i = 3; while i < max { - i += 2; - let found = primes.par_iter() - .filter(|x| { **x <= sqrt(i) }) - .any(|x| { i % *x == 0 }); + let found = primes + .par_iter() + .filter(|x| **x <= sqrt(i)) + .any(|x| i % *x == 0); if !found { primes.push(i); } + i += 2; } primes -} \ No newline at end of file +} +#[cfg(test)] +mod tests { + use super::*; + const PRIME_TEST: [(u32, u32); 6] = [ + (10, 7), + (100, 97), + (500, 499), + (1000, 997), + (8300, 8297), + (10000, 9973), + ]; + + #[test] + fn test_prime_classical() { + for (max, prem) in PRIME_TEST { + assert_eq!(prime_classical(max).last(), Some(&prem)); + } + } + + #[test] + fn test_prime_dumb_rayon() { + for (max, prem) in PRIME_TEST { + assert_eq!(prime_dumb_rayon(max).last(), Some(&prem)); + } + } + + #[test] + fn test_miller_rabbin() { + for (max, prem) in PRIME_TEST { + assert_eq!(miller_rabbin_gen(max, 10).last(), Some(&prem)); + } + + assert!(!miller_rabbin_test(2147483649, 10)); + assert!(miller_rabbin_test(2147483647, 10)); + } +} diff --git a/rust/src/utils.rs b/rust/src/utils.rs index be52dae..bb5828d 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -48,7 +48,6 @@ pub fn congruent(a: i32, b: i32, n: i32) -> bool { (a - b) % n == 0 } - pub fn gcd_euclid(mut a: u32, mut b: u32) -> u32 { let mut r = a % b; while r != 0 { @@ -79,6 +78,23 @@ pub fn modular_inverse(a: u32, n: u32) -> Option { Some(t as u32) } +pub fn fast_exp(mut b: u32, mut e: u32, m: u32) -> u32 { + if m == 1 { + return 0; + } + let mut result = 1; + + b %= m; + while e > 0 { + if e % 2 == 1 { + result = (result as u64 * b as u64 % m as u64) as u32; + } + e >>= 1; + b = (b as u64 * b as u64 % m as u64) as u32; + } + result +} + #[cfg(test)] mod tests { use super::*; @@ -124,4 +140,12 @@ mod tests { assert_eq!(modular_inverse(10, 19), Some(2)); assert_eq!(modular_inverse(4, 6), None); } -} \ No newline at end of file + + #[test] + fn test_fast_exp() { + assert_eq!(fast_exp(5, 3, 13), 8); + assert_eq!(fast_exp(11, 13, 19), 11); + assert_eq!(fast_exp(1234, 754, 452), 392); + assert_eq!(fast_exp(76158, 24586, 5364), 576); + } +}