From 172f64c6f9c4293f1513f1c737fc90d348795317 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Sat, 10 Oct 2026 09:54:22 +0800 Subject: [PATCH 1/2] new logo, and corrected readme --- ReadMe.md | 16 +- logo/nFramework-horizontal-dark.png | Bin 0 -> 28302 bytes logo/nFramework-horizontal-dark.svg | 6 + logo/nFramework-horizontal-light.png | Bin 0 -> 28298 bytes logo/nFramework-horizontal-light.svg | 6 + .../HighPerformance/Memory/L4.Swiss_X86.cs | 2 + typst_temp/main.pdf | Bin 62767 -> 0 bytes typst_temp/main.typ | 32 ---- typst_temp/template.typ | 172 ------------------ 9 files changed, 21 insertions(+), 213 deletions(-) create mode 100644 logo/nFramework-horizontal-dark.png create mode 100644 logo/nFramework-horizontal-dark.svg create mode 100644 logo/nFramework-horizontal-light.png create mode 100644 logo/nFramework-horizontal-light.svg delete mode 100644 typst_temp/main.pdf delete mode 100644 typst_temp/main.typ delete mode 100644 typst_temp/template.typ diff --git a/ReadMe.md b/ReadMe.md index 4d4ca78e..9f298202 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,14 +1,12 @@ -# Nitload +![nFramework Logo](logo/nFramework-horizontal-light.png) -![Nitload Logo](STG%23LOGO.png) +# nFramework -Nitload (formerly StgSharp) is an experimental .NET framework for graphics, numerical computing, language analysis, and other performance-sensitive applications. It began as the foundation of a next-generation STG (shoot 'em up) engine, but the current repository focuses on reusable low-level infrastructure rather than a complete, ready-to-use game engine. - -The managed libraries now use the `Nitload.*` namespace and project prefix. Repository URLs, the logo filename, and some legacy test artifacts still retain the StgSharp name. +nFramework (formerly StgSharp, now also nitload.Framework) is an experimental framework built on .NET for graphics, numerical computing, language analysis, and other performance-sensitive applications. It combines managed C# libraries with a C native runtime, targets .NET 8, and provides Windows and Linux native implementations. ## Overview -The framework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. Nitload targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets. +nFramework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. nFramework targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets. ## Core Modules @@ -139,7 +137,7 @@ The benchmark reports normalized time per allocation/free operation. Each iterat ## Installation -Nitload is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code. +nFramework is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code. ### Prerequisites @@ -245,7 +243,7 @@ allocator.Free(handle); ## Contributing -Nitload is developed by Nitload Space. Contributions are welcome, particularly in: +nFramework is developed by Nitload Space under the Nitload brand. Contributions are welcome, particularly in: - Performance optimizations - Cross-platform compatibility @@ -254,7 +252,7 @@ Nitload is developed by Nitload Space. Contributions are welcome, particularly i ## License -Nitload is licensed under the MIT License. See [LICENSE](LICENSE) for details. +nFramework is licensed under the MIT License. See [LICENSE](LICENSE) for details. ## Future Plans diff --git a/logo/nFramework-horizontal-dark.png b/logo/nFramework-horizontal-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..62ded825a6da1b2091c4f53462096ec1d107e3cf GIT binary patch literal 28302 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 literal 0 HcmV?d00001 diff --git a/logo/nFramework-horizontal-dark.svg b/logo/nFramework-horizontal-dark.svg new file mode 100644 index 00000000..18d2f744 --- /dev/null +++ b/logo/nFramework-horizontal-dark.svg @@ -0,0 +1,6 @@ + + nFramework + Horizontal nFramework logo with the upright punctuation icon and an IBM Plex Mono Medium wordmark. + + + \ No newline at end of file diff --git a/logo/nFramework-horizontal-light.png b/logo/nFramework-horizontal-light.png new file mode 100644 index 0000000000000000000000000000000000000000..2c5999a54e0a2151df5f65ab71bbade3d947e405 GIT binary patch literal 28298 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 XHoZGmBQC6CQP@8$e$PMbboqY(24lmQ literal 0 HcmV?d00001 diff --git a/logo/nFramework-horizontal-light.svg b/logo/nFramework-horizontal-light.svg new file mode 100644 index 00000000..940a316d --- /dev/null +++ b/logo/nFramework-horizontal-light.svg @@ -0,0 +1,6 @@ + + nFramework + Horizontal nFramework logo with the upright punctuation icon and an IBM Plex Mono Medium wordmark. + + + \ No newline at end of file diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs b/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs index a19f7944..a92933c1 100644 --- a/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs +++ b/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs @@ -186,7 +186,9 @@ namespace Nitload.Common.HighPerformance.Memory Vector128 target_empty_v128 = Vector128.Create((byte)SwissControl.Empty); Vector128 cmp_empty = Sse2.CompareEqual(control_bytes_v128, target_empty_v128); if (Sse2.MoveMask(cmp_empty) != 0) + { break; + } begin = (begin + 1) & (GroupCount - 1); } diff --git a/typst_temp/main.pdf b/typst_temp/main.pdf deleted file mode 100644 index 470dd7f88f72a6a6bc2a26bc1774f60cf002efaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62767 zcmc$_1z45a);0_nG)N=4kd$Tti|+33jzxDj2uQbdN(chd-O?#a35aw{hamkgQ1^E4 z<38tn@Be<+2VCn}G3S_Lj&YBBj5!__xxBC_9TPnp0=XaL2LXWzzzDE4v_RnIW>9vw z12f1Qn1US{glwH{oB%)uDRW~-EfzM&7)1aJ`|U-CmlpwSV|+UW_~%r40|zi<0`u(% z24!<6D=@dHh^QzdBQp~tBT(eWm6275QIs9X4&-GJ20KFJ-wFOx;O+Duy8IBxdM8j8 z?B)buxqB*V?r=Nt?zyzVA5XvQOULxf+H8VAAr{EWYyyx6Mj#`rC=)BY03+kiweNQG z=i0a8f48>01K8zX_VryhyUi4JY+gV>xFhD{~u&nZNJm zyVdS?^L>*)tpBF~(4PWiovj_U7y-b$NdP7Q@NUX4Ub(gDuZt-;IXD|RDLa6{ing{+ z42rf^U>O5Dh`WUioWM@z)?fxDI|CaAWw3*_BgFd-PTy_FAkP4qX=`Kp{bLd9yDKj* zgOnEV&ip#JgCS18l?;*gL$5olLe#pm07R=_2Hkp53hWL5{<*K)(YLy4F)>1(DFXiA zTy%Z{`c~=h>fA;JM4i80?zRnJ`k!8YcmiWU^cYgX;kcn7+HlJZ)IQv zwuZolLD0(9$dZ>=2LdR@TO31zj0M0700Gzl>=5`smVlTaVr(X6029ltX&@G0VuPU0 z6!4vp{)Ew;pZ-xDaVCa8lu^E;rGKV|AG&;B0u+YfNF0hrhUOdP)#`U}JTtBSWmneG_nmVF@P2Z0OlmI5K<2V?~R|3T5; zGvSYI|5YX%0LTsiasZea0nAJQW(XLW0n9AFSM?`p{mhvEvj0C+WoG@ogr7X~TM0k> z@gsWv@W+1|L?h@*+OWqbj z(gucLt6#?aJ&^tw17SU3NWT0vw?TmUBbzWt2nh)qID(AF0_j0)Y!D0mIpZfD ze?Nnjot2&m$iW6t_Kzu$IQhfH@(#8}N?<1~21sQIDI?sR7$mGAwiEh(5!8VclUBA4 zkRrqAwoWjDgzsH`V=#h5@tr*&Rf4FkjT7WU6G-^o`HsN^5`}lecp*PO^D`tg|L?DQ z7d8wgKmf}fL>NpUWPZnB3??kU)9F^H<4Q!llCpc-N4(h)c~Cv+#vksx)!f0{&dJsR!ko8CGYG2z7#SeKm2Jf& zgdsKOANyobgQR>&euvcMkjxLs-mJGzAdG&S_8A#};jBAJf4A4W*lG7mc14-bJgAR!C{|{i8AOr^b$u_LtGwLsh zxr>!Q5p$Qz{=T7qLX4<@kcNWtZI)7Uw)wA60ttk_XQ?~f{G!0WO_RSz$zS#O-$%wB z1O6vu{7Xt=`CY#K9ijYH{a^6#OG;v9qQA>XjF8j;NhP;=iIJWIl5-%LiG?1>1bOQp zK=NPZCT4opTVcQBnD1rIpNIkdt*p4KdH!3(Xey|yX$sRQL%K+ZG7$kFji!BMvh=2R##r5puj^hy31-;ehDgJ>Acu{+p1}9&3erM>q{cs1yX~j2{;Jb$a|OT#1VUC~^$}9Lynqf9M9O^&x){ z5xR*<4%V;Hb$gP86yA<^6`td5 zAL6!xmH``^8{C$zw=Y>C^(Byv6@mg5CVF;u4#?sAd+XtQAJ!34xBlpwe_ve)Qa?bd zSVsWpZ{@??N%G$<{(b)c+2WA)!S{Ore_#AAx_^n~U&;k(u#Ksc83d(}M!1y|*nt7! zO((E0`1YIwnE^S2fDNn>+|n;@Zf=O7;7{+J-xeJ|-u`hD;_f)~V>3VGu|fpi7SIer zw}*~@70sX~b9<1299;ff!uo|&bS@*3-`*v{9(1AOMw~oRc2SHbf+FhC2d8S!{H~oR zHP8p_$Ma48CfwezAT5JEmoguuu^@~>{BCfe>q6~Jd05|-`wGfPStz1|&cvVo$XKuul&4fb@0a&J+4X!CrQ0-j0pX~8=%gZBqp7k(`>B2?|S<;ugv0TtW zta=?bP6pQ$COUKDx1#q2UvMFiPc4JIuk2N{XY{V$!l`2cF`%J4e~kUaIY7voe#1KO z(UV{q6%`d(teeo_$F^J70Ks~MMtfOR2p z7%sU=9iykYt24P$;L=pm0&?YZdd51R@DWYHU7lFj;!W6yhXjQ*mEcwjk= z)T87YrYHoLe~FwymP*< zVnBo*zgCuZGj1gqfPw+i{s#AmL?GzNIpbSaTXj9*d0(SaXru*OsN-x@#Ul{{VXVnT z8pevp$W|TqZQfSB^X)R&4tg$d;Dd|&Ioy575QRKmUigXCLK&YUHc|*O2?SWwrwEZ8j97+NU~?V#aK_PW!Rso;o?jl9UZoaz>f zFRI?!cYLt-=lU2GS)#+m+2{hUsCNL{GLZGj+Y{3VICC4g=ccMUxah4#HBAe5dG)JV z*7TZyte2P_i<9i&IT1=yjx;?#ahJ-DCs7Nn9)@ub0tW{wXC{@22z9Cq7kI7!Op`6v zz8boP`4c@Y7$d@oQfIGvi9GiOcpKp#Bi7@Sgz*bCEvcm=CgCNZ?GkInoOf8+F@}5E zBI^t$koDI{>uF$-1+=sTv;;`#Yk(&!wI7AaGetH%_a((!roo!rn%FEi6OJ9hm+pV= z*6G!@_v|HGR_Bw%M-KSi#|fIFsmU`<^`ZyX6Y`ikA6ebGpN=eyKA0qb8b>yv&g~F* zvB0YLR_*{fA{T|l;zdFccJD-}D0ketL|h&o{^Q4mEA}n1d8s|3)L%8}7^*ZCOTy77 z_woYi*>yK;jjwWBJC_!DL7#1b*ipEgV@hmr+retxL8|KJn5&?Uc#{hOq&Wt{nZMm z$>!fc4fzA{X&c!VHRtp)rl2ZT6YmOTDx!n)oV+~Z9lAW8qT^OkeW^s5uqJOMb>lD{ zq8b(r)JS%Ah2ov`WX5fI6N9pBvzSGetlmMlhXJ@f?5FvO%33ci@ho$7&6l|p`(ISg z^=)Zu6|$YTuw3rEEHIU)^H_{5Y)AOoUV$8^Q_nlhQx%tVd~Z3^rV*Q&IL9AM6oh)en z-~o$pcJ>7fb9>^Go@exlIYlFazAS-q9sRv_~op$paR)0ty`S z<7Mkpi^vk#uXZ`SVbQc8sXPCOPZ$zcEs1s}u)w^Q zoLD-I8T;5$lq6Az4T%rUJ(CYNFAh6}hbQsH+#@c7=DE|)9SZL&-*k=JO0sl15Oi14 z+dGSrB&g6uDbrDJhc1(S8dV$D$W9eU`>H6;-1>AT(Q59gNOi&!iLec!7V0gA!lA5c zWpcbjl?VDC()haC7R#7|s85z;2|D4^A877YkBEOkjx^_%qUFk~AS8o8UvJRb z1dOydr2}vkRizZ`nE=#JHEc8sD*ae1U0!eI)3dmOcpc+g(LS%Og&M5U=P!@K*_tvmDJDm9idTsavTO5HZr6@7+n~7PE^Uu<? z{R=8!khi<8!540&R`1^F#dWOq(Cl}-*(W) zWSJOaX?UIAxG5S}kaCR>{AARs+c^kx_NHEs3_FdCyqGF6B#!bapvl!iPl4@9rSTQB z7W~ChWobjPd$|Y1;_Xq^OAK}XB!95z8PFlIYA8?N`_BC+BY-KQ}n;XN>UI$e`65d6h8wwRD|h5Am|EusFzZ`5r3w z%PG7n9JxKEGn!|m?om3--A1SI^Hp!X3cmVjuqEXnIwc+P9^}$2+(I6s_OMLXYf<@wT z{#78=nWQ?^{e;l4&!1u}7TlV;|K z!RS)7lC>s5=2%arYkBk1^4VUe=?xRX%k z@v^BKLJEH#uw5-1*^APJHBzO~oT^gkqAonoH%E*~0`_S_TsD;~FS(TWG>YWwZEOUa z6!anm$yXmvJJDZZPKRmm5d;djB@1eDY>E>j6TDA)fQGK2Yi#j~~7Ixx_;+w{|o zS0i?|LfaExx}(cu*RbaRuB`h4m$kHTqeO+lRJo{+LK*lHaNi~EBV_sqe`WXUOtv8T z?0uhTM7;$=?Q0M=Lsmy(wp>%jo);I>ryNPTXt7UXS02l-nAXuDCdBaRT`VkPs-7Of zg460Rs46oaze$1>N3GiF#8mR#xM=79(&%Df)NbF*2=7JtxxqW4!})Pf@Xrr&%Hna`I2GM*(QP90DBIXDt)O{qfEdOcE2{ zh%8z4I@*>W9~;3mm7|(D4PV$Rm&lG=Y7Za9VnZ)`tQ*hrTJP6JWEqql?B%~33C^gr z2D-9T^jeL9Qf%jNm83hDTR6A03~*bnQWs5QpFXa2h->B_x#ZPia-ONCbp(JtvXjiy zCc0`<5|hDv;rZnKcpzf1~RbNv;o}rVpFp!N+OuqSGs;t=S5%hs%IMeN3<+pG}HhM@CN@ z|A@pN5$EB&1QaHtKfF~pFYV&JZ%W+Hh?kOB_>YcJ6*bNynl-N*M~l6L302vOk|we& zW@;{~Z7mq8V5exTXJ9v|J~DV#Ca&wJcC6O!7#%zI0Yrkj_O1fo2>PRH^`(9|g-?OM zdL83TqDN_b?`>0gG2PKcL|F}(sC(Xe&>=~8Gfu_$n}F8G#$zPP{lVJpzBZBGKr5GN zb9?-Foaa`qo9=7n2{)LP@2%y}ZbTIY#o@-q`xMkJlB08@;5ar%PeWRrNsjO_^=9c; zu1i4yI)~+EH>xCM*p=lPYEW;K)@OhM3VaiZNe3fXeL5bmq;z}4)LuGoK8||rOS?iZ zeN2C`L*FvnZi>2A?#G;r?rPj?_l#g@fsj2M$F8<`7Ydu{c~x=`CN?9Tul9BGW!@$E znZy~xN?SZ%@cW#=lH_*t=6eP9*pvz#JM7OnNalmas0?6Dm;#p7yZk?7_1LEi!Ot)T zv*L=uZ&g6!%XeBx>l1#=rl{6p4A+P+_ZwpmRZ_=n!Db9_Om2r6HhMV54tXy?rz7Yp zxm|+RKvmsSyqYt3%1c*bCi8=6e3&Zi@u2u&h%wyv@f zts&^c2JlBqx`)-}ErA$@=^}3g;Oh)fq^RJF>0bttFPg%K8xA?@lr%)mTOA&lGCmg> z3Yjht;o{{berBm@sn*fVUomm&<{6*j;w~A%!|;JKU`!p;xp|Vw05&UYroDuh-ots@ z;_E^ShmmeEN5yvMH-`r1k2N0_LBj@kQZNQ{1y#c(?!8It?j^9FtI&6`*$I0K+1_sN z))q8>wI4E+u+u+JojT&J62$8^kH{vQa(ELtSR-mXz0cQuGO#f=xNXUl;TY*zGqrY5 z!@V(d?a@@aw*+;Xw~AP|h_v<~l^aI3&O4)rU>7^{eJGqe^RA3eH}$Tbsq@+=-I^70 zT0`AS$i@=Ys+du1Il|A_R;l!|>DXsV5#`*C_j(^EuB-(MAEJ%{rq|!r@PR@yn#)t4=vSkUC4#9 z$HU0%d$~P&es44L{f>tBlh0M>m|8`%6}Ee)^SrLxOZ_#JkGs}*~S&v^wMv46MYVx*-vEalbY4o{L75hq74$|R|K zVfi8;1DCu&$E9E3;+|g>zx+nO;p;KtxG5f6e|>Td_V5 z!Mt~dm@)z9)oM;xIFLcInd<0CqGfw{SNMX+K;2l;$9tDJm7uRoziC$da&zTpPmTFM zv?^|UYBx7fC@1$Xe3fPEeTMij)|es|;xP^z z!KoIf()(oLm`E^=eQkP+lM=-fM1sd|JkZJu zby{mhBI92SywRA?>5OY^EqPcQzgOp}Gy_07&2NlIwdJ^xn%-{!>A@?Z4)nIy$~+3n zs%hDeGf@YyVeGKtYt7Jv1_O|M?fJ$_G~$pt>`a~JKx1}7kIslNTTZQ=^`#0vZOpmm zERVCrggNPG$n9#dg4aRBr2(rZwz72HeWOO}%%LGUsJxQ_J7p}60X#8l7*A|l)>=WP zsk1>>*xV61u8Zd4=r#$FE;l1&H<}>nSe(`}52-jksjSH;?`IpQQpL6aCc_B}qQ=Ul zRu)qfRYACld=&-EZjUeDG} zViG6=@~@zH{9RsB%0KkzX}w=9K>KiYW8HN6N$rE3M@JV3?0zdwP=a~MN*xT{#^^rq z7Lt3XaQ=GK0{eptl#4jIO@Vsb%q_&S@*Q$#sryPk*x$hTk1pfOVECR_CGJakVSRhb zPW27UPX3?`mlf->vc&W3noe#~zj5c(Q!Uc_eNW@yF8#Dqu0`i*S-xeHdD2D0`<;_j z*<;nq2Wg6AWWRyaZ3FtR%`^}jknuktw8R6^N^3G@&2`v22D1>hJBjRzeIbovvOmYIwC}S~Z!J#bp?6uM3;Rm{XiYg2_iZ&Z-Bg@YrK8M;lHaX78(q z^%?tMW<;9O=s3ecTH8)IJJojKyEau@_Td@b!kX!h5p5Nmi{IVLU1)kg~gQ3fM!05R7#qc`+w2=57-A%xIhILz= z+?xd-XgEqJGquB{q_%Ub52%5Te&-!E#glBU3;5qW-+n{$i`LfCYdxu|dKT`Na&I5S zXY4)qTC8a59CY73eJ)wO86ROH8@^9bJG<6=IeL_+wZztPhdSrh6?~y&aT}s_PS09J zbV-V;1>0nJqe^;9_c+3jXfj0S(JwWbLIk?ddR=P^_5*gtQHa^x!NF?x&R%y%%Rf{y zN`8-I%4&VdOD!A4?0n)B7`D+VmSx!D8|UyV}` zmCj$K7k$k7HHIh**gu}QE1^pq_jFCdyg|E?zopQag!UD#%)8mqWGPdYgaHO7_w$cB zlIGM&#@VaqkMvr!7D@X(B1hsgo3}_ZmFWwarA-s7T(1I+L{qGQ{rauSL0P?*h016o zuj$OU-MO4|>&O(Fj1DltMgJRmxB-aMxecrZ#OTo6P^^t6ChqO`n5wEcN~H(nZTSYRT@OJftSkD5q9m2^X-A6rbI zCkoG`vl}_P#d-TbKa}GT?d%sMzbVXFBqR0FF;aBnR9aYrn75&!@W80}lMpBA89G7r z2?soKcmi#)bYx}GxLMT5)v!kdBBGy|9CMr@ZnG2|+T39Zl^@D`uzF4Ac#y{POygC~ zs?|8n-C|bO`!;Sd(i9C3nmyuTz1l6=rf5XVIq<8~5T(6HMdVf-3*m6D2|HMpS(OKR#~E1c3`I@VTo z7GLvtw(9Zpw00ec730mQ+ndaYm75O5gsld(wipdR6Jd=i4S?79JD63YaE+L6xFAFz zF{z2bqEWGi{MnQVwv`vRKK8bk6&oT7uS@bLKe(yn!c}z{S*gv46*uV>cXXHiGcV&?Uys344f0xE;xssCP{jF<=36uOH=J9{(@eeDZ_6*09$*xqdPZ2 zHAE!t(VMG%X5G5w%6QZ6c278-`=>rDN zeIHYk7It4#d3Mmsf9uo?1`SBT&mQ;pw}Ek9b@1(4SqQw z$IwbSKTUm%@o;=a^K`c5r?)1)@q}~H`OfN>$wib>Q+R_1sd4Nn4V0E)*v*t;@r_n- zYM&}05rK%{P1ZX;?m5QVTbwbGu%c`uVJb=8#*$&+Kuv5cN!?2`>uG-2n9szK6TNZ~ zz)2$&xgVvHh+eQ#eaaduG8?7oH{3>L$OowIO+zPUIz9(m4=n2z(PyH&tasrF$2=Drj?Ja0W%64WV)mS zK_@%I_X|#UDCUz+0;m)zxQx9%JTrem2m-NM`}Izl+$<}kq<+!Q5RFGhOmO!Sr5#*P zk&@FaI)a+0fi4l+V>l#cLIgXoYdkq)74kUFq6uq|ZH_|TxN859=>`uDcZf=TKme3f z&&2yPt)Cmun=+mO3h5JJ&v+RJ z8k8-FA1e#jr9N^R9Qv8O5RI2faeanHI*|Xa2|&PZK1Xf{?rE0l8ldfc%$j2rwQQ%K zxoo$9*}M4G-yM~IAfz(FJz6g#f@(1JG^EDmQ?uBc8fJoMyA2@%Iet@)Ouu1!Zs?44 zQxtrK#AQ#_z9Dg_@N8p@IU2Xz!2=mTqspE7LWNHhR3+H8^c9lVLf48+xn5e zxlGPBXIJ2`rGzUm!g;WTEz|cp{IGq+S8k0@lJ98ul~7wvCGcv2(l))x{b&MqC9Qc9 z0~acCl#5D?YUDLK0#E?X&O-4?JpBg@r~w7tt;7sgQSC6#%WMATy1MrmN!JIqKw_6k zwZREw70aXf9M@W1zG|)c_Bh*)i(Ded$uONGpE=Y*K8(9g+C`Bf zt{RX57sb5pM6n2~w=6pl5Od_|!V{hCNgx4Alc|SN&gJ~Pq?%qM`k8S3W0dRJy;^Z$ zTUF~_xdLm&6-{c~BTox0yz@kv!-ez3>Q0U?<-{3N=h^$4?K`;Eb8*7aMC`5H@qvB zz*{veCs;y|a-8JECR_aMqeZ5iT9>aUPLB7^n|%x^=nE8+Fh1_}U0(L}T{c_ldwZC8 z>+5@)cz6R^fLwKroNP@n{#8)Ual!jb71XQh?fvO>bis3fbHqc`skO7S+3d9|eds~~V!_-Jr^rff`!MM{ zhf``%%6E1((xo4~9l`Pd#lu*xV}rw>UU7FfX!igV+^MlwTDC_Nkr@)(F|zw@B==x9 zSE~#u@GG9i3BkTdjw+ol)CRN%tV{h!s@Wom+ni*tp(hFA%Ke2>CrEkmaCA5{X#4Y`Z=WP>kb3x zEcSuHFMMFW?M9tVOG)b+1-!ZS3(AD#~%Wl1v?S#r# z97N`-1Ja#NYv!kE1jF@-4tYjfZ72m1xEu{CY_`XmAl~~QP!1oFc+oF@fI*Q+T#3{4 zN$3L}#>?wUZi7TV&Ak31{A`ixRs5&$#WSdL)F-Rds?cyGd7h~}9^o^KR#PSN&t`$- zMdPv!oblBPa%vZ^-{aUu^D&L-;2+A~_pe#BI0TG*=AFdLAecFa;b0|>vbwT#D4Ja$ zo%D6PK(o$gc=^yW6iBQPn#I35A(b^uh6j)+puiG@<6+QhkyVMU!_w(HEj(UYKBu?NM8uEXPidy+dd&TrW#@Cys$t~OHdoT4ZTWI)j<;S5sy$JaB~ugt9B^Tykux4; zRy!t4D~pI?6;kKxL~HF;_$$0VE~7AQtDZH&&1hZoQ9HprYZ`Bw-`qSVE_{8zos++K zlaL)vD*0uMF?GrQg>Xe;U!{XCd06F3yU29A#s`#ovz2yZ1TA9pnm;+>qf+hoAdlr zRbDM0_8`b|aVzwd*8oKg9V<~XPr1j@LJSRy*>PkFIJfdjo|9&Y&LY5V&mjg2rXjduxycV-0-$?c8v2o zg?K|bH$7oU6X1k~aVhBN+?(G!gJA{r0AIPj+uHRb-m_Um<%;K9(0UabY{j6gd0&wA zi`1$SM4AAp;Isl32_FM9UZ(s=k>l7K@jYaVowlp-p|ODM9#rpVuW1SjaK83+Qs|P` z)iFi06$-b?kisoX0e zHT&iL_W_F6R1f<&p7$O4)7pf^f4~vo*Zo{)G>f5Q*U)0Kl{tmsy2aDd;%)ExIjQZ2 z;es+P>e3*!dDaM3x#ePS6rc`m7UzTS!yUUpM~d!@-eDO`2CtNvw!&nlgIbTH>@2X# zu@OAKV?Hu#(^NILV0!%3>B!KQ)rOcMZa28BIZlf+@r0$PCR@n1MTD2{H9?2&%W!}e+fV!x0F|gc`z6QJ}^s-m3!orTm#ML%kK&hHLM+Ni6O=r@qB15$ClN- z+`82AoGaRjRzbq4ahTq+avw%W9p})&@ z?dO!pr}7OdsotFdC=?`d2DqBOJmepYaJuE#)`v*z~PqDPU2M=Kjwe!sgGg7DRPq=2kjs?Sq26mQo z1~b|9z%Q^=4WgG3BTzonlfCe`4pwHJ%1P$7_eaW*3XHLTY$?;bP4@Ep*`sx_hVKl$>N zXt0R;?Y-8F@=2WK1Kl#b&E2Tib8~K&eGOx;>iN8Pt174Bw=_t5A1zq+JVp zPFkT(HZ=BpVVz72dow8;kZzU;?~4j4L~WnhW)%ebwW(o9066YFG`EhNvC1n-)XlCT zI}M;w^8tJt!U&KwMn=cOXg@W2t2bc(Ff%j8HuLf^(?eK}S-H*o4TAGa?(*_UqsnJiLx_ zdhx!X)-8BxJs`0q#b>!;{A}P^Rwu<(AcL=O?VbkHu-5^`j949tM2$nZd^q;aO;B6$ z#qLKgsCQPxv%J(32(q4hfhLS__sqTUV4<4>q0_sWX!Y@4um>7cFSA4$5kl@cYYdS_ zV|gJGx6@rdV$es>>!fN861k-FiE_V;w?xptkN@p7?*+F^CPfyB?HKfCjFZHllM9ZS>z{L9Y;t8z* z%xQReGpv|EP@jmp2-lfBTJ7R#pOnZ{6i`zykZ>bE_Q|#|4v$XYAbDh>o9augGo?!= z7N=|soe-hUM~QY=IORyl*6Q64L3YT&kA#rey6f(p5vHTiE>Y8y2Hj)KUZnU~{!GOz zxgA()^mxk+Z&2Mo2u-^i%Zk4fe#Jf|>*a(#wwnFDE%mNS)h5@6$WOJq;#fYLSu{TExh0f(fwFb}Sqza;UvXhgwM6r_O)<&OFml_If zxId5+>J)GaX_F=B0k0g8B|hj8pm4}wA-(!T?iUgC{ZJ)5Zy0uLF~yuPrhwj0#$dT4`iJ}|(m-;*tup$kix)-v#c7W~|Kum4{*$bxt>S{%-qiiI z2ioXdP1A3AIQ&V{G4~&$2zW+2ml`QdUEzTOR~wL$K9F2i`{U*_`iGv19}ZeqlZg)I z;@BBcQqbWRf-@ax~-(d_1HNN68q>goOC% z=}?GQMhAS}gYIO%k`^?`K`rKakbML^!LVn88S$VIw`VE*W*Opi37k$RSQ`Vh9_M&v zT0zTl`IYC*qG7CM=t<*CjV?TT1nFHJ8?4Nv=3I4!cKeSa+IsV$k+a&BPyF{Z-D{iL z{EKtWOTxWYv{^sXuW>}HN>R|HvH3hGbGZ~{>a;yys=AVK!s7aPEr;(%X@_*QphF(? zIK9ih+VK8kX(MRw>ipgDlqgc1R|~jgq}THuL;{uK46 zy%#$;DVG+{o_Nb?D8|Yz-$2*li5&jsF4QmIBKmPt^FRGR_XQLb)B)7NZ)E-Q#g!kj zm|6b^H%Ju+Z2}k{H6LNk1iV^_G3=lVi_t_ULvg24-btB4tiiMz1O;DugN@>fzCeH2 z9@-uH6qDpw<+1!FtoSma<3R}?a;J*BnX_P@Y=u4lW>g5l+0Z9sv)p76Sa3`6D?50S z8soayj1#=V*hO(ld2|@sgFwy}Lms$I z&M>jiIZYNx6q6Zgh|IYWxI)3%K5;=YjaRQvpR;cwR4nA^Scej<>4I=5(Je-p00p)A zz|$b3q4UXoEtp;#L)oBQw94Raf)kwS2+jEq!T0fssNBEh`l!j$?t`+WyyXiTwJ1{Uz#JRs@so*TdV}^tL(m=o0;!dcz*onD2P1a>OMK$wW1V zX(jsOyRjsunOlXqR_H~hL2JPvK6f$7Q^O`4P=LV{;8k)VLUL932 z!0ldNJT!rBbFB-_$TJl{72Hd(xjXM$-z7?ZlgRZ+e=VAxHj`F3W1D+1lL3}r=UHwp z=Alz;p$lEr5^djDU&CO$Z8w1OrYZiKaO1K17RIjp;X=|kA>a8fwu&g{Wo!ORgeh*z z<|cPJuX1ix~7m$TS<531fNX?aATcAwXlVpm{pT_6icbdcK)B{>|@e`1hT zv{(~~4yqjAQo}J`juR8*bt{9HNzSTUt85@>r>s|XzXxM8Q6WJUH}N113B39yT?U@q z(iJ^J$4>jx-hjmfe5C9o{1JQ-lIuxg9w;1X64}C7)y5h(Xi=4LV1oGl!52}_Bs-!c zeMWqxkJX2@40X5J()eY@;}C#gRy+F3aEZ879;nT}ASAdi)KU=gjW#JS!8W#=V3hUZo=Nj&=AaGn-(p zwd2)V`~sC7o4i`_a4PltNkd*LQLJzL!i!!+(&7XfCrZx1L#%`_5F5%9JtZ4VLu56?5qUZMrsQG0>Y(w}0+ly-zV z=F>7)D7fv&;J(=EO&|I;Tl!fv!DDyI&x1EkxbDHib>vTD%iKr`qKBHt%)~)6BS}@_ z_qEvZ``yE1Cuw+hoGD*-X1tE8Xi|@+GJxBz8z9r}NI@nEELBYsgfp`GnCqQL+WUIe z!7N&2vxu~qkK0Wi5O8Hj5X}6+oP*52ysD62kIp#j#QKXWNh1P?P(FL%;3Wbw5wVhi zf3FZVi~CR!WB>9pwe-QaK!h=F-og$FqAW!N=I94t>(wm9iGGduaU$qt`{orAJC9s! z5&3Z7cL+N&trsoG;t{Ir_WRf+?(>BRuClxKBq@;?x91nAP`Onro9(-)KZhqef-JDD zU=Y_!3Rz&PxPvA7{p<43US2HFk-D;jFx$2Qd9hPDwNAKjg6ES3A@!Ba9W|$Fez6NH z&iuUenCRE}SV&r46~1JogCnR`3G7yKk@meK6|Ia<8+)34^u##oNqM4|ke<40d-`aT ztVLXv%Ah|FKJVDtxc0nCr^~7c>LeNIuE>;k5SHcc@b$+>nsHLZLXDVWxwKbC-@PoC z$x_UIw%bfQ0N;w<*Oz6SlZwr?i678?;j_jZ&N$CEIbFsIwOeT-qW9 z2*lv>HycjbB&%?z>PFv2lqDNPzA#L&h*r|l#ofLEUNb*NIAS@cJz{Ab0`($*RHi8c{H%e=MrlZEDUP-JN|78$Uxp0I z-x54jtJYifmkIyGAgWfQ7oj`h!{i}z(QPJi(Y=v+5C-dH)O! zxQK{m+(BIY$h7wBRcNO9ISy&x@eG>rd6F`fVMh{aUm#y30HZZW(91+?KFuS4(-&JH z|Hl1(%5@@wi1fE`IAAuK0EL%|b@dlEElDt&G%3>>Bv%ZdX27scJhiWPu-r1RCWS|2 zjKZJihk0k#$?LS}X8y9cs-%WI1BkxF?HBm03kz2em*FjA7gz~SNE$jNDlhatdW!KE zegnoAgDbGn1Qny3FT_xgGP%upz{a#j^TG4iX>+hg-nXek4vgLam8WGW&dGjHfqP774}4 z)j2;9k2)bJNb*(fVZ}2C_XS+viUnH|(ZLHDzHO`4X4e(p7}-mIJtH4p$GA4Nh4ry{ zTdNaaH|h~fq9A@4(MuiysL(9$Z&GjfK^JTn7%iG(!&!{nrC9lPY#z*lkM-JJ4b$?! zv7R#qVybhmfbO}aAr@7V-~Vn1X=edjIn=c08)jx^X69^|nVFfB4Kv&%8|Do&Y?2K# zGcz+YGh^^{kDY`j#THmR@PqVwk`^9B8N&qb4kl@hojjj8R*geQ9o9{!$^C+#M zjHzC?4#Wsk>a*@o?igF>8{D&u+CW|h>&uRJpv8ya*VWT4t%DG0-~g_M8^+n%7_C9b z04pY8>9+bM({hMqs*}DZRrwJ767q?5(LJvXccgiRjr=U(5dONnezUsX;>8{!9tL8Duj%n^$rZ{|N$h!U-k+r#NDZZ7dd&Tm>t|$33lLka<=gG)Db%Vn zJN@O&;ez{0oN#$rbfFzIGBx@%0JzK$u zlWq?>k~xJkFM`xR8%dILC-e8Q#U&X$>#m#}i7N7nCG!locRDt+o;+^7t`qrcf@P`e z*|SG>r?o1Y)`r%OWsS-HMAi*Yp(id9naXRA;Skri^$qxhn2!1nr4PS{?{#=C4qrDZ z#o(ylS8t%1K1)wIf!f}-{_e6p-73S63&FR(XWIAC+H#|2ZJpOuE51n+9wK0ee4{$B zhpb;n&D79x|3yUhzrc+CM?}W*|0iDlPr?8PH_!irczN7mof%pD`7QEPM5s=RL8X8K zA{cNk+!MtvjNf1@iQf)tA~R#U+szavN+muUYOgotbR{sv{PE=Cxstw1K(u#_(y_;1 zp&E|#IPVPKSA)~`80`KUWHqxwgb<S`mV;fxH=Wd5)@<3*9{Ty$Q)~DZjD}q73cb>~8!!0Vxr5k_|K_;t5BROygFC@2(#nT7 z?u0ya>y`6BcW1?+O4>_v$J_hMn0~3|jbVYc^>?brif;h?2S?<88vTE# z?fmbGXr6!L|B!~r_OW)tU-epf-a0>j+!SEj{An~JFrNmN*2@Ec+-VdIB$f)+10`>X z+9d>q&S@Vv^}28|moCAtYz(uLy^C9kP>;CeR#=rz!Lt=VqE#4^q36>uBfx8?{c*5T zR@az_KiWi3GZp8lQtbl?Xth<}OBdYw-16wS_GSwN2n9h28QI;ZvPky&al(l8jS3P zEU?TCfZgK#dF96+#~)Z02k1#pb#QR_SQ_N|MmIX^uKjq7d2b#`jWGnIRe=QtnC`z7 z$N74E{shcZfdHpB+Psb(j@R=*@ei~cU9{dlBW5?ZP5Xoo#9zeUt zu_7zRX!ZmPdHty@?oaj~Z$I~vIaRO5w&s;Jt`_#YQdLS; z$H5{iu&r~T|9ry1{)u+2-wk^3Rmn({J{#e;##HqyobI{up_8BWV}hREk9yT*`A|=P zHwJ#4aST|WyC`LUDxZtmv;EGq9wcKg+w(on__;Hkl`;E5>fY&q3WA)f#*iBk25NBI z)V2Ocdzgj3v$qc?Ki$jX_-^byukU;qEW(VMSJxY4H`U{1k$_K^_Z25UJSUpys(K`2 z+7_Ze%c24E&!YM0dRhkSv_LC?5x0BGrC^a?oG!Ydc{|PPNPM~Hrj;Rm|E}uaa1Y$l zT6M_hn#wWs83(`F*3$PuBjfKDT;3uSa;&K6JOpbfP??a?CK-RB2a~lromk7%id;M1 zvH{`$Wz$+pJmx4UhMY1O^Y29PWH>h?+kV8@tQbR2&1-y=txo3>zMWw5ULBOO_QNV< z*!%B^<|0h{GOhkr`+>-<1Kl}G6oB{*9_^fx!#haM4>XJ)Vn|KabXd)v0QAN^j4o#f zkF%f0%l@Je2!{oh?VCBKxw~(Doc?u~7kB$(I=^J$)=x&)ya$qs8g>jf*5Gw5m$k+p z_3}3bS@kUOcdH>%U4(u3(l^=~k#njb*6~EVI-4-qAWg%2Hn{YVq)%TgoLsXTp<7i( zQewl48eWsT2@SNWLyHbFC7WYe*X(PScI-@5)+F>`2!ch|q2X+(p;C`TDALx;yg+k) z*@A13LH;ChF_~Zhhx311Te5KKeePenTT9tEe3CeE468IEI|y#w;Dq`aD#8~NcK+4J z$*;}Fs%NOP>X2Q<@T2}nGX7mxSMuW$Q*&!&!|0VPR8MK;OanqPw&D3f>!!JHY3RtC zk($1taH#*$sCQcjn)4&d*p}iz#vj@;q^8m3i~qdd+uvTc6I%~{s>;4uM;GtwNMdT+ z?zm|!6D2qfetFi3Ang333jQ<_dBWYo`rhT2ERtVK4WZsIxD`V0j*GEQ?u8C?Yh|Soo!DhO9l;}$;|qx)V(6Xo)86=QrB01A&BOAl z@7GXq>9ukI-4G%kLo+h^8eXc1UZm4#%4aw>W6ki9M$K;cbmg*0mzn*l?$^cAn&mDJ zS|FXHV50#XY}YB#BbkchK`^LjpYw0qYoe-Q1nUS6_*WIzl>YO^%j1DL2Wi*Miouv- zE43d$Hrrj7>IiIoUmD+5OcM`EARD}=>(K|UXcw|{Xgm{v;XhkSZxo4rd=l-zRsXs9 zHLSg?7iZ+mbulGYkVI20;@BzEvE`{k(zb9?P^?57%4I`xJmvaV9CC^gO|QCEeArK_ zVFMeWVxr1&*(Gz-qFB5i%kG{Z{{?bMe*w7Yo)dg9yQ2lHxdb z4|9)g{jpHZ^~NY?rhK|yzknp^tI3oRN>7VQC1_Zascm@Y(d~_!{`$tsN$jS$AuRzE zS*W3u6DxqEXjdu#N(4HV5M zlRGUAY2g(7`r*}mO2$J@XH(I4^JozxhbZnMi?!Ns{}(lK%9)5XJt`;uy9h0XEa=v| zqa{o<`}OHlyYO;zx`T0d>QoESTK08)3WiXk($JcTlHUc1)a`9U0Od9_I+}bvR#BZd zy@@p$>faMe)P9Uu&9EtRy4i>|Zo`$P*td?762L>JK`F6&YZl~v9>T?Aw1+wjuXhQR4M)JXH~jkV^O z0heZwY22N;u1bbts_*od0JuDe1Q_Vc%3-)Zq@}QPUERc|EsnYUVjZ=O;<~1Li_pzW z;_2`){(8eShk|Gj36ZXc-f0hW1v7;UVL7#droF8>_I^M5XEVwaA3q<@c6|F&hwWh* z&1FGs7mBd5X*PM(g1KYSE(Z&;08Y|XWygS z?`V3p@MP&KMJ|+k>s8B2 z-*;lw?`|o%dfb|tYkFd`jcr>tq&8@dOr`HkD-|)HR6#9bOzX|9z((;|&+nE$lrk+<+`&ECTh8*YuU}smD&8+;tjo&77px1(5CAf_(~I0MKRRuRKI_cvlok8P=&&cQz)m?d zl*xi?(#C7j%6cfL$RgKTh0V}FB*~mi*O<$iNVJ@Mc8xoFP8|;CbzW2=$5;15D$F7I zUTOe{RzKCvreZcEwdq72)>4`n8P%Br?PW+|DO8vqb-T5!m5!MlP=teZEaBCqRw1_DcTHvneqW=Pf1oUeUWF$A>VYZ!vrBsvy>aPCAE3-D`Pm>$l{0bwO>4*p%q{vVo)$tKfMkS%Vobpmttyi#2*+V+zf{ccCVp2To+wBF<6OTL3 z34E&eOT)q0ohN2Mc$PgCDekTiFs=$kiP)g?dic?fx$NiWMZcJqV?#bTl#Iuec&p=a zJk&8ik8|7B?n1HGL@-KPLTK8^XI_+F9`-<+I9c1!yXf0^X5=BHf?!fcN_!4{nj)ln zf|86HuI^Jtj{B`A*(6#KoR}zQ<7-7-ed;I5{im#BuQelryJCcgfyRZs^2{qs16A9A znXW0BN-fo%)sPsnl(%D4nae6pQ+0L2c%^kkSE_M(R_{@mY>P%)`aj^bu2kxh*}l<8PuWFgy=*GPN1hDC42 z;TG|-spHwSW0s2CqdLht?O@`_ZUlan50RjqCWRDnTU^bfdqdC+%$N_5xTw6F%h?+o zc|I^%^~u>s@XXVs^2Aa{?C$-P9`)~Xq!m_)AifvcWMPFsiOY?*;*8$tG#XMX1$HWFn{`{< zvEA3HbBKw09d~T9Sv_s$GR-2eJR15pOSSw{s=UbooT4_5_SJH3ya1GJ9b+2AU>$O} zS)x=LO%Pf=z@(wS8_F=0e!&PAu^)6J=-sJJoj0~PU`^>|A9lkM-x3 ze#A5_j3!I8>Rj1YNgRD&rBGaOTKZZxq?8LqZrHFnfNbYtO_SC4Ymkt#A8u%9)5Z}e0e%p2qfW+sH3MiOQmv{_ZAA z1erv99UNtoEKdxY*XoWvpCmMuEsyRl#bcIzDb3O5Nw&w1?6S;AekL7M)+$y4zxC(wE<;AlX)Vfp7ZtKd#$PJD ze}9%(7cC?gL}Ibay2vTF=N7d8GtxQ@IA+Ax-)H3T>XU$B1T)Ad*js%*jzc-uLv6ci z1r6FNWSsJOhd8gcz3aoDZEhN>F7Dc@Cj|Yxz`8+a-G&PKrn`b5J*Yplb>bZ5z7n$W zhJ$D3B98}AJO-&<1^Z&G!V|qw%Vj@V?Bf^A)^^ZfArL9~u6u}jHG6~TA4E|kbZKUf zy*j7wA6yy#tViy(iBSmv2=ie=%#dPnK!DhfDFVPZrjZUuQnM#Iy$s403>uPv2f=q@ zZA=wP$InD*wyHi$G4<3IZ$A?g6AM@Nxv1+NkDYNPps*HDhpsmUod^l%_L9p?;}%1x01jE;B=NXQSCa!moP+V%%o|a=ow{ zfttH&wmnJ)i&quSSeRb&x?=dvCse3_s88)>eSV%FKiE{YEBHkkhM(0g@FdqtWT~H( zb)K;6C1;22dMMfGy?=|B*Ve}lm6hNJx_DfUZ3n0 z6_!uDpbR9N;A$nr6RXS%h%*mM7sONq{^49pRcbXG>s?TR=-2B9LgPEuyo4^iDwu&F zV~0^z^oflBu=tyluIxsHJB0{u{{FsrsQO1Oq(n%{oRy*R(V(1W%#}kmPNcTTQ}0LZ za8)ZV%B6lS(Q~KCj~~j}i5#Iz@v?tMic4$|+RHg2;MFKhTcXp;)COU|WPg!mRe3^X z;$X#1yoHgZwOR1c->53PzWw8Av#|k>T*|Q@%rR(`E^!vO{zQ#_7UIltNu9uHak>g~ ze9<&QQ)UsHYA?s9tGNA4Q4+^WPZ@E4f8K#hupqKqwU$kLHug6W;W}26sg3oxZ3rO_ zOYd|TkL^y}xcwx0uB3CGu%p^GYauXI32@R6m?(V{vNxHG|>s+QE@yiaZuLQ zgsd-eF|u(@e$AekHuY(T?q?n@fE1r+OB$^kxIb*Qm}h8@Tm4o0`9s^;$A_&hY8&IC zbfAwun0N74hPrzP;0UsVh~|(@E{?j1gH(EW;y`xf&{xq%MfJpfs)t9o4_`ncNjlT2 zJJ1{sF4@T{Va+5#i6RKDgZN}rLnJZ}0}1)9z`|61I7>_|)68jUiKSPIoq^(RstfT) zU^59A0ype+i0uTcL1^FM zIz@WW$|@L$(iiLmhchC-o5e(B7Bi-)EaR&7mxRvOPH0x+gPWPWaIN{kM!n&aG+&)ks^G7bBf@c%HVNPt-tVeapKv2Rhva-f*~IEoVSuy z)lwT^c-k_^u*6$K3L zShQG%JE~lCja-y>i?<@A6WC$Z!38Res8T<$;B@LNRUT#K(wKt~acyu=_B;1)fNxqT z8GVMU=l8*mmY1E23-;T4X8+`{-0vKLk&fW16@PyA{+asy*Q;GU3+5|t!XxX^L^lQ+ z5)^0Pve}Q23(!g10cSIFF;#=d24(sDacX%C2IZfSiR9vBn%~Q2`sL5ZY`?82?%jDt zbqgbeql(y8N$zL}qlm~GtZd8KD<}j_l+`;U7<7?21VH#lva9=_cP~#o95AqhC=YaG z++0RfT}~g!<*tAU+Zd7-H4*K%`;5oT%hc+af5^H_;-ImZ+E~##Iuc}x@ddE8dn$Ft zrfgw9o4svV6Uru66^(#;Fao(Hp{#^;+rQCcRmmqy4`akEx|y6KuOHY7xv$MwhUwN0 z%fd(r8@MoxYs!)TjyB8~9iQI}D___Li}$ldBDm-G{L9MzM71V3(jOLHm1vkt+cl-( zSN0OS6g!ZcJcxXe21ZjMkByxWH&V+K5~m6=Ovs}Iy}IG(GxFv7_NFm@o>w#~&m0OW zVkAk6qXldfmy8&{i_+9@A5h3Nc4WYukk6Agw6EVQg@zafSfnvU7_jjo*5Y3D^w@KP zhrd>kwUDwNDpj=|gBW3Ygp>$9m+yZ*#+zf&O^ktWEoPe zdskD-ScEgN~_-QO0Y4JLX=XTXV+EYd`~JTKhV>zuh$mO%9cqVO)k>)>Lzv( zVdd`e-(UN8Q!IZZNTaMlE;d|TJUB?W97~8epefnq99#mwcMm!P%hPT~)d+N-7)0T? zZW$C9@e;ZEt7sZIte=FkZ@sj%D?mrxP=56!ZF+!mo5%#$7QHy)0f< z4nDfrF*lqZtWP4;Xv?m;my!J5LgS52l*v=6rKt7-!MjxNrS~7b{q@v;;p^CF7;4Mm zAJ=`ij>U{CBxpqwg82h6HCL!NSWB(WDCaY4vYJ6QE4K-hbNtAee?(i%-BcKF%crBC?z> zT=;^0@z$l%Duom}MGAA&w5Sm<5%q)|vR?hwsI}8>i`5q#Rm~VWmHfwV)ZKh6CwiQ9 z$GpD6ry1ChudUzE_edA_H6`Ofl@fS9Wc9&2s4UP8#o}r&cMSa@{ zXRBdIPnXBa9cCpLl$ohx*spPso~H=A?x}`kP-3e4Xf?tRP(I90b)grxF`p=rSm)$q zt8<$uWuAbgVO^~HhgN%28{7DvdV{K_jI)haxPQ?`_Nt8Z5#t*xGwMs_^(s>(tZ=QM z^(SoOir4ypQnH@W&ZiI@*S9{uaHFb}nbq&s6w(f31PlGD<=S)&|CKM<%^x2=%ItV2 zg@K`iDSt|__QM#uP~sc?3h4lasXC4}?HJu=>dE*(22A?iqqgW=Z7HBuro=U=el0;t zfC`_Lr$~)T2O@F(I$(vv1qLV^xkmzKo+Cp$=gFD-FZ`Wa-fh1z9|?jYa{~O{q46w8 zF-#ZGp3db~=G|D<%;0r0a&J<5{PgY|Ft0DZQXV4A@dUCB4Q-B?8J&ghQgULI{Gl9A zv4}Sjt~cPXx5T_2Vt=kvnBYmt>C~$FAe{>@Q+T(ZU-06xh7su_Xzl*~@TdR9Eqf4b z;vh99jK-GXQ~nxRG=ncPR8gT^be(FK{)^s*;@4_Gk~?~NRJc4->W*s=SsU?D*5G#w zFN?D_2Mr6obfa{=nN7zLvxPSU+izn}d#!@ZH*dLws7N2L)?lXykK=*c9TVC$5f$!&F5s4+RRugAIhw=AioY0HW!nxwfu zEp;mkCF7c|5Ts~ias3>CnD!}1W~LJV$h4vz$7LNfx4yR;f)Sq)`%Qxpbvbfk8&jk! zjv>&La$;2UmieH1U44rqBpE*WaWBO4}ZwZ;R;JiWldbB zen(uXV`fY$q6EQa(UzfAIn>RhWT+eh`@h5ozNiL?d|!Xqe;9rG4Fz3?^uYrB?~jiN zYo2qfw*)s|+ozV1dzJx0F<)*A=g*B(%SHg<<7?h;xIqe^AJnu)->|Gg5D;e2dC)Rk zlUuwe%s7YSN^KnIX4v4kpBd4`jVFfExK=wsO9WnU&Q^$i_@6lb5Z{Q(qt#CM9;xq? z-}Qa+yXSjZFEFFOyTpIchWIL$(FNo?lpD@~UQ#{!f98CJORT>T)Z6ROQu)aasTt+Y3cs&PCx$!Q1=FR zBh1zbqN-2-W|tp7Wd4SnUIZ^T1PSg2RWDe+R8X!5JGz&9q21d3 zx`b+nUN7X_3G=EKfg3D!2X-SQ)d6u8(#j450-&b;o<* zO8jP*Qt#_q4@b8jUoY7922?Ku&j~_7{ITLN+ln9ZoS+4&c?Yute0c{|51fcml58`^ z$hKd;1|}fY{XH*XD|GAYluv5aOYh4*aEAZ+Yjlg zukvJcsJlUQa*Mm-P}b=@{EoVC#<*p+c;f1$lK{1v`G}}#3l62kbd8m!U!w*|!Wi8k zri%=Pf$*?9!D{N98P;RB5t>Vr^kO6U?+tGBRZqZidR(^P`^SXh$CT-N_Gr{!usFIz^Zso!Hr0dIAh*z~RSz;p`VWC8<+#c}Z5=Dy)PxsVC@)Pi zw|TBDK|SvgPrH3nqv{#Y7rKXn{ZIS-Ab$lOid-~BnB#buBeT397FB(qY=gCYas-iMqHnvGVV>E0zMDr%4hS?i%^@H z&i33=!{#r6#P(JlhR*;H$NoQX|AnaTKYRB4U$vG31Hi08VcdP86=LytMrv5dX<_4mhg}F6amUg4IjY6L`VNb6x7rHC-*kZ z&pxlOq<$zwiw4x=L)m$%=*t*8G`6Ujv@V3OO{|fWeY2k6ab;Opj3CKC&+r{ z6)4nSydtM+rZkJ@CEV5z#v~Juj#OXCw zAtf?<0XuAXSArFuU$0p|&#~@`n!38Z-*CX4wy^o1`j7b)Vl3Sir<{~^A?>`uL~0~8 z{9l-2bOjX36?DIHerW`0@XV4O5=P{d5>c%sxClSUjjePR=~b+w4Q`Okur8^qH6xLK zK<)SntHRwi7nDJ~>RO1+8j7QT!W5h^(T;9Me!$#mDtVxMVu&3uZKEV~^TUOsbhrJ$ z?Dl&Wh%am`)z|8BI{5jqD9*(Qp07x;`F?o87?dPEJO0TLK;Biwa!CXy0# z(6GdYVtRe4Hl?*bdvr_TAQkxThUVmwqGpEBEm`T-pgB&5`7Ro!Yl^0=X`VXKmh0=T z?)U5K`%~_W`rOr=d;0BT9uRN(lSWi{##6QwAJ^f0pyCrkZkUX=jZRrErw@MyK&GF zTgj$?yD%MePB8u#oH;?(Ci4TM@#7y*H#=M)^oBG1(0SS*B$Kc=Y)C;!c~5+SYczpt zKK0|a$L!0(W>0Ht(nE-VZ}w-f_+8@_IA!jTj9rSf;KLgRE$Gi1WcnaY;~#0e8V|5F z$U)*a_VLQl%=D04b3LXvka{4dH;7q4@P|If1cZVaFtVLskxQhVV1t%scl0fQfNTHr zjy8Mrk`QE@F^vKVgxQLICMXxV&9H5C2)tpahf;$(;CG+qu6~UOzI)$s59D&6qRsIL z9`Nsuv@72m7|()*)7}{TM%kn$bntm=kJrx3rLg^ z+yzjvzw-uLt`FV8q>bZUC>UE0Jhe|^1vEF9E1rOZ&4$6Cz32&B4_2{nyCyhbz{v;# z;&(6P3%xJGQ@^bf>{l#2bzE0uj)-D@Zs8Bxk>&GHyj@Q0@an$i9cfS8*aC}%=*<-b z?2ULJ2zL19&A&erVeTMrWf=KCEfixhk+AhZn zUmYn7M;i8uBqOv4G^rDgU8r#j z*j4CsKiZA`3rR{)38TMbZ_*dw8iOJ5eHo!mTt6O2<3W1%lHV{@y;?1gC(XBJW^F;nHpV~X8|M}AN^#$UN~x*wSOXHqT8xU!V(+i6H=ae zJ?-3P+X!=EX__s7zO#lhNbMilOm2K_~J0`HCO5VCK@gEy$T2kH%L5Q>8iv}T7T zJBi9GIG)KYg@yLZ^g^xdk$BKn>!n)%<@SP2JU3FM@ryWt zenB{h!jJV`9&azx`+`=5g*)M+p9BfMK-hqw5C(-hxj(%FO>GU9H;{5es#$2X5VWcH zHS^E}k*<6An4zDA;P~Ol4R80yRrqHQq>i9Lx@>16=fmriJJ#d{QYab%*Z8>(Y3V$%w)QtY+?F z|3>wNs|6LazgrZ}GapV6>U`JM4i+;QeB21V3)C=OAC|V%38^EP`65uE1-mNL-<^hh zcXzGv2HGwpsRq>dhSlz`0O)4pv9a4_)!JuG{!`-`h7&?XnI9hRZpj*XLocH|O!Yjq z9il%RQ=w@M`WMC~5|lB`2V`q}wAc&T2TB(d0LwNf1mQ~Z~0nMBl2AT(i(FG@VXx@3o<9z8QLZXfOBP8 zJ*2$?DeaUGAKj0U(K%6_-d#sVRQM5q|IPj7{IUNsd>gkz@^S_Q;sgjW1A)~$=mbL_HduqNAw*OGOsRof>H+9MQnY|p1+pgZ z%B%qJjM?mhY>nvbLhPLgd_kM~sW-$byEBcUHbD4{@vr`UI0S0o+M9{ZQ|+wIReG#y*Z`D@${i*0WAqS`QB zF?rY4?^>6BAK8`AWUJsNMR>}^O5paXP-EY`cM#5Ax~$l1^qO$uAb1l@TXWEdpcHwN zMuiv2*p>DTk;geJ`k1KC4YX ziv=%0=$|Gppe4`_V~H2h>U7&Mld220Q>!G>&a-bz-uqZ>HBhU-hR@STpP@7}Cem6- zot4pdS)Yqd&lI@A5-^{%lWpogoTB5;;I)x$hPlGhUX6@^9CTsN!n;4a687|D&Xwgv za@Cdo*^8hj^-wZC$cf3M`}_;(gZbLGtHP@;yqQU=sZQ)7JyKUW81r)|-{SzfN@j374kW&V)g z;UA=*DK=BgiQ0r!rO>vGP$X2MEhE||({4q;R&;UjpFWmL+EVqdlgU>QL<8x>4T~C_ z6TSn%C^fddX9{I<;+l(8p}=iF8wr_pk60z_K*R5jRsam%dkGJVaJrn@+ewiG&lUpnFRc44i+O8S5+!?6h8=C$U$4cboLv$e zuOGRTZYlM;gr?Bxcxe|&ZZvs+5|{P3h?6nJKG@;u=)fj5YSellu?osz0y}+>-?4es z2bcT?U(=bqacAW|}I-;BmWx`MdW_`tvsSkH(p6kYi>pdkI}>RmP^MTSPDRVD&R z7NRS2x)7wm9JY+;_D2aH3H83A`??gCSwUA@M|eE59NJ31b6yz(*{*!gJ6ow+DcV2^ z0zR|cu2Vewda|#C*036{`dMM(@yJ+JotR-7R07aY>yHoE=yX?FZ2p^l2Ks2CH_!$4 zOHID%Z}v24AOtj`LG-_6G9!GC>XJouc7LOwGd>y1a!rA#A!86yxBcVua%`z)I7-@- zGbHcp9!W*!8vmP}K_xLkZ^jgv)cBB_@vFuVtSay^gAv-i4p&ui+_tu0^EzNSsb;Zi z!Xea6F|0g_Jk@VWZ%)k`Ie$DZ&AZLNQ2ld9sHYet3&LGD0jn_jGZ8leysy9-qb}f) z42DD%^PSjXMjTdi4ih6~cRnl~*CLiwXpHh2yk^y-dYJ6kDz+xooZ1*LaOEvmvo5cYhN)e9+``I@UK^Sy!kR%JidV9F{&F4G&svbj6dktv6Myb(z>j%3nypY z#jDJYEWxY;)zYwiF~>jwch8x-v=w}0@ru%S$|D?2%|i$!h9JRYg_YoA4P!p;8y#aiy=hHoLIon z2vXjyEdYDRsKuq3)e}&7e1jwehwpEU%5I18y@L$q9czY2c5|aM3HJgO7$ht`lYc&w zLQLEDuFs~Q3TxT^X3>_n!ZA;gb)Io*GxdfD&^`3`bHYZ}{I@g04oXvIm1$~4Zwjk) zS;3X?$DER$Sm03yBMKf+KtbxKvd9&Q>x=6}SWfa$*{9HRA^OR}`y77^44)<0(OT+T z-}xoafxDs*#dd)2!8%22qlQ2-4hh%fIZ*AZs>X;h_50moY+fl#(43C=@1(xfQ~@jQ zqd*8Cnlp12U{cFrYCqXvktzG+tE(%B6(^3%ZC^z4d%Cf)sU-{XlBx=4(K$=Fi6?{4 zXPvRt;`u_gENt`BJ!vZc+|J(MV-#T-pZIwbx`%MCB2c3te9J?5(r~|f_51L`W8hEy zw7KHZt6L7rC3JJrb{XDs?9cYsOtAU{l%27-@35#0Cs^%KP4nSI(em(hif2R^H( z@&LO^$4B$>&(F<;2Ui)GVm9k6!Y4t7SG8?j!J+1oA3qYeQ!Vd^tuEd9iLn_lT{zQk z0%wRNW6bQ5H6%KVSl>La4wM_B{Qbq1+bl+;sk;qx1a$>D?z>bN6+K|)l`c>f;MnvB z8I7AZC?;H2VbiP98O<6ofT#1sANs;S%L>>n|7pGj7v~m=tQsQ~{&;qRKxmh5JnR|+ zL1EQePe+Z5YsWhLth9PTpctTn)sHSmcL7Ky%F23zQ_GdaKS*@L+vm~pv_YaZY$!hICR6QAy z1V4^utIXS~u36QQ`)x7(fO#7P=51(T-UgYpL@m)4&YQv<6sL=sj4(ZY`n2$-GHtaL z3Miw{<4r|Trsnd;6-5z_3CHNEIjbm&?|_!RnkzFdLD14yqaA3bd3poQ_4zGP-e@vu z2Y5_cc&){v9pGJ<1Ykzx&^hhT2IYLhJ zcRH6RcM6$Iak}{Caq=|HSw!wu?aZr92E6VNRQH4hqeuUr4zsj?G6+f@~lC6PgV<_3597>KP+41C_WF^T! zQb^(?VJtd_E0-L~eQPIOz~#{~!nbGYDOt5$LRWFR*kvsg{yu#Uk@cxGXS;7zZK8+K zva*M9`;kT~YP^1=AfZc9?z|I~M^Skc%4kecN+sXn7S5u_-YRcYb;Foz5KK%|rfb)t zkLp1z78Q3(Q7IP5`XsE@?Q&WjZkMaoNmgWAJ6Vy08DCgQsz_a2ZbiLc`+V=PhcqppS=9c~W+;ZM=y6rtvm7l~9me8v)n+IzPm$%nyx)NwRm7|I?L6AaBVM&S@Bp9JKGu!8 zt4yOu z+MeB=-J4~yqD81PIPE|-Uua^+*P^xL*-REZu(n$J$sxf(GlXerN#=~IH(9g;YvoV^ zCCEd&+}_;o1X`IGNlYb9B-n(R^i0e%yaeHMHp0CUH%gahFIO+$cln9SHJ6j2euFW0 zxq0`)i_v0%3|ibQxsb&*;(FD^xT>|ksaDD9v2xqcV)u&LPOse}<^|s#2I(wpdf50swO-ycSZf(vr zGunl*m9ZwdF}6-#>qq_tK|l27`y{vo6(zVxQ}jcBy3Y@*m9-LDBrf)&f%s*9wC=J% zr_Yc4z6G$NdAWpom*?A5+${lui`gzex-_%O4{MUEB`vJ&93SA1hz4?IXsisQWi$$7-B%2%`X<3p?w<={J+Fy|>4D|JQh2tBpEYBT8 zO+~S~7U-^XCkLn1Mj}Kdj5NfxTBpP1rhl|0(h!g3bL~W>prcTO*6DD&2+zvJV@P{p zDIP=lwd;@XyKCqr#ms0mj77P*+A=aZ0F|Z4?WeI`_k{wrO7Xp?mebzv^uv}uhA|Y89(BP6&zNHV;POk zG_rbh)8{X1X%j{H65Xayb0~5p-gVP$k6(7l*ll~)Uvlow)(0l`H8 z*rIt5oapI~^pd0CuD!1Yi2QdLwTMGhaX?gwKvaD|#%k?vl>Uv?+8+;*Dr2?wHdPsn z)!J)Rn=n>uzdcOG7&Xr`Zv)S~4IJ2+w^f7Pq3X!wPT(L@d{b$e7N)1?^aCPjj|siXw=&^z^Gqd;uRtzfT0TjbD9Ui01bHih2K3|o2q|zn~8pN zQ&=QlGAxqM7`BUyj;M;{yX&g8lk{`z$mb@_x@zrbM@Vm-Nu1}7;yh7^q&w*)R%@>v zCcQ+7=#uW0;qBp_VJ6(L9S{s@kya(WwAI?wGh_m7gVqk2;%kahK3*2yEYDHN@%mXf z_9I0Rlw;&W&eMuXqO=mGXEM*B-rl*)!iDvW>S=G+4yalL+I?C~E(#!p8#p_8y!TZ< z8L87nBPL2*CQK;FM9GajS)Ig0q9Kz8ah}9PF&epm#Ax<7A@AeGk6 zijGA0ME6C{L^V>hKZ;dyiW1UkY01&FqqCl-v-LD0(^PHpcynShXkXr7N(ODqBjLDb zp%e@+GJ1^mJzAtK05ljmyG_4GhjaxZuO_?mBvCEJJae7VX!4k%qN)@`N}z3>xjiE4 z7txS7BJL6QiDyKOI2pNsK&OdvcvexS$0(f^r^kpj_Yt|$Rg21_WlFC{LCZHkqXIN= zR=b1Jt$mJei{5EMy41$5+uQb!lW}~Wj+2cG7Bn_?Ex5zeyl}R=yUC~Hf?j{Zj2xN= zNkdm-8bL|xV6|*0j*=sMbwBoHQA*WW=V88N+D9}imynUXe3wRTo*A?=oCj*nZytWD9NTQz7{gEVpmA=JouZVsYNK@^lF zFB*JLmN?Hgx`MVQ z4Q_1a>eF~^y&80#=2IH1ksG;1L9{t|Ll6gLn*otK{F~|}^k%WNws?6nwSwX{lEmY! zEp@U}j@8p+P@FE8$xk?TtXve1*@${TaS@-I)TKNrY_m0~hC(V~D2R@daeeH|!b4Gw zp3^7v$)VPf*6~)YrL~GAb(g8!_KN8h^RehL`PY%xqHm<$W#5gw8~sDdU@NA|sZXSL zq;{j-csDcd9QTg<#{KuDcQ=_V$b$8Z&Zzb4Q$KEaIl}uHm&4|F1w2V#>M`A8`Y%gg zhxR}_lDDMpF@HOhsPsUnZ>HZ zl_4({|I~(%jVM@uA^J1pPz@s%G-MgE?6EMGDr!?baj&N-#3MenH;#tlBk}P#BgM0E z9Df!`(1N4`y>$d?bgXZB>?}2|X3E`zleI7^mkOEgD+VWP2$f3|o;uqlOc&|OIwedK z&5fc}-Giz=8i_4fLN57oXluJZuvnC`@ zL6`6pkg6|WRbTKC-lH#gDaX|pnpAxuMSr@2Q3%?KW{I4twKJ2x0!=)HXtnm|b4ImT3MtW2U<6a4Ni~rP&Zst1!6=vt zDalkwiB|ff3h94&AVn!bxm5D0CXAcBURP5y=g~K6s@%JWh8sCsu*$tVIcy^!qCyPW zIn;^D;|SqhROfRO&5^2ykp?beZFSYPz*sasr(BY9JKhtH-F(I3by8@<=U;jL#f=-iwwz@ZhRK z&y{;R3zv;P>b96gKwtu3{)gs4Xv5E6!1)3l6k`(fRy`HzdXa)hA_b4cYVCu0v3ydc z0F+1}Au&cUs1T`$g>(6M8iiS+<%Q`S;WR~$($(63P~9&*4;Q89t+I5rc3QQN;b}Tw z8cD2Wp`LjgY;~*%63p8WhaB^^YROY=Fy97oYaqoE>?lLDLZTF&KK*gzXi7NtqZZ+q zGOsJvVWW9=I+&wUKTpp{V&l1d8;~ijaXppgdI}WjsVvuvv`VB^BCQg|j&>BL)nQs4 zrq$u}ywpzZj8k?f+f4$fssH)W*ENgP=tg*%BM?*wevQI;J{KbTEn* z^>++)jF2j|14+D?3|i|DQ92onFK^H%gTnGiI2nx9A)A0Bde{Tf~?`(l2L$B#JP%LV2EB7zW&juDJ^w2lw8L0r!Us=Qv+4YQm@PB2y* zRS>LHLHGz9vbOz~@DN#K+g1B?nZS9S;O3l5a55WnpyMk;YE5c1ryA`Bi2o%Y&>F zJK;}sx!DbT%HndcRpgrlq=n+~nG?q|)5qp${>>@jN12c73?6qKsi0@)1$ke!_8z5D z?|jBnt-YsGqiMu|3>F`9ZgZez4n!}f`jM7TSqwg;p%ky70I7yjyvAOKgthceDaG5X zwf~?LZ?^~h7f}3(;{@xSIZ-Z83CD$F<#~+|6vcNGOttsag~q}Lv;pH{;4$lCo)?@i zxT>D_Jlx&@y3dPN8dsV&7&n;yE^4%*Q;ds@%PD%i3?e6oZy#cu*}1!AvKS++F;8yV z^{VqN=U<$RbBn|GQ!rG~X*DGok+CTgs045T5Mo(P)X{H8<8}mg!CtXX*-zL{+O_r} z|5NwPDYt~k$zS z#uqgWHh!-ClWBL7eR1{|i)X%9x;UA*YE$cmO?YeAwQXsP>=0N{JH@PFD&T+sbLavJ zOzr8EWB~&RWQ##<>X6-m2S`S>637d{;FGN!@fl&_eL6%& zlgwe#k7J2tM{;bn_LFA_IkU|M0~Dtf<(Q(Bi_??{%H_II_NFqGIX*?afORqrILBch zRDdD3paN6BiTV}mzOE>tFM*S|3daC9#EoAc#M#p!x0KP zfl?dMShk|vYBtoLD{8Gv+A1jxY7J9Gca``yXdp))dV%akULyp%N zjYskq+>*cGQvHPxxhsA5@*E%PHkI2*@n9m6qt$B~n{qy_M>lA{(zU_8UcB7PAx5X= zbi7gHT&BGbKcL-ZoDl8~d;>ov9=87yzhU`}a2Ed$#%>$phWHU~oV!o=BKKp<8IETW zXZj3gbkEjKg0^;2UEZc!jF;$EhSuV>x~uRQzR!N2=P~;?b>Gxi`NO)3{-5#h@k!%Z zy@Nl&A>dAMc$B2%-R?O+{{bvenc+HD2f2Io%aUguj**6H;9f<| zVC#AnTdlpWF0~cNJHEn)$QR*$%DWQ2g2jclyLP&EyBOD5hhv;aS$+?Xv;1!UEuIm0 zl^^0q_zHiL*Ye*rJ6X7o&=r$XZCSHw?l&`F7R-{FIb%j<>U8K{o-=m`yX%^`BnjF>4`1^yTeNR4x%%L^w+X5q050M+aGd-Ghg;fG{2{QG1%Q z83Gs{1&*NOk%Cc87ffopz!P;bQ6OYCnJ5sOf6`Yk`RZ+RC4If5ua|VRq?&aFr{F1g zBx}JWQJoPHZuZepBGQcG3|1#pxpxi^JDmorugd-D(6E&=uvI=ZIc%6?1@q2H;%?T~ zH9t4u1UR3w5pS~`rVh#m?QhWL&AZm$oenvF^5_$P`s!Y-(F`7hdqw z8*bQii{rlEA@s(d5ckS=I|rj3>YYR``2d*TGY>%rt6snrj4{2Ki?M0&hf*2hgh|^4 z+)-uTJ_6~WRSQ}u693%Hi4#-wL`{$kSslA)Y6-qNXSmtsSvqqL9mWE;FnYR$3Pam&H~jdUI;$I?p<} zzw<_Jr(vhC)4J2O)BQzmudvtpr1)GcXx3N+i`9|}2_b7JmDDHQnNGdXIiOqL*5Ap_ zv5}}bZ1Ny zJ#LpPp3CL+2BWc6)ay9T6U*gut$EqD+m*2*Yu;!wxr~7wp8g;TX5{UWoe><_9YGO~ z+|*QPP5&*KjJNg!f*pC3*JwD|!*S8P?8xV3qbnZIwi+F+t;T#VChCmt*0}63bYx

#P$-qjG?5Py1e?{05NMmy5@}`AD4h-l1A1c>^&H;rLarvc zip&Qk5Au*Hj6z=ZR6Hj=XFM#aIB2ouJkQ}aXax>kGnsFSR}p^@T2bqB_(c*3jr$IU zk5hLQ}or_3gt{$I8_#CQMmndSvbY7Hbb$E{pf@mvPMO(qG+6sbTDOd$dA*DJC zL{XY_7F_kjM)--7*sEt$yR*>7I}3@dqmaxx3Rd1#An2*;a}|=RwUAVug%)ZKUJkC*1h%=lgdQ3ZEUp}-y8IBlS z3wxI~#86vvw0U61skKWBv;Aq0UA^ZEi_+=YUqyYf^;6$lzUm_8A=&R1TZD$KTQ_-~ z0a<3mhOsAStG6~Y(Wt}hc9)MGyWA?qaWtxN1a7XK8*T>#xOR$}VIBe*4_?6MTybjM z@^dZX%1xdYBt({WsMBSFWw4>MNC;e>min)zgvK05O*3MFR+7^A5 zc^ll!+p1ZVwFb#7Y6B^=fg=*)FbRL)^^kc}IbNJD+6slDFnxLsnXa3rQ*RPf`n=A5 z8CMsb46ipx2GJaq-J-0mPia6rHCOd@9|rYmNM7pE@Um2$*ONN0jpt($)zKv_sgR_Y zjhLgPi)|i_CK;-w+idaJoR)4SXKNuN&}a*Cnj$KDQ8+G8+qC#*T`$U`I5AA6I6=26 zaW-dY4|PgQLrW!%m$$DBit$KzWl)Ys_&8d~1^GpiK@RX$)T7$w4C>6o{Pn}Jc%D7qT8B_#q-Q2OV|~Pn`i%= zzUlU!zR{GweJNU4Dk|6a7S=Nl&HQTb62CPv`qKC%r3c2*V+&h+NS^ufcz@dp%w5`! zW#TIZU@PSRnm|DsZ_v3Ot;g#F>x0*!>+p4f>wlrBpey7P z!VS8R&k~MsVgy21u<+q3o>J{Pq=4!+7i|^@K|kyV7OFU*dU+iU?$XgXE*&)sbPaA- zNC{Fshm3%r5Zn;l7i5Fa;sm&Ar&R;>iPvL`s(9+4w7Fal`Z`Q>OeK*K|SB}E_E8rbSVlFUKU=a%3RrZ1P6lx9}=tF*~7yjY!a*# z2oPvcS4&Z8u@z{D9RRS^s+N zl)JT42#<7a$Fv=jIohps4lA9*DiJYS=aktwPWe)O4ykdDlm13EkggJ)qt02$Jd(?k zY3W%?gp0If?SM-9WCqa6f$al32bh6%+9l1RoHB4-DGiMVo6ZnWuUuA+3o}#Xm`C*p zR`!2p=iI>c_%LE`D#DMbr|%;mq)Sd2x>6bgr{UHPtmDMyB~}V#SS89fC51wG3awMx z7t+!~T3Wb5s@DEU`J^Nbwi78ps%eJwYA3;9q_w?$a0Nj^Nac!o5O4)iQb_BH6{W!o zc`Q8>21|d+ae@F+|6h>+3e8zmG4-w;d;u2M-h-andyuKU2ie+thrOal6vdAEQSzz& z9CxDh7Z)>LLe>&t$%+Z>DItlMl0mVG&mC%LPX?Q-_?&8JSdk1aS>9kx2HjQ0d?=zM zgV`!$Iuu!$3@)xRCiUXTK)i3^+Tg$Qd$ z=+>9qZm(dChO-hHkt!0Fswl5o+LKL6v?JS&M%pXwxSiCw`Yu}-U9lq6*WZW7`}Xu< z=o9*IU$r)M#No>I4Gos6c>Tffw|0tEwE1pDx%8}Zo}Zs3z{{z6(sgOimPLd|kfTWd z^-U8dKj#xEzZnB@x*$1xyI3JT)u*Ks{98IMb|giz@- z!_DFny{ze~^7@0r1`S8`pO#`PRf1?tT6-OL{C`0oNtj1HUbI2Waqf@YmglRv3t-8Q zIWt>P|7KfyYwNn(o!30ryKFS7IaB5{+ zeofj#g4&8LUh4R)N41^aY>72)-f&BA??C76vp21iT%l;xEkvySXkw&E%`Y`5v%M6$ zj7F`i(Zooz8c4OzI@h=PqEX+10d(afsW5et0;;`J`=jQ&niUX7|C36f-U%DB?M4Wz zEAzWCvIQ`T-Ph#WO>sSMcvZCbH zClD_-4ZG^<6W%3oHjzj~g_z#eq-b^s$G}!V1(8q~Cv=gp2c*4T1@9Lz52#*qaQ@QFpFIQWr*NQ0nOc>1vKsyQfI;|1(pQJ1ei}s-r z<|4n`pFyt^=^8!vXAKu3N2d!qDA~M7bx|qnb2|1*-)ZvL@CgUy;aN@*?DGE*0+5S<8 z)!tJVQFo1>I=|^zqyc_>{Vc|GEF|dUx28}EZj_O1xG{kehWhNOR5~1PO3%Xv#bU8o zE|TyPda?Sx0s3-%2W(9=kg%9{#U(+q%5kY_L(?|hNYfwWKO{boKS&r!he>;$_WQ9f zlnXaCB{#PPJf4s*A~do3SRj^)6=DPKC*4nqPsVtI+#YR@uY?t-k6Xqsi7t-!CHj*0 za^u3d^|SK5iF=ddO}w@&8bx`Z~F zcXtcj)_%=uZk@O~d7ojoaIbiu=f23j^1ZR~CigDgJ?>pG#-uBun}wULtWL+rVsTm4 zBMt|V;I;+@DH0A!kW2-^qBjREAx|(Es@Co~$S2}bwYEc5ML8<*9IxY|$%G@BOvK_b zIiBTp4xZPEPSN9x>Sc#sFGr%$tmtuwq9+-PctkhZf2@Z@_&IbMBnYC@2SdnWB_#pO z=(MW0SOg&yk|e;S51Ba$yOEN;D zR-LLZ)9*q1Jm`7PtDd(!jOX*wj95j!XC#Y^1R2rbS%WbrKZgW}ffHS$8dZH}Lky|0 z@feO((dk3F9q|nREb;-5e5zi81R77ANnjFsde~shCH8T|lJWH?(RdP(cxoxBCM(IQ z<}B}@|u=kZR@oH{#Q9u?1er#&QubJRO?ikLFuDets!mi*?hmo6_+3W{CC z@lZEusN11_rqGL~?*6CH3r{kd`OJKcqKF_&BVp?Qe&z(ei|-<;H;LG;2W%^-taM*7 zcsM3Fj78!?CjrBD0(Arm?h6tJNzP2V3vwOUAf*G&kB|w1g{mfAj4=spwg$r*Aj|og z;gAKia)R!+sf*b25Ce^jwA)EkWV{YA&@urW>SYvRPz1$daT6NPhpoqcm=oi!E_7&V z(7~U0(Gf48@MX!_pC#X!{kuH-n?Oew^N`F2{h`#%U(rA9>T;VIS!Ucq#NnLz8~U(K zvIns&o3@?%6J9p+41<@ongB3F05n@QDUPHxPESkV~9HdbKra0 zpR1zJsg{;RAmGOqvl$`aZ6qLkaxhO4wP$4>!u!K`Wq4m0hYt&FNNpQwJJH6p<#=i} z^7K8BZ5DWdZ*UP$BOY+lN+-1rosZ`?9z`CQ7r0%0bDM(3&Jvd|F%~mS`VT&uOqb@} z$VBi8)3!o+ETa(ro5w3mQ;Oap97DE(S5y>AwaP9*v*TEERwPj^>E>W0B&Q>pR@5BK zw4$blR4YWHS*fKJ!F*IPQU6BEFg9AIl31>toHP~?p<72GK_EW|6|ISC?aX1JkQFQi zsEbihN~o+TzR75ct#W@*b;GHY#gBM@uF9R89QL-0i3?T+Vwk(S{kQIzp0UaXrn*^;f!9E zsAZ&)WK=t;WNA0nE%OFJhPq8hZMeB$%#U1uI?_)1dC6cOMXy)~MYbG9fyU(uG$@y) z7A(oKY|v<|yYQ$HMGQdUxI!;Nu&Ex~xD!3;I^y~vdRccY@P} z(A{Tw!^efxmOM*ccY8zVN6weMSPh|N{QTY6SVH%T%}`v)B9%oa$hn^#Vn^6LY=zab zr;P+bR*id&*m&M+HKz1U6F-#FTS=_xt@N*2f4~@Ab|AzqTfKhpc`(+dfUQkIs5V86 zx9-93gO_Om7932=AA~>nE+|e5WD(E@>b_NNLjjv?j$zp!)63eJ)#8vKfV>iN=^PS> zoL542lOQ1uN;iN+A|g^eQO9aF70;v2QM>2EXNX1m38qj;mA zFPF+dmV>JE3)X^9uoj$zgB$80^`(;U00>p?kB5eV6lYSLJyxmfqM zHg^MI-nO}@b_^%(xbCJ`cfNYtH9LN~CV$-}dq2JLj%`br{g3b3|EY81Pu%}ccl@uL z7ZxAC{l~Mv-S_{TeP9SMEUQg3_c8mS1ukOlzkn8{BE?*aG)mLydnvdL67XH<2By465{oUJ5Q zgJ32>(#dFKcZx`)wIb~y3UZL^O0kfcE>07Vciq%ij%Q|w4*g5zILgcszcMxTCc!Iy zS&!6Ew;TKut=P7v4cVknVZ8Wl-4Q)wQ*1k6N9#Rszv2G8Hehpg3dQjvtMjkWtkCvI zJq;^5)#7~tUT@|kXjq1N^~(&)^1bcdoy#t|%y5n2Zrx|}pD|e0y6$q}P;o;M5Am&# z>uO4-bI&0k7zr&N(G`pd1BslQQoWsd!Psxas9+#0!RdV?`+zlK!Yg3b%dwaujjEg+#oezZQf9jz#6do!DhFF zOn%f5S)eKSp@Yv!D4#Rf{DL2u8w5Y<)OPvF+9A7HP3VuTEUPD2jFcjJo zB-Iz%6Z(Qap)0f}bpJ;fBZ*q{9Tr74Xv|f)_YMymjoF}3<=#6q9OSaeDsMV8oHejr zG)je?k6*mdIW24F_CU9{wYAqJlGf>-kJBbWixd!{@OWFhvo7DX9NiSt+JcvSHrjU4 zhED~PubjSYO;L{FOia#H_TF~s0>4e~wg^UN*T~h)o#>I&%0=rsR($4qtLM|#b~i7& zWnJ{Xs~Z|polPyd^twIC&?U;ciIv!s11yt_xq=pJhHvr(;heVEP9t0Oe z(fc3qjsrmr;Q<2S1{#MEL<$9n#%k@|`5h|f<~OFCqfi6c9-^A+qEB=Ki*pdJN)xMAp2ti-~Svu#Rj?A!EqcphgpuVJ@by9%X1G*di5r z)!Lt*p_^2UG5O;mD>Ra-9x+&t(7siOp>JxBXb*FL46&LRk!(`zM&>5=9%dK&E#@hn zTf(7Eetr{*=r#iDbAb>>=kKIBq}ijvLz;2Tehs7flhFkrMvX?n)NdLw?J=?Arah*L zi2;*fl1y1s#WZC)Vd6~0i`tbp4aqOf#crHO?}$HUj6`jaxDBhj?t`I=E8&qC0~eE+ zpoF}7(GMQcVD$4r35D3OgggeH9|BsRM7M>Jr89@w!6;cJ_3Z-(oq)qt-hF5ob2=a0 z@U5Gt*ZF;;_1#yo_(xkqt=9P!3Rb$o_^SR+=Raj3bz4G0C>jY)h4S`v5^Rrj=>sv~pT0W{#6R0TIzOujy=w=b2Vd<2@YK$L zw|3@$UYLWBpVE;XbY|s?No>zGxi+`mrMX*+bvliW_wZhw;_$|FQCrj-Q#w$aE$>@m z+p62D-{!g6yUDjTbqjy1{#MV;-Wz?vKfl;Or#L71|6f@!B>%4 z4JfZ!BxI5HN?6*xA%!xjVhX1`t-0?I*qK1i<*~j~<83 zu=`#71(r6kLaere`2TH8qftV&b$`2}3xG}AVs$XU&^^zf4aBRwG&vj~yABl^-a3c8 z*$Low-smMbF)@3Iqa9JN;{{RT?TA>(vA7XE zZxQ{!2qfBDs@6VGsmUPGr6Bo+dDJo(wRE0Q2XP}a`||1nr!4DI|hk0Tbnw#WJxYV z==Kt&DL0h4oxNQ%!H#G4XQnconi+6_H;=rr7NYY^~2I_(q7mreV04R{ix9pQ0RtplVmBa$Z;I*|YR)CN|vcMqWU?B+svIP=atzFL9!LA&x^Cbi-5GS@1z)rvq zptP*~Sg;)^=jqhtOmbIuNiH@d#&?&8&*bGhpTS)9{Qs^bjGaf_T_sg4Xr}+^o}Qkb zo}T}oe`bCye^0*5|3y_BuYen?teL0w)h(TGSzNPNf64CmuXyXy@Opl~9t!abc3zCf zZe8Et1^2jnpFY!Xvd!|%v{iZFH&uC1Y4v%;Y;(18==$?F*0cW++t*_|KZ@(?zNbOU zvinljpFJ~Rw^DV=W=;-JW)p?F`OwWicW$s?{ge;J=sBh?Lwy4U#D;`^3Abnd^t zc6Y1GQ#jr45IF%?S$=UK>-`HgmQZhPN8_!Dfm=5(x$(Vkd_yc<@sz%V`@Pp5Q?KP; z5kH6yEo|NL?eF|EubyiK=;P*3!!IKfS9J%J-3QcO#DID-DfSDu(;{rnDr2chu9IJ{ z=Cav4bJc=dW$aE1o4K8r+}J97r3HYi%EgPjb1gR1*1vVyeY*a;neDEp`F7&#?r-wH zN#|djfJ@~8FMTelvQ(57z*c3f4pDD1jh(M7#t-yoXs1edrY`?T-CsDJf$Fp=tE~yB zt^e+K(+|`)eaiE2-orB~O400PMLTI{w)qj0s158W+hf^dJzzUv-|ya^lL*+|7PnP) z&F=Ec88+uLrMAzOi)Tu$DYAt%Z|Q;3Q>C&rr@~ht{B3UGuA5zzn`5zAr?`tnaZ0q0 z?GA`QO1_g6v;8UI4m&EVM6N5x{aKewRPYkfu?-vMsd&*MJ+80UXyKbREq|sjZ>BH5at3BitKip%lV*?jskopbW&LH?K2TbkQvl?+Q`TQRH&B&h zG285F>c?hmYUEdltYm%pNM^!^UjcgO&*g_^_T~DHM}D2cPtbXD^Tgp9zMEEEdabIm zp(vT`cy@b#$D({C1=LYK>wl}CJitaF*87W}&(5_VHw*lBv_5BU&fF_^Y)*hhSxqETQxl0)*G3vR zri8kzsfk>gsuF5y8hBP8pd;ojC>5X6PuH*(W-TlzoN?p4!tjh6=M{0_D$UQYvWBcp z)~BqNu;*6OI@_%U>k99%jpU5velhDy&WCf3W*v3DVt%FI+l4^+=|$N^76%ncv!zgN>Yr+Iq{No6 z-Oi_B+-Dc|6wM81yUo*g7l{K!qeZglB+UhhU1LIKm!`z7umhH#wodJuIyiO5RMXUy zu!g7dk$cnO348AI&<4*zkMtD1Al?PRA;RHlU2;(Fkh|q6`KmlC-5yCLp z73V{5t#q&7{?7UpYU=?zUS8%@e`&kk|DWG&7pLs6+9lR+@ARKH&UR4eI!L#%@9*BC zT}2Y>JDnjlQ|jbx(7Z!oJzF#U#m_q&?$T1}wx?{(yJWgB zzyF?3G>9)eb^qb`_7mUy$32@jKmP3d7p`~D39yF+tfi@U%Y4(jT%q6DifrmA-^br5 zd70xk|9$9R-W$2}Y4>fmn*s2fLj5NIYuWpaSnI}nBkz9SeOm^T{uNsmiWw5vW_km& zO+dB@xKS=te<3QtMibzWeC*jmOru?BF#*LUK$_mbO_qhoF@Njwo91u9VO|5T323BU zXfomQdj_wld>XBCArdCA1)~{Hh?nSZEq1HPHaz9+?ETsM9k087o_pKWuTR@qU@p9I z`UA!9l|-~I&))KE?}HWp=DT6$A|-XPd=?>m4^~LH)mIi^{+Vkp%)B20wQ>>o92%B% zx~dt&0;TGTVF{upJN3Ck)u( zG?`2w2opgfJJ+UQmd>3!O~DqGUZG%Bi`+T|+Z4>3JA}y&$nxAa1J>o&VO@S5*5%h> zU49+b<=0_dejV23*I`|L9oFU7VTW_tv|$BLnIL}_%U1q?g0m;2I~gv2NWr`##aaHi zf_bJqw>+WXX%p!46rYzBoIfGGNag*$f{PW+IR%$YpwTA46^j0Xf@drEeFX;?_GU9| zn;^e!LRr}p(zDZbc?uo`QHLO|g9odz1%2>f1qRjW)g!nYLn>u41~H5w6?S6;{feRn z9z-yJ0eH}kes%Ws2x9804>9y%7758meohhdU-MAZr zh+_mE3}6tO(T`qu(2GF~;cg6P%JF2RuftsRC8_k)0#qv*y45#^w!njB=*Bk1qZfA= zw97GwE!d{8aco2^QzzC#zpBT;hsv`_>B55s^rHs@DwWIgTv11z&!ACLk18dOVf3o{ z^3^7c;4XBl93I56O`Rjoyhk$a(S+4XJ6sC$Vcp)K^7korv@3 z^eX)gWm<5&9*iGBH~KMv7;3N@{n)DVc(Br_OFIU!RcVsjcQfKjKf@oemqouPx95ud z9`ywqmUkF~8J$#NjnYFbBkKaxU=D&4xa+*fIk#aIR$%q-EnjbyZlz1*-=*Ko}R()j?HW_W|K>!}CQth=31L(uuMycGox*c;5Sfl7i zj27m;HLPm7Oe$y*KC@a z(*{-VW$Wge_bJIXDmv!R95{t*!sYN6!TOE7++P_=OWdt^vwnuL75x}dIL_6h?7_)~Q|(bJ@DliN0?dXjE$x^-Wz z2I8?BjzyB#BD~)jB`)-5M4suepucfrD?rwd3;QJ5UvA{t6a;pmM2>^ z+gOikHC?Z|hrDvcxUwfys*#iHxd^qWRhSx8#vN*0U|#*`Mvcj^X(Wk+9_={+^XD)`glBU-5+(YkhaPX zzhB^%Ys>VU*n|;FZ*ScS`i!zRD_hoWW~0*HCWY>o!xFmhr>e zR*ia-?wC9!h6U<6;uh8=9QQLHtm8-UQ{I>RIMI!s=4@C|Pt$9@%@Y;!E^4tTkGk zQ}z3LU50eOo@`5hxK<~Q@OmvLjx>{PdR!AcPVzn5pVp4)(SYx^w;7sAkBYjjaL8qj z>*aRzsW!gSgI&!Z>z<$WJU-$rn&id1(WAcp+V>&PFZ#V-dXyR0PkJO9x~fl^oQrGK zZL}4S%O5`Gc7I5V4rgl5_M&?x?!Qc{kGvB{^gq=C(&O7QG%9Q>mS8p3B7%06(u7s; zpbhP4#Tqmribh1?!7M}+hVlFbSr68#acCJ@af=$`bRO-9U?oY3I3LTROS&4Z zD%5K@St{ndDkIqnG@_lqz-(Y6Xh4(V$MSReOBB2^Bj*xBQvTXzo8rto7vmPRs4Gsr z1?^~4U#H^onRC>TcBQJr60{q2;jfN1s*-v1;xwI(^|Qvna2v6FEohlgtBBG8>%M)0 z{uZMZZRk||mtvX9%``2l6?Mu6m7nX#wQW&X#0VEF&9P3>t-;cBjoS@uwb7QW;}4d~*2fxVD%Lz-r7eUt+-gXl zwobi_w4JTYl*es5QI@n-dp@F%=v>nHTa1?epk7$(+;X~Hdhc2J5u9tVQuT9wreGh} zgS&+TeYhRl@H6Fa(zx_9=SKZmk{+Ss^b(z-m*@nYq@(}O^GQ?G-?cfVzmz#8Zmnyx z4cBHHuFW?5Im|Zb-t*dw!(Zl%gWh`AW*x50I$WD|xHju>r8mDe^DuelA+4Qjvk%v1 zAFjk_ElV&FRCe1(SZ?h-jx8ZJ6scDX>*|gMjqp8`na1y6&=)+2M z<4$!CuluqsA|W0lDBl{vFoxB$GMAy&QURCCu<>f2k&jHlYI<89LkTqc4|)qBX!KUn zQlh2kmt!T-d2FDc;Q+mfv-BoRaMO9X=}pwrn;4?=xJ>6^zI>W~GS=7_45#QP zWBwWyCo8LhCm4~OJ|p-N{X`zcEZ!VSXOsEG3iEohp}_#IyIu#zW(R_2BU9*gyiKn| z((7~**kO+Jy_W3nmQG6WBpdU^hL1QF;O< zxabLJ^aN_@3AAzw9ibz5n+`$1MTbyJhcHNou$vCSgw^yk1n%IFo}}AR4vjud`;Z5X zK1F*~{1w`Z>CotL+J{nT^cd|`@d4xdQQD{Qj~M9>(>~-wqc0fo=V>pBq0vLMPsN`z zuJ5F{$~R)f2Pu{;)!dO%+6xcuMIG%0!9)8{NBa<_eQYVT4}v~P1FEFsG=Ly9+N#Hw zPt(20GOuc*d&dfjf(Ij0=w2k~UYw?Tafa^Yt-o|nn&uvzCP4QfLHFP^-Gei94@{_| z7{V0e^?nqCn_}=#41{wO!v&R|pcqb53};mOUsDWwD29W4MR(EdsDefh((TDgEu5mw zV++H<`WI*u1j4jwtf(Zod;H2ig$;4jnB8f_E~f2M*uF7)HmCNDO)t@7yEAx4#7VvQ zE9!;BH0nhK^6vDJpKQ`;eT&<@iMv#BmBa!*kZZ8ktdE(H*> z;W@1Zl80x(z0PZUd+bZtB7r zx((;33ud^<1rNDUM=qQp7tWCj{P|j!!nBbK8_0zr18d1((s`Kv0%OR9SpYdvN4);; z9651;A#!3l&XEHb$N@WrJa)(x&7qD46e?=Z;%&ZQTPjr zCXEYoUU@NGJ?nIpNbo^MVo#L_hw3B2aCvmqNn>X0W0)D)O3A*YNJJ#NvW0|@k|hyIMAk?O*|N2NFZKN@)3?lD$8+vy zJ@+}!dFQ&!+j1k`;;6ojQlOHGwIfT>>BFBU&+S!S_W0sj;^^xhpg;>04c1H$ZD7Gh zl+pw?8bqg?N2ec;wlj*Z+oL-shw$Mpn-`uKgWi5jd>h+zqS4AH>T|wo_Uxk0CzfDh z*c@&9HQspLOObEx=$JpscpIzhp()OuW-fl9M)hW{r=w|>?CT@RChrP1ecgAtusI`9 zHFN&fjHN@+&H^p90xh9}hr9)WB1O-vle5ZNQ@i(PlnJMo31^i-I&jvyE=~oiG8dXN z=H<*n#pv^h*W+xDo)lL~xh0+-a}Sd>TVWSe&#m(nG!IH?5t~u;I^|h88ZZ`Z)goql z`EuBOiKQF26q5x#RQWw_>xi21>DDW4G1?T`YLXVLHMd2u(3IfmZJjte0BfQ?7OmY& zKXs$ExTe*EC^CMPg=Z_)AlOPYBGk0vSTOvdRnq9yBx1oE%j;Olt;M$5!stq&KHG_AgQPI>>WW*p4JnBg0>~C9D*@4I?q^D)C2NSxpL@N zqNB;jtzj~kze!6C30>+^vp%Gnlz;E2-x1Yw&Sn$im%7Hw;(D&;`y1(nx69;l+>Yx> zStveRE~`~qc5o|oSAu@umQ)hnI%<1DxD2-@zusc&`DW)f!7)~2JCA27x_aSOier&= zktnm|_=TH?;_v5Kww|@%wBb&TNU6&T^16Pk;ZEkVzml|K`%0V3cM;zJh3Fwp3tH_` zV`Mr$KOiJEBKtMpdp}uA8~ppOiMUy{oUb*@?C*C8$`{MrNGUYz)izc!ONkw^dhgxf zqvWw4fuBo8S=U&pyfN$18jKTorTAV`?z-B9O^(`>9zN#zbdi5d!+rBt+g<9ctQ{5& zto7c8-KkI5E_owZtNH>l;?5(Ld?jwNj>5y?*~L;XlOqHojmPzxrP`fJ71~_jHS)!8 zL@3{s$`vv4;}7?aymFolD{H#j=v%tDY4VuM{fXjuO9#uGUK+*iYG*v{qt011)iP<}Id9fk&T2JzD zu^&?X?`EdK`AlcWTBLbWo`h%RsqUV{NR+ocXnf0wtv}+>#5w0tSyPUn= z&r^lWt2Uv(UE#a7@k#&F&Z^+i>3j7TYEF05>rkK5tE-mxzCEtv<(JXa(N}SI z)SFrXPMX^4h4!t=NFj!LDY{)V?G8*V*)dbWRVvo%3Qh2=__+72z0U48B}tX}_R5p) zE4TLMRo3i7lzWPnrv=o;XN^D;k6Yjx(4>14rTmwC51%<1@(95d%oeUHV-WBq(5Q@Lt( z#s;Q|r46L9rFC4iI^3pa_RYC+YVU5vp(FRIzG!}QtPJMcJ?X71iZl9l65F8i)gKCj z!Ja)bj;7Ep?VVl5YGuYk zi64g*CetgcGu*?;9sO!n9HrZ>a!T7siTv@M@$h(h{FWW1O~wwKM_(%LQq+q7tmvo6 zp>%SPTOLi~ArB-Rm{vS&ZexzgQ)q$zOUl_BlG&NUi*4IJk zF0Mg%*)yFlWvc?>qe>gFhTrX^KE*7q?6wstar?{9Mk7EF5Y^6^ zOH~!DPj+))ID$|p3|Pm>l|iO~bzBJyvNoAWC6U$Dx#$cUnLy$4PJWVLK<(7z4Vn2U z=$FYM16Q%FGByqO7u8%iJ6kYjBq^y$k)vQ1hNNVX_=)_k?v1C4OXmxR9Av6yyc?-* zLPPcmod+;D74fg!0+18lHbb$znQ}qK0e-zps>GR8?{BVsos;1E8|6TUj0$h$17}h`qZK>!xP?{pLrMPCLgjus9}HT2rREg#WooL_RX7xKVKrOWM6lSHB)L;^q{K zZJFiAIvR3MbL089smHK!Gz=$8;Ge&8p!HlA78NwU{i^Eq{e93Y{Cgzh9^}l|#`srP zCLfj=EgdbFWjT7b?s|+;h&HRU)ipC-U0t*LMg!3f!opg}zAsdW>5C#|-Z|Eotv6>f zI-NPTUyf0g$SqNu0cj-4??|-cxFjs+H@I!PP)r3|xTIqg=`$f#@={!4E5BUQ6bC-? z;LCtLtg#(SaWV=pD-M2%u$F5lFA2p|pVtPpzTKx3c%xvK*s0hz9}g{jn7?azd|ZKh zB=B%o)qv=YhJ9~A&hhWF_9$Gmf-d!U=Las!Jv%&qXUc;zByDjqPHwqVd+&^j^HG<2 z?ucYRoe0xG>*uI;%Nei7@xC{86!Y$Uh29M;;aWC(pKC+mAW0fYPl&lx;*RvawiKAq z>Qw2bv80QkbTrG(dl-dZG^JZ5;3#Y{N$LHjb=jW~@k2eXqZFkor#G$T_Mn16#8I&h zrORE;d-}SshhF9@Pi<<7vPPli3tb84_8fed)f=&G7pKfK%hVrtkb zv@d2StinNT;)RCtg^XR>$^(qe-SHJg_LUYb3on*zB@Aj=cmg(F{0qU6OazBQ5dVwe z4VEp@eJFu}b_>Wmm+H5Rr)N32SxGYTPgz;E*H`V?azUljFre5(*Jfd6OLJxu?7-s- z8yEM-k?_m2v;B0u;n10D;G)88s+<4mtl5UhNei^l83p59qp$jpEpG*|h12$HPFX66 z=WH>J1aoyIG|Z3qW*a@}?({PyJ;>|*+Sx~e`+af$$j7H4IoXrc^RhXomnu}`AT*Vu!XVuUr6zN`T^yT6?_WP(aFv%2;`AgTu&95jFloA?zdz&2rRA|}6Ztu=tYLHM zO;vjz+KBBYT+%H&W&d310SAB8!{cy?Z8}l>epv0{wwkF9&l$gWml*^4^&5`fkr|Qd z8FJ3&AyKAN8EMcw^zj>vLJVB`P&vI}SG#sKtr*Jlxy!nKnZDkVv*VgEBd{ z#B%W5qHK@fpe|{mDhlOoiB)b%m_W?QM~UybQAq% znY5J-)_$VJJ3sNA7avGNcV73GA{trd!QOkPk6!dMcddHQ7{u(c13w@x{8 z9^&p!8Xa|$Wy^kUE|Bm!XUi~3>ed#yH=BH046k;}EL`%r(uP}k75*X*t{*vtKMQ!xSfB3|Jp!j-z=Bdqk6tBl(C{ZZB8>^BuXM6U2fvc&S|D*EvHo%!e5@lOt$q!oWA3t)ZX{d>;0?GOS4*jD;{3?{yUFfuRVs| zw*27$-4l7b7xAGX)vNZ*mE%Q8_KPb{pT4E`y%}ov>a;b_d*h~-QZ`N9^1sfCG9LPr@AgIm%=%$_* ziVr&bjx?PLM%-I|#vY>vI;(#e@LaEZW&RmK>n?mxvQHZdX7lNIfD zZa6ct;;vl9!AjbsX+!Ck7VPYq6CVS(yK>nGaWZ3%?^caY<9dZ$xuW0=`4{&N-gJAu zAVlf8}7Ni>BGKz;uH&(PJttFc*pOfQ9Q}prQ;vCeyag4 zNf!P$zw9JkEBj`7c1nJs{q{H9i@Rr7miQNZ?}%XLV0stkq-n5N_0XVHR)6gGC64N*k~r(K87MG2~m3 zdY=8^%aQ9R2bT9IwWQr}4AXJpOz&+J{ls?ex&&8xLD%(=*vO)-)<*5~{@&il2NO8r z#;cF;4(t?&xFfjrW0|+)jmqOjbI;mA#Jdu$V+SEzpoLh7t3vy>nxTwGKIoc4p-z>e z7kSO>S$9>AM6+v{E3ep2GjxRS3k}2dTQ(hFYt*|V@54E}bJBNb90o#e;Kv&+Wv05! zg}(+mgY?l3_YHA7uaDs{T@Ql4+^JTOu3aWY6mNdY5hb~qzwWzkc6lNv?}<$$+gd-~ z-pbzYcn3b;YogCTj@wLCq4+=!k9cUseV4YgM#>n;?TqlQZ>kr!znhWhhb^ zA`dp8lC*&fqntJl27#d^m%gamI3$9G1Ie?*9 zEEWubfnhKu0HH+pabpm?mE7o3KMiP;=|q~7I|C>WXw@LWj_S!!lLUOO1w$hKW7OS~ z=DKE*Ln_gQ74`YKtKASfj$+7yDtAz-*Q z#^NB5AHIJ>{)&|R|7fn|^aHu736u_JXaqz}SBI^c{!Or&ls%P3Au!Yl?(VKmM8YZ) zz(AE$S7ZM+HKcZSUrwYW5Gt$;EC)4PFf;vhHlYD)st z2{aeHn>`hHT3#KJNgx1422LTmJDJF!gX{^ebl_1LZ0wenfyW_SSB14 z24}{>;XtSP(;gH7WAY0{!kF!$Aj~)vhS>*tl`ZRhV317tLb1#~U=RdTtzb|DlP(Mf zWr`05hcV>_lmPe==FeDQ2nf*q*2@ro!J(OPNZ4Owf5D;POf`U^5zO&nnAZ|`vltWC z;4lPJo#6=P+~6n(lMgrs#*`ZZ!psc>6wOp;1RTSZF9L~W@_|6Zn02wtwL(HsOm#uR zft|K)J&tlgJ;Y_@TLNVC&_MjLP zhFOM2uCEmo3tU&$gG8%eq(q zO(tF;u@I)*kihkGeS82jnBqfX0pL0xC@2K_n=XS!aB?NnR=+1Taq=Yt$20gxUjWtu tXbh`O0fYkTxH{H8D)*oIviyIZv01yo5@?LI27rPBX9}0J^nt@V{{k>oq9OnQ diff --git a/typst_temp/main.typ b/typst_temp/main.typ deleted file mode 100644 index f91f2253..00000000 --- a/typst_temp/main.typ +++ /dev/null @@ -1,32 +0,0 @@ -#import "template.typ": cover, conf - -// ===== 封面 ===== -// 字段均可选,自动排版:留空的字段会自动跳过。 -#cover( - title: "Very长的文档标题示", - title-en: "Very Long Document Title Example", - subtitle: "副标题 Subtitle", - date: "2026 年 6 月", -) - -// ===== 正文 ===== -#show: conf.with(number-pages: true) - -= 第一章 引言 - -这是正文示例。正文中文使用宋体,English text uses Times New Roman by default。 -段落首行自动缩进两字符,两端对齐。 - -== 1.1 小节 - -行内代码示例:`let x = 1`,以及代码块: - -```rust -fn main() { - println!("Hello, Typst!"); -} -``` - -= 第二章 结论 - -这里是结论部分的内容。 diff --git a/typst_temp/template.typ b/typst_temp/template.typ deleted file mode 100644 index 96cbbba1..00000000 --- a/typst_temp/template.typ +++ /dev/null @@ -1,172 +0,0 @@ -// ============================================================ -// A4 文档模板 -// 封面:页边距 0,中文黑体 / 英文 Source Code Pro -// 正文:上下 3.5cm,左右 2cm,中文宋体 / 英文 Times New Roman -// 代码块:Source Code Pro -// ============================================================ - -// ---- 字体配置(可按需修改)---- -#let cover-cjk = "SimHei" // 封面中文:黑体 -#let cover-latin = "Source Code Pro" // 封面英文 -#let body-cjk = "SimSun" // 正文中文:宋体 -#let body-latin = "Times New Roman" // 正文英文:新罗马 -#let mono-font = "Source Code Pro" // 代码块 - -// ---- 封面网格系统 ---- -// 坐标系:左上角为原点,x 向右、y 向下;坐标记为 (x, y) 即 (列, 行)。 -// 1 单位 = 1.2cm,一个单位方块为边长 1.5cm 的正方形。 -#let unit = 1.2cm -#let sky = rgb("#4FC3F7") // 天蓝色 -#let ochre = rgb("#C9A24B") // 土黄色 - -// 在网格 (x, y) 处放置内容(以页面左上角为基准) -#let cell(x, y, body) = place(top + left, dx: x * unit, dy: y * unit, body) - -// 实心单位正方形 -#let sq(x, y, color) = cell(x, y, rect(width: unit, height: unit, fill: color, stroke: none)) - -// 单位等腰直角三角形,corner 指定直角所在角: -// "tl" 左上 / "tr" 右上 / "bl" 左下 / "br" 右下 -#let tri(x, y, corner, color) = cell(x, y, { - let pts = if corner == "tr" { ((0pt, 0pt), (unit, 0pt), (unit, unit)) } - else if corner == "tl" { ((0pt, 0pt), (unit, 0pt), (0pt, unit)) } - else if corner == "bl" { ((0pt, 0pt), (0pt, unit), (unit, unit)) } - else { ((unit, 0pt), (unit, unit), (0pt, unit)) } - polygon(fill: color, stroke: none, ..pts) -}) - -// 单元格字母字号(NITLOAD 与主标题共用,保证视觉一致) -#let glyph-size = 30pt - -// 在单位格内居中放置一个字母 -#let glyph(x, y, ch, color) = cell(x, y, box( - width: unit, height: unit, inset: 0pt, - align(center + horizon, text(font: cover-cjk, size: glyph-size, weight: "medium", fill: color)[#ch]), -)) - -// ---- 封面 ---- -// 所有字段均可选,留空则自动跳过,实现自动排版。 -#let cover( - title: none, // 主标题(中文) - title-en: none, // 主标题(英文,Source Code Pro 自行排版) - subtitle: none, // 副标题 - logo: none, // 图片路径,如 image("logo.png") - author: none, // 作者(可为单个字符串或数组) - info: (:), // 附加信息字典,如 (学号: "123", 指导教师: "张三") - date: none, // 日期 - fill: white, // 封面背景色 - decorate: true, // 是否绘制天蓝/土黄网格装饰色块 -) = { - // 封面页:页边距为 0 - set page( - paper: "a4", - margin: 0pt, - fill: fill, - header: none, - footer: none, - ) - set text(font: (cover-latin, cover-cjk), lang: "zh") - - // 网格装饰:NITLOAD 标识(坐标 (x, y) = (列, 行))。 - if decorate { - // 天蓝色实心单位正方形 - // 土黄色实心单位正方形 - sq(0, 0, sky); sq(1, 0, sky); sq(2, 0, ochre) - sq(0, 1, sky); sq(2, 1, sky) - sq(0, 2, sky); sq(2, 2, sky) - sq(0, 3, sky); sq(2, 3, sky) - sq(0, 4, sky); sq(2, 4, sky) - sq(0, 5, sky); sq(2, 5, sky) - - // NITLOAD 竖排字样(列 x=1, 行 y=0..6),N 为土黄,其余天蓝 - let letters = ("N", "I", "T", "L", "O", "A", "D") - for (i, ch) in letters.enumerate() { - glyph(1, i, ch, if i == 0 { ochre } else { sky }) - } - - // 主标题:中文逐字对齐网格,英文居中于单元格行。 - // 第 1 列到右缘的整格数 = 中文每行字数。 - let title-cols = calc.floor((21cm - 1 * unit) / unit) - let title-width = 21cm - 1 * unit - let cs = if type(title) == str { title.clusters() } else { () } - - // 中文逐字对齐网格;混排西文字母按 2/3 格宽打包,预留整数格。 - // 中文视觉重心偏大,整体右移 0.1 格、下移 0.2 格 - let cn-dx = 0.1 * unit - let cn-dy = 0.2 * unit - let is-narrow = c => str.to-unicode(c.codepoints().first()) < 0x2E80 - // 分段:连续西文合为一段,其余每字一段 - let segs = () - let run = "" - for ch in cs { - if is-narrow(ch) { run += ch } - else { - if run != "" { segs.push(run); run = "" } - segs.push(ch) - } - } - if run != "" { segs.push(run) } - - // 逐段放置:CJK 占 1 格,西文段占 ceil(字数 * 1/2) 格,溢出换行 - let cur = 0 - let row = 0 - for seg in segs { - let w = if is-narrow(seg.first()) { calc.ceil(seg.len() * 1 / 2) } else { 1 } - if cur + w > title-cols { cur = 0; row += 1 } - let col = 1 + cur - place(top + left, dx: col * unit + cn-dx, dy: (7 + row) * unit + cn-dy, - box(width: w * unit, height: unit, inset: 0pt, align(center + horizon, - text(font: (cover-cjk, cover-latin), size: glyph-size, fill: black)[#seg]))) - cur += w - } - let cn-rows = if segs.len() == 0 { 0 } else { row + 1 } - - // 英文:左对齐于中文下方;多行时行距对齐网格(行距 = unit),左/上边距相等。 - // 英文行数需测量,故把依赖行数的笔画/三角形一并放入 context。 - context { - let en-rows = 0 - if title-en != none { - let en1 = text(font: (cover-latin, cover-cjk), size: glyph-size, fill: black)[#title-en] - let lh = measure(en1).height - let pad = (unit - lh) / 2 - let inner = { - set par(leading: unit - lh) - en1 - } - let h = measure(box(width: title-width - pad, inner)).height - en-rows = calc.max(1, int(calc.round((h - lh) / unit)) + 1) - cell(1, 7 + cn-rows, box(width: title-width, inset: (left: pad, top: pad), inner)) - } - let title-rows = cn-rows + en-rows - // 左侧一笔向下延伸至主标题下端,三角形收尾;右侧保持原样 - for r in range(6, 6 + title-rows) { sq(0, r, sky) } - tri(0, 6 + title-rows, "tr", sky) - } - tri(2, 6, "tl", sky) - } - - pagebreak(weak: true) -} - -// ---- 正文配置 ---- -#let conf( - number-pages: true, // 是否显示页码 - doc, -) = { - set page( - paper: "a4", - margin: (top: 3.5cm, bottom: 3.5cm, left: 2cm, right: 2cm), - numbering: if number-pages { "1" } else { none }, - number-align: center, - ) - set text(font: (body-latin, body-cjk), lang: "zh", size: 12pt) - set par(justify: true, leading: 1em, first-line-indent: (amount: 2em, all: true)) - - // 标题字体:中文黑体,英文 Source Code Pro - show heading: set text(font: (cover-latin, cover-cjk)) - - // 代码块与行内代码:Source Code Pro - show raw: set text(font: mono-font) - - doc -} From 0cef5aa51761c7550878a0c5d370ea9c41936bd7 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:55:42 +0800 Subject: [PATCH 2/2] Implement L4 Swiss map and benchmarks --- src/Nitload.Benchmark/Program.cs | 13 +- src/Nitload.Benchmark/SwissX86Bench.cs | 306 ++++++++++++++++++ .../SwissX86DisassemblyBench.cs | 135 ++++++++ src/Nitload.Common/Global.cs | 1 + .../HighPerformance/Memory/L4.CacheLine.cs | 19 +- .../HighPerformance/Memory/L4.Map.cs | 98 ++++++ .../HighPerformance/Memory/L4.Predict.cs | 22 ++ .../HighPerformance/Memory/L4.SwissTable.cs | 206 ++++++------ .../HighPerformance/Memory/L4.Swiss_X86.cs | 29 +- .../HighPerformance/Memory/L4.cs | 6 + 10 files changed, 709 insertions(+), 126 deletions(-) create mode 100644 src/Nitload.Benchmark/SwissX86Bench.cs create mode 100644 src/Nitload.Benchmark/SwissX86DisassemblyBench.cs create mode 100644 src/Nitload.Common/HighPerformance/Memory/L4.Map.cs create mode 100644 src/Nitload.Common/HighPerformance/Memory/L4.Predict.cs diff --git a/src/Nitload.Benchmark/Program.cs b/src/Nitload.Benchmark/Program.cs index 43c14f8e..129310a8 100644 --- a/src/Nitload.Benchmark/Program.cs +++ b/src/Nitload.Benchmark/Program.cs @@ -9,16 +9,23 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; -using BenchmarkDotNet.Toolchains.InProcess.Emit; using Nitload.Benchmark; +if (args.Length > 0 && args[0] == "--disasm-swiss") +{ + IConfig disassemblyConfig = ManualConfig.Create(DefaultConfig.Instance) + .AddLogger(ConsoleLogger.Default) + .AddJob(Job.ShortRun.WithWarmupCount(6)); + BenchmarkRunner.Run(disassemblyConfig); + return; +} + IConfig config = ManualConfig.Create(DefaultConfig.Instance) .AddLogger(ConsoleLogger.Default) .AddJob(Job.Default - .WithToolchain(InProcessEmitToolchain.Instance) .WithLaunchCount(1) .WithWarmupCount(6) .WithIterationCount(15) .WithInvocationCount(3) .WithUnrollFactor(1)); -_ = BenchmarkRunner.Run(config); +BenchmarkSwitcher.FromAssembly(typeof(TlsfAllocatorBench).Assembly).Run(args, config); diff --git a/src/Nitload.Benchmark/SwissX86Bench.cs b/src/Nitload.Benchmark/SwissX86Bench.cs new file mode 100644 index 00000000..29eac81a --- /dev/null +++ b/src/Nitload.Benchmark/SwissX86Bench.cs @@ -0,0 +1,306 @@ +// ----------------------------------------------------------------------------- +// file="SwissX86Bench" +// Project: nFramework +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using Nitload.Common.HighPerformance.Memory; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Threading; +using System.Threading.Tasks; + +namespace Nitload.Benchmark +{ + [MemoryDiagnoser] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [CategoriesColumn] + public unsafe class SwissX86Bench + { + + private const int QueryCount = 1 << 21; + private const int ConcurrentQueryCount = QueryCount * 6; + private const int MaximumResidentCount = 8192; + + private byte* _buffer; + private L4.SwissTable _swiss; + private Dictionary _dictionary = null!; + private ConcurrentDictionary _concurrentDictionary = null!; + private L4.AddressSource[] _hitQueries = []; + private L4.AddressSource[] _missQueries = []; + private L4.AddressSource[] _writerQueries = []; + private ulong[] _writerValues = []; + private byte* _readBuffer; + private byte* _writeBuffer; + private ParallelOptions _parallelOptions = null!; + private Func _parallelLocalInit = null!; + private Func _swissParallelBody = null!; + private Func _concurrentDictionaryParallelBody = null!; + private Action _parallelLocalFinally = null!; + private long _parallelChecksum; + + [Params(25, 50, 100)] + public int LoadPercent { get; set; } + + private int ResidentCount => MaximumResidentCount * LoadPercent / 100; + + [GlobalSetup] + public void Setup() + { + if (!Sse42.X64.IsSupported) + throw new PlatformNotSupportedException("The Swiss X86 benchmark requires x64 SSE4.2."); + + _buffer = (byte*)NativeMemory.AlignedAlloc((nuint)L4.BufferSize, 64); + if (_buffer == null) + throw new OutOfMemoryException("Could not allocate Swiss benchmark tables."); + + _swiss = L4.SwissTable.Create(_buffer); + _dictionary = new Dictionary(ResidentCount); + _concurrentDictionary = new ConcurrentDictionary( + Math.Max(1, Math.Min(Environment.ProcessorCount, 8)), + ResidentCount); + + L4.AddressSource[] residentKeys = new L4.AddressSource[ResidentCount]; + for (int i = 0; i < ResidentCount; i++) + { + L4.AddressSource key = MakeKey(i); + ulong value = (ulong)i + 1; + residentKeys[i] = key; + if (!_swiss.TryAddOrSet(key, value)) + throw new InvalidOperationException($"Swiss insertion failed while seeding key {i}."); + _dictionary.Add(key, value); + _concurrentDictionary.TryAdd(key, value); + } + + _swiss.SwapBuffers(); + for (int i = 0; i < ResidentCount; i++) + { + if (!_swiss.TryAddOrSet(residentKeys[i], (ulong)i + 1)) + throw new InvalidOperationException($"Swiss writer-buffer seeding failed for key {i}."); + } + + _readBuffer = _swiss.ReadBufferForLockFreeBenchmark; + _writeBuffer = _swiss.WriteBufferForLockFreeBenchmark; + + _hitQueries = new L4.AddressSource[QueryCount]; + _missQueries = new L4.AddressSource[QueryCount]; + _writerQueries = new L4.AddressSource[QueryCount]; + _writerValues = new ulong[QueryCount]; + Random random = new(0x51_15_2026); + for (int i = 0; i < QueryCount; i++) + { + _hitQueries[i] = residentKeys[random.Next(ResidentCount)]; + _missQueries[i] = MakeKey(MaximumResidentCount + i); + _writerQueries[i] = residentKeys[random.Next(ResidentCount)]; + _writerValues[i] = (ulong)i + 0x10000; + } + + _parallelOptions = new ParallelOptions + { + MaxDegreeOfParallelism = Math.Max(2, Math.Min(Environment.ProcessorCount, 8)) + }; + _parallelLocalInit = static() => 0UL; + _swissParallelBody = ReadSwissParallel; + _concurrentDictionaryParallelBody = ReadConcurrentDictionaryParallel; + _parallelLocalFinally = MergeParallelChecksum; + + ValidateLookupResults(); + } + + [GlobalCleanup] + public void Cleanup() + { + _swiss.Dispose(); + NativeMemory.AlignedFree(_buffer); + _buffer = null; + } + + [Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Hit lookup")] + public ulong DictionaryHitLookup() + { + ulong checksum = 0; + for (int i = 0; i < QueryCount; i++) + checksum += _dictionary[_hitQueries[i]]; + return checksum; + } + + [Benchmark(OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Hit lookup")] + public ulong SwissLockFreeHitLookup() + { + ulong checksum = 0; + for (int i = 0; i < QueryCount; i++) + { + if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[i], out ulong value)) + throw new InvalidOperationException("Swiss table missed a seeded hit key."); + checksum += value; + } + return checksum; + } + + [Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Miss lookup")] + public int DictionaryMissLookup() + { + int misses = 0; + for (int i = 0; i < QueryCount; i++) + misses += _dictionary.TryGetValue(_missQueries[i], out _) ? 0 : 1; + return misses; + } + + [Benchmark(OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Miss lookup")] + public int SwissLockFreeMissLookup() + { + int misses = 0; + for (int i = 0; i < QueryCount; i++) + misses += L4.SwissReaderX86.TryGet(_readBuffer, _missQueries[i], out _) ? 0 : 1; + return misses; + } + + [Benchmark(Baseline = true, OperationsPerInvoke = ConcurrentQueryCount)] + [BenchmarkCategory("Concurrent hit lookup")] + public ulong ConcurrentDictionaryConcurrentHitLookup() + { + Interlocked.Exchange(ref _parallelChecksum, 0); + Parallel.For( + 0, + ConcurrentQueryCount, + _parallelOptions, + _parallelLocalInit, + _concurrentDictionaryParallelBody, + _parallelLocalFinally); + return unchecked((ulong)Interlocked.Read(ref _parallelChecksum)); + } + + [Benchmark(OperationsPerInvoke = ConcurrentQueryCount)] + [BenchmarkCategory("Concurrent hit lookup")] + public ulong SwissLockFreeConcurrentHitLookup() + { + Interlocked.Exchange(ref _parallelChecksum, 0); + Parallel.For( + 0, + ConcurrentQueryCount, + _parallelOptions, + _parallelLocalInit, + _swissParallelBody, + _parallelLocalFinally); + return unchecked((ulong)Interlocked.Read(ref _parallelChecksum)); + } + + [Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Upsert existing")] + public ulong DictionaryUpsertExisting() + { + for (int i = 0; i < QueryCount; i++) + _dictionary[_writerQueries[i]] = _writerValues[i]; + return _dictionary[_writerQueries[^1]]; + } + + [Benchmark(OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Upsert existing")] + public ulong SwissUpsertExisting() + { + ulong failures = 0; + for (int i = 0; i < QueryCount; i++) + failures += L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _writerQueries[i], _writerValues[i]) ? 0UL : 1UL; + if (failures != 0 || !L4.SwissReaderX86.TryGet(_writeBuffer, _writerQueries[^1], out ulong value)) + throw new InvalidOperationException("Swiss upsert failed to preserve the final value."); + return value; + } + + [Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Remove and reinsert")] + public ulong DictionaryRemoveAndReinsert() + { + ulong checksum = 0; + for (int i = 0; i < QueryCount; i++) + { + L4.AddressSource key = _writerQueries[i]; + ulong value = _dictionary[key]; + _dictionary.Remove(key); + _dictionary.Add(key, value + 1); + checksum += value; + } + return checksum; + } + + [Benchmark(OperationsPerInvoke = QueryCount)] + [BenchmarkCategory("Remove and reinsert")] + public ulong SwissRemoveAndReinsert() + { + ulong checksum = 0; + for (int i = 0; i < QueryCount; i++) + { + L4.AddressSource key = _writerQueries[i]; + if (!L4.SwissWriterX86.TryRemove(_writeBuffer, key, out ulong value) || + !L4.SwissWriterX86.TryAddOrSet(_writeBuffer, key, value + 1)) { + throw new InvalidOperationException("Swiss remove/reinsert failed."); + } + checksum += value; + } + return checksum; + } + + private static L4.AddressSource MakeKey(int index) + { + return new L4.AddressSource + { + Address = 0x1000_0000UL + ((ulong)index * 64UL), + Description = ((ulong)index * 0x9e37_79b9UL) ^ 0xd1b5_4a32_d192_ed03UL + }; + } + + private ulong ReadSwissParallel(int index, ParallelLoopState state, ulong checksum) + { + int queryIndex = index & (QueryCount - 1); + if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[queryIndex], out ulong value)) + throw new InvalidOperationException("Swiss table missed a seeded concurrent hit key."); + return checksum + value; + } + + private ulong ReadConcurrentDictionaryParallel(int index, ParallelLoopState state, ulong checksum) + { + int queryIndex = index & (QueryCount - 1); + if (!_concurrentDictionary.TryGetValue(_hitQueries[queryIndex], out ulong value)) + throw new InvalidOperationException("ConcurrentDictionary missed a seeded hit key."); + return checksum + value; + } + + private void MergeParallelChecksum(ulong checksum) + { + _ = Interlocked.Add(ref _parallelChecksum, unchecked((long)checksum)); + } + + private void ValidateLookupResults() + { + for (int i = 0; i < _hitQueries.Length; i++) + { + bool dictionaryFound = _dictionary.TryGetValue(_hitQueries[i], out ulong dictionaryValue); + bool swissFound = L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[i], out ulong swissValue); + if (!dictionaryFound || !swissFound || dictionaryValue != swissValue) + throw new InvalidOperationException("Swiss and Dictionary hit results differ."); + + if (!_concurrentDictionary.TryGetValue(_hitQueries[i], out ulong concurrentValue) || + concurrentValue != dictionaryValue) + throw new InvalidOperationException("ConcurrentDictionary and Dictionary hit results differ."); + } + + for (int i = 0; i < _missQueries.Length; i++) + { + bool dictionaryFound = _dictionary.TryGetValue(_missQueries[i], out _); + bool swissFound = L4.SwissReaderX86.TryGet(_readBuffer, _missQueries[i], out _); + if (dictionaryFound || swissFound) + throw new InvalidOperationException("Swiss and Dictionary miss results differ."); + } + } + + } +} \ No newline at end of file diff --git a/src/Nitload.Benchmark/SwissX86DisassemblyBench.cs b/src/Nitload.Benchmark/SwissX86DisassemblyBench.cs new file mode 100644 index 00000000..2461abd4 --- /dev/null +++ b/src/Nitload.Benchmark/SwissX86DisassemblyBench.cs @@ -0,0 +1,135 @@ +// ----------------------------------------------------------------------------- +// file="SwissX86DisassemblyBench" +// Project: nFramework +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using Nitload.Common.HighPerformance.Memory; +using System; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; + +namespace Nitload.Benchmark +{ + [DisassemblyDiagnoser(maxDepth: 3, exportHtml: true)] + public unsafe class SwissX86DisassemblyBench + { + + private const int ResidentCount = 8192; + + private byte* _buffer; + private L4.SwissTable _table; + private byte* _readBuffer; + private byte* _writeBuffer; + private L4.AddressSource _hitKey; + private L4.AddressSource _missKey; + private ulong _value; + + [GlobalSetup] + public void Setup() + { + if (!Sse42.X64.IsSupported) + throw new PlatformNotSupportedException("The Swiss X86 disassembly probe requires x64 SSE4.2."); + + _buffer = (byte*)NativeMemory.AlignedAlloc((nuint)L4.BufferSize, 64); + if (_buffer == null) + throw new OutOfMemoryException("Could not allocate Swiss disassembly buffers."); + + _table = L4.SwissTable.Create(_buffer); + for (int index = 0; index < ResidentCount; index++) + { + if (!L4.SwissWriterX86.TryAddOrSet(_table.WriteBufferForLockFreeBenchmark, + MakeKey(index), (ulong)index + 1)) + throw new InvalidOperationException($"Failed to seed Swiss key {index}."); + } + + _table.SwapBuffers(); + _readBuffer = _table.ReadBufferForLockFreeBenchmark; + _writeBuffer = _table.WriteBufferForLockFreeBenchmark; + for (int index = 0; index < ResidentCount; index++) + { + if (!L4.SwissWriterX86.TryAddOrSet(_writeBuffer, MakeKey(index), (ulong)index + 1)) + throw new InvalidOperationException($"Failed to seed inactive Swiss key {index}."); + } + + _hitKey = MakeKey(ResidentCount / 2); + _missKey = MakeKey(ResidentCount + 1); + _value = 0x1234_5678; + + if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitKey, out _) || + L4.SwissReaderX86.TryGet(_readBuffer, _missKey, out _)) + throw new InvalidOperationException("Swiss reader setup validation failed."); + } + + [GlobalCleanup] + public void Cleanup() + { + _table.Dispose(); + NativeMemory.AlignedFree(_buffer); + _buffer = null; + } + + [Benchmark] + public bool RawReaderHit() + { + return L4.SwissReaderX86.TryGet(_readBuffer, _hitKey, out _value); + } + + [Benchmark] + public bool RawReaderMiss() + { + return L4.SwissReaderX86.TryGet(_readBuffer, _missKey, out _value); + } + + [Benchmark] + public bool RawWriterUpsertExisting() + { + return L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _hitKey, _value); + } + + [Benchmark] + public bool RawWriterRemoveAndReinsert() + { + return L4.SwissWriterX86.TryRemove(_writeBuffer, _hitKey, out ulong previousValue) && + L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _hitKey, previousValue); + } + + [Benchmark] + public bool TableReaderHit() + { + return _table.TryGet(_hitKey, out _value); + } + + [Benchmark] + public bool TableReaderMiss() + { + return _table.TryGet(_missKey, out _value); + } + + [Benchmark] + public bool TableWriterUpsertExisting() + { + return _table.TryAddOrSet(_hitKey, _value); + } + + [Benchmark] + public bool TableWriterRemoveAndReinsert() + { + return _table.TryRemove(_hitKey, out ulong previousValue) && + _table.TryAddOrSet(_hitKey, previousValue); + } + + private static L4.AddressSource MakeKey(int index) + { + return new L4.AddressSource + { + Address = 0x1000_0000UL + ((ulong)index * 64UL), + Description = ((ulong)index * 0x9e37_79b9UL) ^ 0xd1b5_4a32_d192_ed03UL + }; + } + + } +} \ No newline at end of file diff --git a/src/Nitload.Common/Global.cs b/src/Nitload.Common/Global.cs index be87319d..d8a196f4 100644 --- a/src/Nitload.Common/Global.cs +++ b/src/Nitload.Common/Global.cs @@ -15,4 +15,5 @@ global using System.Threading; [assembly: InternalsVisibleTo("Nitload.RegularAnalysis")] [assembly: InternalsVisibleTo("Nitload.Graphics")] [assembly: InternalsVisibleTo("Nitload.Mathematics")] +[assembly: InternalsVisibleTo("Nitload.Benchmark")] [assembly: InternalsVisibleTo("StgSharpDebug")] diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.CacheLine.cs b/src/Nitload.Common/HighPerformance/Memory/L4.CacheLine.cs index 7c605e60..5d03dedb 100644 --- a/src/Nitload.Common/HighPerformance/Memory/L4.CacheLine.cs +++ b/src/Nitload.Common/HighPerformance/Memory/L4.CacheLine.cs @@ -27,6 +27,8 @@ namespace Nitload.Common.HighPerformance.Memory internal const uint MaxBufferSize = 0xFFFFFFFF - 64U; + [FieldOffset(16)] internal AllocationState State; + public static uint SizeOf { get; } = (uint)Unsafe.SizeOf(); #region tlsf linked list @@ -43,20 +45,27 @@ namespace Nitload.Common.HighPerformance.Memory [FieldOffset(8)] public uint PositionMask; [FieldOffset(12)] public uint SizeMask; - #endregion - - [FieldOffset(16)] internal AllocationState State; - + #endregion } #pragma warning disable IDE0032 [StructLayout(LayoutKind.Explicit, Size = 16)] - public struct Handle : IDisposable + public unsafe struct Handle : IDisposable { [FieldOffset(0)] private nuint _handle; + [FieldOffset(8)] private nuint _reserved; + internal Handle( + nuint handle, + nuint reserved + ) + { + _handle = handle; + _reserved = reserved; + } + [Obsolete("You should not create a CacheLineHandle instance outside a L4", true)] public Handle() { } diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.Map.cs b/src/Nitload.Common/HighPerformance/Memory/L4.Map.cs new file mode 100644 index 00000000..0b718658 --- /dev/null +++ b/src/Nitload.Common/HighPerformance/Memory/L4.Map.cs @@ -0,0 +1,98 @@ +// ----------------------------------------------------------------------------- +// file="L4.Map" +// Project: StgSharp +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Nitload.Common.HighPerformance.Memory +{ + public unsafe partial class L4 + { + + private const int _low_predict = 512; + + private int _predict_count; + private int _swiss_reader_counter; + private int _swiss_swap_lock; + private int _swiss_writer_lock; + + public partial Handle Map( + AddressSource source + ) + { + ulong handle; + if (Volatile.Read(ref _predict_count) < _low_predict) + { + if (Interlocked.CompareExchange(ref _swiss_writer_lock, 1, 0) == 0) + { + try + { + Predict(); + _ = Interlocked.Exchange(ref _swiss_swap_lock, 1); + while (Volatile.Read(ref _swiss_reader_counter) != 0) { } + + _addressMap.SwapBuffers(); + if (_addressMap.TryGet(source, out handle)) { + throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}]."); + } + } + finally + { + _ = Interlocked.Exchange(ref _swiss_swap_lock, 0); + _ = Interlocked.Exchange(ref _swiss_writer_lock, 0); + } + } else if (!TryGetAddress(source, out handle)) { + throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}]."); + } + } else if (!TryGetAddress(source, out handle)) { + throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}]."); + } + + CacheLineMetadata* metadata = (CacheLineMetadata*)handle; + + // process metadata, unfinished + uint offset = metadata->PositionMask; + nuint address = /*base ptr + */ offset; + _ = Interlocked.Decrement(ref _predict_count); + + return new Handle(address, (nuint)handle); + } + + private bool TryGetAddress( + AddressSource source, + out ulong handle + ) + { + while (true) + { + while (Volatile.Read(ref _swiss_swap_lock) != 0) { } + + _ = Interlocked.Increment(ref _swiss_reader_counter); + if (Volatile.Read(ref _swiss_swap_lock) == 0) + { + break; + } + + _ = Interlocked.Decrement(ref _swiss_reader_counter); + } + + try + { + return _addressMap.TryGet(source, out handle); + } + finally + { + _ = Interlocked.Decrement(ref _swiss_reader_counter); + } + } + + } +} diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.Predict.cs b/src/Nitload.Common/HighPerformance/Memory/L4.Predict.cs new file mode 100644 index 00000000..333fe530 --- /dev/null +++ b/src/Nitload.Common/HighPerformance/Memory/L4.Predict.cs @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------------- +// file="L4.Predict" +// Project: StgSharp +// Copyright (c) Nitload. +// SPDX-License-Identifier: MIT +// ----------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nitload.Common.HighPerformance.Memory +{ + public unsafe partial class L4 + { + + internal partial void Predict() { } + + } +} diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.SwissTable.cs b/src/Nitload.Common/HighPerformance/Memory/L4.SwissTable.cs index f87d40bd..42c6decf 100644 --- a/src/Nitload.Common/HighPerformance/Memory/L4.SwissTable.cs +++ b/src/Nitload.Common/HighPerformance/Memory/L4.SwissTable.cs @@ -8,7 +8,7 @@ using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; +using System.Runtime.Intrinsics.X86; namespace Nitload.Common.HighPerformance.Memory { @@ -16,8 +16,6 @@ namespace Nitload.Common.HighPerformance.Memory { private const int BucketCapacity = 16; - private const uint CrcMagic = 0x7f1c9961; - private const int GroupCount = 1 << GroupCountLog; private const int GroupCountLog = 10; private const int IdealCount = GroupCount * BucketCapacity;//16384 @@ -36,17 +34,24 @@ namespace Nitload.Common.HighPerformance.Memory } [StructLayout(LayoutKind.Sequential)] - internal struct AddressSource + public struct AddressSource : IEquatable { public ulong Address; public ulong Description; - public override readonly bool Equals( - object obj + public readonly bool Equals( + AddressSource other ) { - return obj is AddressSource source && this == source; + return this == other; + } + + public override readonly bool Equals( + object? obj + ) + { + return obj is AddressSource source && Equals(source); } public override readonly int GetHashCode() @@ -88,127 +93,102 @@ namespace Nitload.Common.HighPerformance.Memory } - internal sealed unsafe class SwissTable : IDisposable + internal unsafe struct SwissTable : IDisposable { - private byte* _buffer_read; private byte* _buffer_write; - private readonly ReaderWriterLockSlim _synchronization = new(LockRecursionPolicy.NoRecursion); - private SwissTable(byte* buffers) + private ulong _published_buffer; + + private SwissTable( + byte* buffers + ) { - _buffer_read = buffers; + _published_buffer = (ulong)buffers; _buffer_write = buffers + TableBufferSize; - InitializeBuffer(_buffer_read); + InitializeBuffer(buffers); InitializeBuffer(_buffer_write); } - internal static SwissTable Create(byte* buffers) + internal byte* ReadBufferForLockFreeBenchmark => (byte*)Volatile.Read(ref _published_buffer); + + internal readonly byte* WriteBufferForLockFreeBenchmark => _buffer_write; + + public void Dispose() + { + _ = Interlocked.Exchange(ref _published_buffer, 0); + _buffer_write = null; + } + + ///

+ /// Publishes the write buffer. The caller must provide the phase barrier around this + /// operation. + /// + public void SwapBuffers() + { + ulong previousBuffer = Interlocked.Exchange(ref _published_buffer, (ulong)_buffer_write); + _buffer_write = (byte*)previousBuffer; + } + + /// + /// Writes to the inactive buffer. The caller must serialize all writes. + /// + public readonly bool TryAddOrSet( + AddressSource key, + ulong value + ) + { + if (Sse42.IsSupported) { + return SwissWriterX86.TryAddOrSet(_buffer_write, key, value); + } + + throw new NotImplementedException("The Swiss backend is not implemented for this ISA."); + } + + /// + /// Reads the published buffer. The caller must prevent swaps until all reads finish. + /// + public bool TryGet( + AddressSource key, + out ulong value + ) + { + byte* publishedBuffer = (byte*)Volatile.Read(ref _published_buffer); + if (Sse42.IsSupported) { + return SwissReaderX86.TryGet(publishedBuffer, key, out value); + } + + throw new NotImplementedException("The Swiss backend is not implemented for this ISA."); + } + + /// + /// Removes from the inactive buffer. The caller must serialize all writes. + /// + public bool TryRemove( + AddressSource key, + out ulong value + ) + { + if (Sse42.IsSupported) { + return SwissWriterX86.TryRemove(_buffer_write, key, out value); + } + + throw new NotImplementedException("The Swiss backend is not implemented for this ISA."); + } + + internal static SwissTable Create( + byte* buffers + ) { ArgumentNullException.ThrowIfNull(buffers); return new SwissTable(buffers); } - /// - /// Reads the published buffer. Callers must prevent a swap while the read is in progress. - /// - public bool TryGet(AddressSource key, out ulong value) + private static void InitializeBuffer( + byte* buffer + ) { - _synchronization.EnterReadLock(); - try - { - return SwissReaderX86.TryGet(_buffer_read, key, out value); - } - finally - { - _synchronization.ExitReadLock(); - } - } - - /// - /// Writes to the inactive buffer. Calls must be serialized by the caller. - /// - public bool TryAddOrSet(AddressSource key, ulong value) - { - _synchronization.EnterWriteLock(); - try - { - return SwissWriterX86.TryAddOrSet(_buffer_write, key, value); - } - finally - { - _synchronization.ExitWriteLock(); - } - } - - /// - /// Writes to the inactive buffer. Calls must be serialized by the caller. - /// - public bool TryRemove(AddressSource key, out ulong value) - { - _synchronization.EnterWriteLock(); - try - { - return SwissWriterX86.TryRemove(_buffer_write, key, out value); - } - finally - { - _synchronization.ExitWriteLock(); - } - } - - /// - /// Clears the inactive buffer. Call only after all readers of its previous generation have quiesced. - /// - public void PrepareWriteBuffer() - { - _synchronization.EnterWriteLock(); - try - { - InitializeBuffer(_buffer_write); - } - finally - { - _synchronization.ExitWriteLock(); - } - } - - /// - /// Publishes the write buffer. The caller must provide the phase barrier around this operation. - /// - public void SwapBuffers() - { - _synchronization.EnterWriteLock(); - try - { - byte* temporary = _buffer_read; - _buffer_read = _buffer_write; - _buffer_write = temporary; - } - finally - { - _synchronization.ExitWriteLock(); - } - } - - public void Dispose() - { - _synchronization.EnterWriteLock(); - try - { - _buffer_read = null; - _buffer_write = null; - } - finally - { - _synchronization.ExitWriteLock(); - } - _synchronization.Dispose(); - } - - private static void InitializeBuffer(byte* buffer) - { - Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, (uint)IdealCount); + Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, IdealCount); } } diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs b/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs index a92933c1..f2edc6b2 100644 --- a/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs +++ b/src/Nitload.Common/HighPerformance/Memory/L4.Swiss_X86.cs @@ -6,6 +6,7 @@ // ----------------------------------------------------------------------------- using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -14,7 +15,25 @@ namespace Nitload.Common.HighPerformance.Memory public partial class L4 { - private readonly unsafe struct SwissWriterX86 + private const uint CrcMagic = 0x7f1c9961; + + internal static class SwissHashX64 + { + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Compute( + AddressSource key + ) + { + ulong hash = CrcMagic; + hash = Sse42.X64.Crc32(hash, key.Address); + hash = Sse42.X64.Crc32(hash, key.Description); + return (uint)hash; + } + + } + + internal readonly unsafe struct SwissWriterX86 { public static bool TryAddOrSet( @@ -26,7 +45,7 @@ namespace Nitload.Common.HighPerformance.Memory Vector128* control_bytes = (Vector128*)buffer; SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount); - uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode()); + uint hash = SwissHashX64.Compute(key); uint begin = hash % GroupCount; uint first_tomb_group = 0; @@ -108,7 +127,7 @@ namespace Nitload.Common.HighPerformance.Memory { Vector128* control_bytes = (Vector128*)buffer; SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount); - uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode()); + uint hash = SwissHashX64.Compute(key); uint begin = hash % GroupCount; byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7)); @@ -149,7 +168,7 @@ namespace Nitload.Common.HighPerformance.Memory } - private readonly unsafe struct SwissReaderX86 + internal readonly unsafe struct SwissReaderX86 { public static bool TryGet( @@ -161,7 +180,7 @@ namespace Nitload.Common.HighPerformance.Memory Vector128* control_bytes = (Vector128*)buffer; SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount); - uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode()); + uint hash = SwissHashX64.Compute(key); uint begin = hash % GroupCount; byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7)); diff --git a/src/Nitload.Common/HighPerformance/Memory/L4.cs b/src/Nitload.Common/HighPerformance/Memory/L4.cs index e0c30f3f..54ec138d 100644 --- a/src/Nitload.Common/HighPerformance/Memory/L4.cs +++ b/src/Nitload.Common/HighPerformance/Memory/L4.cs @@ -54,6 +54,10 @@ namespace Nitload.Common.HighPerformance.Memory GC.SuppressFinalize(this); } + public partial Handle Map( + AddressSource source + ); + internal void Collect() { ObjectDisposedException.ThrowIf(_disposed, this); @@ -77,5 +81,7 @@ namespace Nitload.Common.HighPerformance.Memory return _bufferAllocator.TryAlloc(size, out handle); } + internal partial void Predict(); + } }