From 1a3205dbde7abde44cff65a1459823e49bb8e498 Mon Sep 17 00:00:00 2001 From: sistemd Date: Tue, 12 Nov 2024 15:20:01 +0100 Subject: [PATCH] running aggregate stored in memory --- crates/rpc/fixtures/mainnet.sqlite | Bin 454656 -> 417792 bytes crates/rpc/src/method/subscribe_events.rs | 10 +- .../v06/method/trace_block_transactions.rs | 14 + .../rpc/src/v06/method/trace_transaction.rs | 22 +- crates/storage/src/bloom.rs | 103 +++----- crates/storage/src/connection.rs | 16 ++ crates/storage/src/connection/event.rs | 155 +++++++---- crates/storage/src/connection/transaction.rs | 21 +- crates/storage/src/lib.rs | 241 ++++++++++++++++++ crates/storage/src/schema/revision_0066.rs | 114 +++++---- crates/storage/src/test_utils.rs | 48 ++-- 11 files changed, 535 insertions(+), 209 deletions(-) diff --git a/crates/rpc/fixtures/mainnet.sqlite b/crates/rpc/fixtures/mainnet.sqlite index 88b5ca98c3427f34d954d0e5d8cd88fee940f60c..0a5316c3fbb3d6ae1168d27f454e80fa1c0e83f4 100644 GIT binary patch delta 70875 zcmeFa2Y405_dmR|efRF&%}wvQ$xZG}AOu2hfrO9*LMWm45Rw1^(n#p#CZX35B8>DV zML?PYN*56Xk)onVk)k4gRf-}iLHM89z4}GPZ+`FlJn#E|pZEXG^DyUg&YYb&Gdp*7 zcIKR=^hQAGQNM)&mJo`fhQPny|E5<)H)t9{#Y`yVKtyYgNJbs4T6<(JQ)-zTn|hhP z7VDV;g>3E(E{I*v1~W^UK)L{(LiM#OG%vQwY8@_@d3p&xNlB=zHVzHtJ$e;qjK~>Z zoKdeqMs{XNW^qa8s1f-&B^fzma`H?5oadF;H7TKclC^t6o3tdW<3Fi7BJn*N@8fM9 zJhC8bgtbjt=QdGgHc!E)MGLVkL$BERN8$fakgK+y?YnmGk&x!9^&fPtJ-W5;lwuuH zQrsv~w!$B2Q?!n<3i3;!DRB;m!Iu<_$jQ&0kW(klky)ITo9mFH%4&HCJ{h%9S+Csu z?3{6btz*cjf;@bq6nVw|CCAfcV*5^QlX_eKl9}=QWqzK2$%)i++{%y-J<%PfPeTon z2fkT`yzugADArj96+cRzbc~FFC4= zcKc6O==U`ve{|m9oKb(V!tx)jc>cl{Suxm)j2KtRQR6f)!hy;x_~JAa?h6^iax$}_ zO1D2%f-g@)fq2O*XR z?=Ym|%q-;1L+ICcc`W%a?-IS&Oo>(sG!fMIXV9F=1ol~V`<1jc<% zwl%ZZIykd9r*T7TPJUKFHb{y~M&;%YEw&ct6lRXfBszn~TZ;=bvvU6U@mRYj_3m!` zV#vIp|AY-TxpUW~_9>mL9g_OET4)Rd6ot3XM2!{rJ+)T^pE`9=c@nP3LlMY~_vE3O zA+GK)ZeS$o{wO(Wdaf+^I2>rM1>YHrLV``M&Nn8RKV3pPC(ptA(pD?3n}zC|$q)UH zKb$@k1>mt+$QSn?iTr&^a`STF-sKhkp`in!=8cbLA>MOnW^qR0sNAfa=QMEdkthV; z%|aIG4=IixhCJJjfl&veCnGoekBmP+`%c}HQj)q_yCx+kbxrD&nAGjxzb@Bx#gVz$ zQCP`FL3mCUlEqG)yIVW;NK3-Gn#6PkO6NrKrD@Qdm;cD^|EXqfL4U{so zDEQQ^i^|%UWDXul?t<}5`MoRtCXmSJ_m+Rpw?^X1DBcsh%|uGL)g{#j21c~?QQ5Qd(hWrQorIw6;PuIT}ESF2X;M1fDni~6O%bsihzkiv}3kBUm-{(az z97!jTacsq91t>^>Un!C3BhmElAHP)}Kxrj#xJ||RXza66re_yEd4i;oiijiKUbgD z@t6WM2j9#`2{NpU{CY#;6Sv##nTA@gv%0Bd&4jdSNU45WHCET4G$)(M`ZFIf^Oz+1 zDZP&_q@&O+v>bIuX6gjxB&zV2)RaYOX(GSGX$$H1+0rokOWyC)+WoBMoHsu^(Bjmv zV?|8+=C$_jVKU!&xy9vS*KAGY?r&zupY|QQHK)_#sGb`)B%)*8S63u8(Tib2tu1@s zd}HG~L;Kyral3d=hcIXGJ9u)-%C^4w*BX7XzWk$sUYA?WiR*B;b?6}P4W;=>jr9xh z+be0SgMNB7WA=5ry5;@`Db4!~Iq`MvrM-UcGd@2%W@NymV{Jc6YGf#{?ahVS*Z(jj zY)ef3>Sp~;e3A8ItfhIyua>@zA9yWD?|d?EZrh}WhHiqBx8^ggW?mjW`H{gb7sZ!Z;tTZ&fAJUN@}1h^FN+yxWCi!MM>{3YxAUNV8`CeHqbkA4|`N* zIyN-D7;%=`H3V;5F!a@pwy2>w4XQTJ@III1SQ6W(g*-oWZH%pTsKB*hxKp}!%kgJF*ENj`b{?hVgV z?e+)mT()yNx97d|?+R;QRlk(0Qhk!@8OpJJIcvVKblA2>3+yIo^qST&<9=yTc4FI) z`yZVC<;RJSdZyRj{B}}ZUAd-P%J1%+xAFU1@74X+{l^)77AN|++0A3=ojC(rj9-86 z~XBz5TWBsBl7gtlq_v*r7J+={^UNrUe2 z1>RlPz5gig$Np=!o^ub_{I+zal= zQ6j4P=`$;yun}3AKo;SU0bDp9u^CF$ul(RuxY8eIz6w|Rktrc>&xYD1wuH(fAskA*&b zs5wVf9<{h>d~_m)_vdi_S;eK zd*2_Dq$FL9xiflm?_bx1txE0l{-{^Py!suMhqep1zx2a3_lql+wiqn6#rbh`gi}g1 zN?qhTXZ!S_zg)B59D8%xj=0xP*zT^HxvS}~LTUGDf3Tus{+;|EFG&fyT1``x6tAnX7f0Y`t>~aGQfq^+;g=z{($#8e zniz+*70@)XrLIoZ&@{1yp;kk`ip_PkntDKNrmMmH7XBZfSz=RN568j;MvF~wY%4l~ z78~RHO9^d+tt;t>D6yeYSPe73*g(IRv{2u;T!_V?aBpLTdcWK9A59VJK4*#$qgPD} zbqte)XuUI|gxb1NO_P^U3qM^(+xe*HisU18b7D7+ zR^Yo!X#sn#psh4#H8i;py&$|}8RY04#|0VJvVn#M8$dMdFhh|MK(l_NC_B8Dz*x7# zr1bW3+Rk{}S$uv37Y>t=a}|g^Nf~z72$;JXh&w6C&RdKIkU58D+;A)?@|rHILf1f~ z>NSXrS(j1xLKYt)Fv=eq_{efFGI2E>#y_V)(-N^Ef~Um@Hci6F$%N4nEUqZzYzz-z z&`1CI2*@;l86^NVGd*nMEcal)8T+6T=WcCch_UB``A+Y7mcM6ZQff-KhoJnPlE=;ckbHvanl?P;NRMMRzRpETj6x#pbapE$ofvfx?m1KQ4l zJ!;78%fqP4C!S`$w3lpkS*e1I_rfPkzH-$6(mU)s)R?!2%`i2qv%s|p^trN z_FDPrkckr(R9$I0ed&=i?PZ|5Sq%Aq<^d0c-*^L8n zqg|UCkJ}V8;`467u!wuEGK#7Swt5Vj+FJalE~}|Bf!@2?Cw~0pLjGCkeD93%xijyt z9`yRH-5thy7R!5+TQ1CeY1w>iBg(C-*le^lx&>efRpsw<`+6Ja=^IHTUYj#-8_X4ux9-rnVOTsWbFa zst%X;u+sMRZ*C0x*SdSHCN{C1;(HwLvF~V?@%hbmi!XouQ0$ya8dJ4te(kCNtJmMn zmHB`<(%$F>efK35MkOvke7xB(zbt3LeCCVC7ou~|tR0Lqmw~zT#ddv`dmRkuIb~eS zDYecwyD_QXqaBNOtZca~b4=>j!OdLe^8aWqDW&SWsXeUh)*r$;1{aB+IJ8~CzVCgv zqDTI+;&;!~?zSgC;Pv5becjx{3+hw_Sv~%4uDekE+HdVM_8k2(deznuIm7uyCGY=h z$(>`DzG%J4_k4@{&2Z*pFjttD88BrxZ>~D*xH;|3K{LEQ-8H$QrDxc*9|pWOB>(V1 zm$}?On+v5>Su1*m9A4o+v~=>sdB-}beI{?cc4_OB0X;K{mj8Nq=KPYD(m37PbJIhC zzF;hb^Do`MFDUEMv3vMh^U(usZq;g#^H*WPct! zYs;9$1@8}?I440X)9$*pX>&qxoBP}g%#Gqc{e14G1dm7CL)H)J)8J-W zcGe8fJ9jo%@Sgp*;oD-McEk(0>`(GTmcFmZ}-%j}AfOdPQ;WW z{cXlS^sOj#{jq=K*;-3GeX+CF*yi}n^WW}YH}$ypgOr)AW=#{KmSO8}9yk@M`G+>R?B5Ob@CHP};dy3Op-JFkx${bRo=uN|`Q@wxoq zt-_d~=|j5YxXeYqY2u5`C8bn-KDWD-c{C{arT2R+{BGoyH`w=tL419&>a&HvSUz}f zUd6ZZ8~5twE}EyC8;kxl5ySq0AGLX#^E8O-Ot?~blSfizqU1O z*Va*0>GMLZG_|()(e~53{ELR%+j*oSaq#B&mg~b7<@Rmzdf52Z9&}|waPVM>EbwKu zg3@g2XWBlkom!#gX;-yH>QnWN*Dcm!6)#-Gup^k@zP;0LzOtx%;`-RqZEuxlUD$Tx zVZq0g>EqD6P`{5$2L(^=Fix0#8*|Wj>6nS=(dHcIWjmA zgA;yZ!*Sk^Y($`0w|)VoUD5VxmD)%xMGI4ZQ!lBns|&#TQL57>F;rSj*4so-s>ynr z07^AkZ{tCUFa7MpynX)d&fPj#W!~}Q#`DMx$*-e$#*GOwox?&Y(*lf}Z*tm#h;cQ% zo!A0FsiuXyEr2N1uxz*agVJ-0cIOMAbAz$Y$kFAELQP?r|1hjDh_I>d+XNwcVDy<^xya07w>fb_kH*O*!SH)9)j*OiTtj5GiHe@S=y)NwgJJqfgK-G!G3$ZBP&e@6(}9+c22O>51lp{@q86vNjLw zB0pW*<<^dT|3+?I25sDzR{v_u)(5tspv3N*^_u&KUrwz3Mek1n-#0 zv`*WyiBV^pEab#ar|)G{49UMxd~DdsPK-D~5D?`1~J9TUDmTB*smQ`#@G4FD|)caCF2 zXj^|=FqH{op4j>k5RRXfGd4cg)(2FuHJgJ+Zto=^XL}LBz3xQN6IYfq;am^B6y+5@ zgtK)gxo`I-kMeE=79JoE>#q3bG$tIShigLUxPIUtE9l5qt@QX78{jezw~*=!(D zxIZD0cq0(jLDcRKAsuEX@T|VJiXc!eA<}RH7mOp7+HeMx496_Z2?@pbNjX+R!kCq| z5WJ085=``0>6eoAFMgU$ObEmmNG4gw;@CVmBWX2{d6PeGED7C)k%~M?US$&kJqWzn znAGWxgXhDA79vS#3JbfP!1Bmg8d~z;eo;1+WOiu|nIQ`p9y1k;6)8Bs097pM_bf|jrOD}Db22ap)O?7)gI>A zW<{xGa>|e7Txq%FFD?>UVH$m!_CXm`B~+8ily!*1UkyQxVV;G<@g?RK#I+5TBmFoX zTjs(x?_AIHr{otQE{22ZF!&2U9I^k>C% zB;w7Hyc^yz9a-?>`N%AnMb;5~deuQ?eQ;R?_%z|^aEP#@6iMRfLU7s2DUQYq%Rvs; zrqdZwc;iSE4O7cUBhl^``5nqH_S*9KlMB}GUR%%m`!LF%oJ&fj|B0iiTt|t8>)j)b!)WNDTWw%J&`$>LBS&Xuj<;Ux(B-B7Q~=%ksDQ=fU4V|yy|#l z3S^MKNLLR))dP`F0E6m_e04`u-3e89M%7(Vbvmj}L#jWRBrw%YP<4G&-57~}P=SD| zIi&b9C=hbOP_+%cs`?N;r*Qqt+ttzN+UHk&y&2R{Fzwj6>y5qD?x@-m{Z#FRs=d*V z7B2?b1-9A`Rr{lAUsUab+&sZLfvf(F!qrcy>R`0Q;sHuLTMg}m?vzorfJAqwnrFWK z?rYh?AYY#Q{@(ouKgezbGaIX)Q7g<&4I0g}M~+szp=t|~RXy)f^%H8JXojmf=Gc|D zMMXyy3up2s2Dx#lQji(s&e5A*Hc8MY9RJqALx&H%DT=z@mhGEm0m@;yk;P+1m&m-1 z2WJl*lB00Y1+37cXUFcH(mL}j(+z<&aXbUXGN?6A>olH6I&DQZu6(FFlvb)%-EjOYR0p2uHzRm|JZlz; zRQuzyn zMaJ`sT#vt7fV`kRbOlPr84i@h2 zR!n4u2}frkFW1JufC$cFcjq8B5Imiw(c;h*!EA2?ZrKKni&UoR52jMHFD2*wrpN!|sL1{3ze>w*Z zQClVnKACWf^NbFDFa+*LPC2TDz>TjQ6*6TZZSk$?P^OBd8EAr-Xe>YA;2B){@v0f{ zkZDFd7H6Y2xcy9+_nYD|GtqXY%s&CQo(ujpO-MO5ym2mS1k2^Cb5ROvOsbApGLMYs zsZdehdEmO+8kf!kl}5&+Uuf5~Pqho$``WwO5$!GQ zb?r56leSh{t}WIowAtEJZL&62E7C@2Ia-F+N9(3_gze%4t%cS|tE)w6HZ4f=(JU}| zzok0i*Nmh{J$2}*T~8zQ6y9ec*QTdoBo#yT)T*Z;dK#>!L3$dfrvW4t{PomNPkr^& zM^C-=)Jso2IVY&{9)#iD_0*!LZhETesY+7Ltfyr1L7YiXWj&SjR3s@Y=qazK97!2g zPZ>R>Sq4cAnU5qbgwlT0zSch0Ooz2~+6-+dbWaF$2&XyW`Gwj_0qfSkvSH)qSGR6^ zZTpTr``&zO(V@d{AA9HcyYHPk9S(D3ni%4!TQ7F{jG41$&zU=K{(_2yl~pgjylnZ3 zm8({-S!$1n34-~vmEc39G;h7K>DsJ=hY^$ZBlYdyY{Ia(mHkSlHRR*kDk5idqOj!S*=0C zMva>^jce`Y9iPxj^02`4YiE>zz@Xp|Yp5+e%*{`Rnb?=+EWSSO$b=XvP~ZjGq=+0D zP?C0r(w=CKU_J0J?Mv-*?GmgB&OpZNp4J^&Bhq1#-nF9MwSwNYyxz5(-nHy=T}#sp zBT2@lvahV2Bqjf>J@JL=0IN&f4ADna;WkaQOkK!kwl#hdX`^FzaSX zw%|_=fRY;yJ_tm^DMvtVR#Q}5c!X;QVF7O)fr}jq_IL|2SW}b4EIH2#5P zBHa7{XR|?n_m~<5Y4`FhNIR7KLz-IF71DNPCP-6~VF0+n060o%kF|%|U09^vf(^cl z+F9+Sb`*xrE1DMv2V6Au6$eLHG$rRz@m`iofZB z={?hG(->1nQ@C6$e<;5ym&(257@3oP(cZJ){&E^JiO*j=ynZ)*SB!RObE!A`kg^oJf>WVd;e4+i0 zL|sw$?{vQ5Xk(OLgA*It;YiRGpF8S$;YkWJ4BYdY;hn;orL!xG{O+YfDWv?8qd z9}c(lvn{mR5vMEG*wmLDEpuTt{<5v4*ohG{@0QSFEv9W;>b^ zhhn-$S93JWL7d5HQ9rzs&@=~e5eJs^k%T5Yh!Z%l#Bo|eB@W`}4JKEC1*2l2)R&M0)CgZNt$4>&k8tdIITh(|Ro)O0Y3dOGwb8lH0! z>fs<>(NGMrO+x7o;`a=!(U3L{;@J!=IZCX9T04mEGA-12@0&Vcr)~nWi%lGnbD2sv}5qSQ1cxd@M!tr$gqPN3F@;TiaymgS=<6WnPcNPlny8B*OzkopdPZ(0W9;sP!)4&GERL`fr)F}D51q{nwYy;pT)|}8HH@YJ7-(1VE>gw-JMm@) zMbhs=PdjmCMusbMrzx;wf!i#DZNfQD!_6@2v{R(x}07JEg-=VHbx*a?iYj7aeRdvVm8h<6a zsxK+E#<8!fl3SWq;|M0IRd+w@h=;t2tX1D!4y~GWxugC&tm=Q+irm3*JsDZRXFVAn zuHdd7QC06sXw~&Aynbt|4qt&cHBu3SiR)<9Sjfz~D&UfBD5UE0)lgpdO*d|DRod0i z*Q>Aoi1f4hs%6(htLk1iywj_0Tz}pl?uu{9FU517Kd$8=^ ztChmC{}ovDFM=iiSU3;M(fY$uKNVK8O<|dD*G!rxEc9vhSMyBuo_R7jwtoftIfuYw zWVL$5yxhFOT&ebfJ)R`BvzlktOb1M=x>%j7mZ@XaEOUaniMfv1X7)2XInxLeBUP&{ z)L7M_1}S^w?^O>~Qcfu!DMys8%39Mo)4Q;HdrSUQ9%z1M{>l8UGSxI%{>6OL{Hgf^ z^E*m`5~qw;(v=hiEAy2crJvHkG}}}qx0BbIq@);#c2~oThlY&e?o4$v5?2e{H zxxUFyIxbz7&dG1f4!K0y4x7sB~qy!5hE4zBxO!B50fbjln;hz=MlQJYFt9|TUNN9QD6BW2AHa16v=mF!S_Asziqskx93@h#U9d=` zRvFM4R47s_4IDIFq*fSE86i^34H#G;Qp*e&kS|h84d|aIQZF0Occe&R1A4<{OAKg% z!WO$A81a$;JsFXzGN3jLe(bVSZ@%A9k*YMrexR|?fF45xs=@_x1Zsf`W(d@L19~z7 zHP3)v*#b4!1+xTdjsblK3)E}_dV}sP18SK9HPZ#1g9K`Z3xmdV16lx1GoS+1O?5$# zmK)G#pg@%w&|?5kl}e-;$fG~+bfJFy6a#tpR1v z!tmn^r1j*-x?~-=F)q}N8*Lzu9$blmJiBtmF4UbHWgvy&iVUQ6;|g7n9DPS-s#*(7wW=|aG}oJa2M*t<+@NuZkP+DaYJ3G12@D#mQ*&!g<7)NE|kb- zxllYi*oE4&nUa40k)<6w$R%vSX1GusJJ5v~c7O||u>D;qneAsFEs5>xLT%YTF2u0C zT_}<5O!sAbOR}^*e))_u$^5fj_u?^E!mDP z)B^nL^|7EdXFIr1Gd9&go=w^I2J&daw$qUlc{FBIT*5|dvI{k2lMDn~%WYkVVG<1_ zH(=VhP#}}wLiL$=1Gz^stz9UVX=NZwJtoeD>M|`|D28bvad7_;veaRkyMzqW%s_6@ zOj8%C%``EPR*PxuLQza37h;%(22vxL1}@}a>Kn*xXJQRx3TEoLPy|!gg&4*eV?ZUG zspCQn1H13UEXBsucA+q)mJ5Y4Q7&XF9pnN`pbK~exPX^`mF-#WRK|<&BI4XUT|o12 z0oC0FSc?lVZU$g^%>~S=3n=EQif8^EWP@cy$pskL2_favg5Ux?FK`m6kuxX@8&HI2 z7=zL@$4QbX?e4<5wF)w=gxE5O3_Gs4n_ng|Wky7qIn{jIv=ZXPwktEuMdl-FL-jmF ziNPM7a#0ziH$D_$|~9c zo4E-AlxTfk3IU|5Yp4w%r>MfR5rylp?9O>R5o&IgVna? z_sptkys4?YOqQic5%W*PXyp(r^x==7DvLEnF8A#(_{7Gdsr~TxwfHF168qHVop@*j z-vBqO%d4U*PF@d`_j3E=R5>bf0PoW)R*whAhimh8Q(W1Ieu7U#1e)rHheq>3@cuMC znvcWkaAf*-dH7N^-x~GBzIFH}s4nhOhfhO&@X9)T2KZF~7li#|_{pqTLBF?ec?_RZ z!%<9-TwZ6G;M2J?n%e%4{d5Vp4CQ^v&bnmE$Q(L!RL)R1V$C6Y?BRG$D1?srVhmo!}&O~_w(l}cx^ar`B#SX4bTw$Za7~LzF+YuoZpFZ@TLg9844yR zM?FwBZer)-;k;p-o$tqG@jl(^7#)4z&WEwF zKWA)4R%UTt#&gm38L=;t#*%TM+u9@(w+Q1?z@_E)Q?c&sgh2uvWzR>kux|*`WTreI z1Ah|9+rjDlw@Ch>Toy1;@aYzd%39<8wfOLG_}Ip{=RRYQK|VT=Q{3wL=38-QEk1-L zn)pa9-e!)r0*oSi%0XIRc=@Es%crkai))Zs^VYUBI`ChrLTvskB7nIY=JIX<24@{Jslr_pyWsx#hnWjuu#=z^vFeOvz zt8`P+lq98<(nP5TZw6sXfa0l`6&~!_E<0hr9d_(C8)TC~HX3AuL0&P)dV{Pp$XY^R z6>pH$23cj0l?GX1kmUwhW{{-@dD$SChl36%W{JTrHpoi`sWQkSgH#%1AtA5;H^>5m z%s0q9gUmI^9D~d@$Si}*G{_8tOy?PYQra|woobMBgOu$H9zfrw-8t=RGLo{%2d-fA z{yU8v?{}g7XTymLRoSz7onb;~QNTs*;ruo_mkz<5bLn`VjN1zg{yc^Yb`#;g!@)a6 zBL#)QNzRfEd@$@h^TX+;u<3K5Gf(!R=MJaC@b=+!8`y#lP2+>HWdz;EDNv1`B)=a2 z;aW@Kx%h3p=lIlHZ+oX~Po zBXGr;{tXNJM#e5?r)=GIX7=h0)X14~+fP22Hh<{AcN%n2UaJ-UHRl(3h$cN{JF2|Z zSWbsF>c3B*)FKaxqt;Jq`bS5$aqE#nHFVPMxJ=;N;BS(->3UaI1aiT6Sqj9&9!lXd z8AOHPn-U)aUC^o>=Z&r7d7BV|^uEGJ&HwJ~Q2H)|@3-SZLGzn-d?>awgXq~h?E#H# z4?bHgWyQM!!TAnu#=Gsg!HjqTcGlYPVK_gPgTVm(S{cL!<0YwF8yGEkjRYa{k5j>p zpblIXKHD1le0>LCPjui0<6{VR&qUamDgq}noR&PY0 zQ0skP&MJ_btWY6K&N3k^3=cD2t?x;vY_09X%n$umQ$oP^`@iiU=531@bfx^fFm~m4AsGqu8!>BF4=bs2*;#tx zz2K#8ogXG%oOq_9R?AD*{iLKLeBhp<#%FhD4?peUun+Uy+o$_inWz05#@*cOzG9;7 z3y+&EOX;p{&y~EqXhA{!^Bv8DhSuA;y?@$Umjh3U?QKE(mY>TJVvDXtSWClty9MrQ zJ38%N{@wTq)#&8GaK%+~Uh?0xkLnMEc& z7|L1GS~G}vJJ+;l7^o}Nu+9u1N`KuabpG{A1O7y-rZuLYu2jPc)0ZgyWqtX#QGmYfhkJ_TH0Ekuh1?7%+ZuE`+K^vlSuAgAh(1njV8 za$V*M8opYgU!ce9Okut-HhL?aP(F#*aruywkZ*xJV3XA0S6Uwb+@`r-Kefnm#HtQ3)ER^xjIQ510Gz%VM4zI`+4w7my-0f zt)3?8X&XIF(9?K5ZB0_Km7d1wX-hqAp{LFDw3(hZ)zc<=+E`EZBV@6mjvJ5^KB_^| zST2f?dOA~APh<47j-E#AX>C2Nwa^o%mp?yW9kf04$(@#aJ&v|IktC5~MDAJIokKgHPH%oB zY+tuq@`z_)C%6G2UEP)zgeP_UZnzum{Oajv$tjIH+-d98_e$B8o=!LaD%19s{x{yw ze&d^=rXP|-eXpGk@ZbIUk=z}J{Z2NjN?6z=?vlM6^$pOjC(Uj5?8fjuREJ{jqw{0? zaW8*#U~ppDoY}7rTC{LN%Vj@m?aHUQZ)%VvkYac=@xu=7R(-YY;KeH$2lM7!D6QZ8 z)v@J4!z%oboQr;_j)PAR9(A{;y&oUadt>F|TJvhzD@Qyny#DLD)b(Rh-WxUeToO;p z;kY-=Z5CM0eeqqYrSZhbBahWykMhpEQat&SgmuXwtA}NxUB180GkIRy_sejqWC$}s zy7;j0aKraEuTS4#I`!e33zIlf42#}6v!T3k`H7oRzs?)5w^z}CQJVAMXUuPL2kEW* zw@svaGVM-wOz_ zv#_>DsOaQ}xv`T|?)cF1paWZX&%=$Dth*YPf0B>0Jb63urHM;Zzjlb=Pu4f%Buu8=L`NCMY?-o{@KQZqzZIw32AMh*0b$nMkS^h!Uj)tK`wE|x8 zg9VdmhVTtNLhU0j#swucB`+uNRH;X9!hu|hXw#hkIFRF&%rp*6= z(x5>^sixqj45DNNH|eoW0WS=08c4K^;3hNx=T|W{`bmF6!|~Ha5N(p%mni6=O|a+l z3`AUST|#1;P%m<4!}*>@A$n|64?S0pZR)N=65E8j;Ttc(rCoK&&8du)OaC2)&~v-s zB9hx#7j>Bixt(;Vhd6cAp&sItrb9i%se=wlh!aZ1hZjRUN_*USF~nkxX!m1#1-k(TF)hsPN)@r zdkK^tr%OnX6W3CQ8eGb?(BYaH0GsQO1UrG#Xgw$pn_q?%pAqZSSkEM}PFy1$g4l}J zzRYyi!=1QTT^K(X;-Vhc`yE@kx;i9LPh5-+FU$dZ>tM@LuqaxW+=qsN({w43)Y2sx zv%#7u9llCnq@f29Q1Dr~%;lhG*Cl%NQ-lsl^b_pB4O|A9HeC`&Dhk8*pjhyl24dq^ zTJ@YAbD_)-9qK_)!8#;CP_Qj_o)oXY8n}X>R*)blW0kE3L3!!gdJvSS4oMIcM^@Yr zX*3^7cGsa12(^+4r^%8VsB6h>#6YbgF;J_S5RR<4!BGNUA&n)s9tg#mbSZhU1fSxk ztHAUeJw{ZIh2lg#_XfE}&>;zjf_G#qQ5?wWB0U<4C9n#jp=gf56{{F4c|Yr~=aaJG zkXfJ2ACuX<51iz$hV%N7TB6oWb5p;D75id!q?)KURqLo>=3mX{&6~_C%q8Xy=6JJ@ za##66`ApfV%u)s_4HemR!}PIfi)ocoaB2EzV#kOKoQ56~CXW;{3y)a(L6A}e~{yzUDe~CZCALIA( z^Y~J5dW+_*yeBVmPq+`cS2-uwgG=RFarHTgz0Q8bo@5WOmF#r39~;XG%r)k9W)9(4C))vMZCdwQp| zXS>d(?-|`-PT{27UlXF4$TEs(rtuyFa6uevl_T}DPT>3F%ymqtG{u$O56>m+WP|nY zi`R7JKzovl?}J-4hpQbE37^V)_r`_YxDYRC0^wEOi^+)`Z>TE0u-1dKdduSo@5Wmg zx_0DPBTwsz7p`TjVe%L~kF#_TzLH0~a5v#(WQnfFdGx>!)-zB>v61K574k+Id7jO7SFVRX8s=g<;tB{hl_Q6`c!s|}ZVTb0JkW*XV1NuDScSmI?8yEErm>b3{2+z2pr4CR#+~|s$&S8+Pi3_vd}6^%H>}u$G#Y2yzyYgD&2OG(x(8b78S_vn`&IBv0!pNOm(m3dwjxO91YE5(D7Eo&k zg40-~xzMGlv*VDR-y9C9+d!T3kyP9=`9R5y!yWH$ez)R{JIf**sYElCRhnJ?dL<(s zD3TvG`?m3&+VKO9;SL)aO=@qH$iEL`9x<%vFo1x?;F19uvP1wc;tJT5{UnK=Ma`91E20m13NOw))AOM(Vhz@8$9G|ON0sXts-|FQSzYYh!bEyWF+`>^CML&Ghrm=u z3CD3Ie2BLkZK#vqb7XCT(-_5uEu>|UwOo7{{+e`5l#30;CuYG9))7f~C<=moUS&dJ zWC!8h7%2oskKKjISdMVvU@$(M;50@G!XY`Zzk6XT^9F4X#+BO`2b|YPkSBOU>?>^z z76S>Z3QUd^K#-f@A)Ivh8#-S2=r$%4PIAZ<@bU0n5U%Vg`RaPShwzod$H;;IaiKF* zQ}h~BPXSXLo}365aBeuJJ-DDcJPcODZp|S47@XPF?Tn+AWFcZVj)ker&46&Nr0EcR z(Z6!2`gN>%$qq2WY%mb$oW?@IM|GDK^w`Kk|K({4R%&-GAaRKa`$JmD6ZN0{51 zgW=8Vg0e-Ks`OVHDXQsf(^1pQrb1IYlU4o|+}XFt<#J!SKAZ~QlJ-lBq~THm0v_w|5cLN6DgfiYLvSxc%om-2+ zA(vI`>A)=kBLvoD??NuJ1`$TKcP7l+Uc^?(-ia`S{8?Z`dq=|TJP!2;_B4Xe`;$v} zdk4bQxkxJI?5V)OdUiRfhPAgRS^4fnkFmGYnRl5mfsRN4EaCWhd?+803|Pc#ykU*T z+LMUVjdvkp-`Ou==+gxT8=W`G|XDl(z=1Yi)}H6ChYtoC@4wa@}J zG9mWXgmLGum!U+@Y_CJI z3QvJSiai?e5?#w=udQqCfY%g(k?pmJRy{Iy7|9+*m`s0SooJ6FOw4Kh20QeO%WzK_ z-fsA$?CeDf;Oy`TSA^FPHP#NFaD{OIY65sl*>na96B#-p3>f%Y%r$aNL?|#Kj=usg zqKw^2)CRr}UC-G=07EClgBH!&gGo*)={TCP2NC8xQKKUQ0ZaJs05Vzv0E;*#kq_k} z{K?m6D}q2QMEDW>jNEuW!WS^YPg9AG50US>%7pT4gg3!fqR&Kl5&Qtg5PXQ%QRL`iPUP*abYil-E@3}CZ%hD;pYOiLKwL&Pv?o;Oyu{VDWVdg z5IIc_5kmx1gh|(O!qt3)tm7N!;I2y%5@9I37S1U72vN_m=w%6jF}e=c!Ol3rMKBgb zE`l>I*a?pv_~@>FLD4ye^f#k3HzcSdoCYl6SOs3B!x3P7{Tvgbb{_oPhFsTu%6aCj z1|Mdbca4cya)V)=>*Oa!eCAzds0m+zsPRK{FwerWz4LfWpGFM|8^yXHgY zJ?7WI-+QHbv3b6Ex_PpBv^me5V;*4cVeV*7GRK)4fvez|Z=+ z@~Lu8ISEeQ````qRb?%BT~sQwl`?SVE>d#A=b{fdb+=dAq$(|x21+d@RPj|bg*W|X zdI+9ww@jB!ADT|VlWU)8yJ>@Ig{jIk$5dt-XDToaF%2~J1b6MWrWU695Qt_q`IuA_ zClgP%ostud62M!-M6EM`OszG5M6EFZI8z%ypjH`xr&bz(qgEJzrIs6jp_Undrk29# zf&_jbF9Q{UVm(7NQ%ek>P>TUL!B@jXy<~`Fs>%Qo1>YqByF~DoHh@4aGyqRk7=WV| z7=Web8-Sta8Gxqd>XnPs9KCXpnyqIDCTf-eWNM}XoDgqfAd#AG0C3(g08dRd07sP@ z0HRS0z)+=7GMui_PB{Dk_sJ$vID?8~odyub7?dAv zP_D$FY_UO^Q3j=p^y>IRy*j=?mvTzJK}~rEl}8#>8evdzxIu+ngYv@+$_*8qq!k=H z#1Jq!2Bow0YQUL_w2B+7OWA--gZd9Ls9%ObeFqxUXMjPy`y14&pFutQ8q}kYLEU>B z)Y3~}iCZq~*3%GZJq)ULH>kOrL6xosHKiL=?qX1>vq8m91{FFQl!rJJQZCoQplqr^ znf3;y+v&|?Q+TLfL~ODyX2QvV7%Zb~Z4DZhXwc9$2DK&_G$h`j!L1D%)XJcNaRv=& zX;A+b2K8%hP~T<-^=WEQ?V);t3FFN>Nha;Zv62Me&tN4HdGIW} z30T~9;%3o_=B5)>(}`KtiDK4?NzsXH(upMNM3i(Qh&tf~op8KPSWYJlONcv|&#~}c zL9?tVO0dw5-^bpk;lI{N*uyeF+U$c*g91(5$65((yNRs_+m19ngyR224}nRv?|&V) z{_D8)U&pQg4>@kx51~IC1N}VV6x)|_rx4Y^mnP@mv>Z<<_2hW?={A%D)(l`c>TS1w zy9z2{8_KAX+jIo{=VtI;K)mMG4hIK2X*l0opi&H9z|d=k=UjYm@Q)J)z^+RnoLI2C zi38!?)5Hmr#zzN(6XxV@Aihf+^DbP5vkxozNL^?sWd%Fh&+JIUB4$+MKfDxwX6pn| z-5b$RT<}#mi}D`{ZkTZ+X~S3X+(A-HfnQEO~8en<9?U37H<4cu$Pr7|*Y89H#{&7#l1ZY0-!-hY&Bdw`q zY8wUa2!-(RltA@kb&Fc6_WJLqBL1KJR7A3q{1lR?O~3bhu=*4DvSq2w;cH{3&1L3h zW;f+4@B*(=QX%H^lBvR!YzmSe%3I~ZvL8fszA6otnn`ByxVRiP!6e~^um=vgz(Gp? zo4%dz@V{pWALTQ|ToJ(QgPzxV?8+0a>MwLL%a>C>V{$ubl+>*l{W2@FyohFcycr z2V@M+Jp=NR(J7A!FAHb(S^6^7L@^J83ux2C{Z^M7%Gy5!j3NmU0-gg{I9!^Rgj&A{GhvlZ^ zV$-`&?J(S(kfFHu36SRuNg0C6PJkc>p9X=D4Q{L^M>c*$atCK6W#N#MTrxM9rP4ZK zWgy->4Uew5At5UQ+nf1iJ%v@l5XjVhrI{#PCXnwa3w*VyGM1$`vLLY=(UWa z8~%z&)4L{j#p)?&dOGfT3i_!Fo^%TOsS93xinF86_{1qFtP|Eq!~< z1+wqaSx!d8YZvw|PQ&CY;zRxTHoEIH>&L=Oy{nh*>it_UJ`wK>fL1?+qa<9Q3@cvUwXmF%mvFAVFC>vthQ^xA|N713H}t7kr-w zGi|+mJ^^BSRt2#C@E9?4W6c3KRHE5Avzen|xTPmA~EKoBiXT%&Ls>3^~Jo^eq$+rDu3& z9Wo*+0xA+DC`d99gkc616cA9f0f}M;2?~OMBoQQt1eKgb6ckjHAdcu;JwJLPis#^cG3c~{-CHGm7aw+8QIZ!$;QtI^=KzssBTDUEIpgj5j zmVzbW_z|}OtRzMNLbyQ;2cVe)=K)v{LZrZEBjOziY8f91YW6}C)NBsDiaALzm8M`t zrR<76@%EjG2(CYQHL7s#95;EXH-a~b%c5l=_liA22m{R`jCh;^ty^>ouz7I`h%1Ra zI|UH=no;0d1o03B*T(0uQb;-2=mc>0?kLdm6x2XN!HNMHSzDt3?2fsMS%*j+2c^4% z>L6a=3LM$m9SUxs)+K~j7?e^N0!rx$0i6TD8>U!E@O&ySXw&2{AiCi(5Pww)LQ2C4 z(juT23aehsMvnfvvD2-(w}ClKUO#TT9ysI#0~W!afc|DM=yx!WBf;Zfg2%{!E6vxe zG4}Z!pwTGAN+R0lLHVdF1_497AqyxbpRy?O;{io$JYfs%jvN$ouV~@MIk{gFg3pMJ zxUH6nA4CS_BxiUWZsFH|5Vf-0%3V~K!)fjX%7RL| zheu35n;UE6N*xYNo=z72Q2EpRaeIeSOVPq}jyapdn#E!8*|p`kmz8_>jLHveFbYz2 zlg{|O!Q__xI$hdV@;sxl|F*01a)>lh$` z#rG%}x+|IaDCW`|RT4P9W3$^Ap5lsJH(MDqRAj8DRypsN%_Iki)$P3P(fiIxd6%lt z-Uk^svPaTimG{ToyD*lZ@l|lb!a3!uR`&gU<`ul!-%JH)q5o9(0O}l!I$o@BcbC@ehGFM*R=c3+*S&f^BBuNTY`Ddb%IaQ2@cZ> zJGnnV;lrd*1LxB2ZZSv9yGAME0>hYP&vI~Exu#@W#`NDtMeRjV{;KEjtz}G$hR67+}TBkhF zI;}KtK+}weK`>sL1@syMC!!>AdvI^!X6I_;3gc4e!gCICrf~Xj>Vt?2w>hFY9688f zBkv0PVRmV@6}Ao*-o%DYn%qxLB72cVNiRq@NRa@DNG6VgIoF?PO88FbB3vUl6V&k2 z_$+)V_>zkQ@W>t90TAYK4%>{40ALY0%rJ)kBE}knfd}9s5YN{Mg!hA>*O$OyQI#*d z@jtO}DAMy1Zv`evW2Af(Op=ctE7OgTF>pz&#ei9cZiujrf&0}pRJsA;`~gfwdq5fl zmW~<&U){|}3z}P5WkJ_NzKr3mKq$WYpLkjF4)`Gk0&H~=pK(A?UO}aUsC(n!5p84; zy;JtUg06)WH2_}sk=_O{^K3+x8}N#-CL#%@XksY_!Yk4>5NnVimw0WXtFtDm(jVYO zL>j^Fu^PfP0{Gm$r%2y`=%JT|MHck+i0259LKP`O|5T7=G~@Oi3%WAc{s(uIki+Po zA|lrhGSY7<(m^C*^fK#)B3&K{LoW-iE7L&?Y;Z~3iE+m$(`At^G;s|LIYyU3exO8o z`Bb_zA~yhP1l)55Kz1%B4`fRr86bhr&Y{vJktb-j1i~>0{=|{pgMjKPt5w$_bdbPj zWK!v3NFm6^gUQL(0cmdo)e{~9jUs{=fj`5!;3kNSpxJ4x zYypHc46-j?qSE=1jl&?D4~azoc#-B|paLn=c@T+LfIJzEBSz;&tUv-EcR`uXg&ali zCt)C*A|1F4q4yIKsdNsc?G^qnebExCg}_Gp5rC)?=9&i^<_OmjE_2QwoMoJGoO?Mn zIPo0AVBgvo1ajWb6_KqN6BU6IP!k70I7qNPdZ6bBTf@*h^a(x z5SH{a;RyjD*c0&hetZ)C0A3R}gUbaV+if^uY!~(nb`MsR3o`+Xr16-27=CyfXpIRV zg1-<5cZweEC5KgJh#^Wp@Ct0A@Q^A4Z9p-^`3GKx1OmWv!^#X1BnIRXgyB#sLl`Og zfu|Bx8A2?wMJ6Bvv16H#GD8p%{|U%IG+9-K0AdNQNg%i^KTKutBPV{c8WRj@d3G?M zG0F^H@VpSxI)|5L2N7Y1lo>p%8w+!I88%R=DubIfu=>1ii;gb234wxELT1j&>!fvO=WsC>&6neK}5srTF|3V z-OB=yo?_)3nguz_Xxq{wk>pu0zJ9OMS3_gjb5&Nr_#d^?hl}mPof_e zRGH5I05U3<6hY|J4)pTdB9(p&Swb_9BE}!VUnmmy5o9cYH$Ma^LodJFQKknY!{Czm znN?H}LYxEl=RZ;Dfr#cDcnGM|wV;`@(c1w)Og<#x4XE|IkBW4ER`L;=eE&g_ewdYf z50dzqSt|Vy!aD`pgYJj8vi^OM0`N~%r2DYw7N!8*+i45BH?r<6>;6I3{X=g7yYVRt zx)(?~A_z!g-hd^%H(rt)4{C?%-F(OtxQ(BfRHAzzoYQz^4BZ{ko5m}FraS=hg%ICq z(CTBXD!H*9xD0OKM@Fgi{Yd>ZD%L)9eL$@=|AcRZZ{bq#JXjm(8SrDmAD6)?VaKr* z*aYkWtPtiorT}viV~4@PLvS(d4RC_I5PD9ciUO4`2v|Gd(;tots4Wino)E;={^m(* zKGF9(i6JTM@DU@;>=Z{X-N4Jy=mMw|xJx$}Ce)qQv;3n5Vc$km`~=ItC!9Ccy-Sm( z1f5bH>{Em5pK!k`R8=zhG`IaUoPucpA3OB*+*oxzF?rrm zz-n)njZt*HR8OsypmrW+S^-Knq|(tB*8yKO_BN)a;bg&=4lQgDrzOF(Y`xlM-&^l` z1>co$P63qRTL7QNOVIKb73o|kZ(NeV&#N8FQ)jjAGp~j>tgidwuQ+etHRx|QIG(y+ zQ35)bq)bO&d?BL3ktO1nXMS81UgjGgm$wC{_6)w@OwK*@V5^j#HQNfDh`9~sJPwpA z*2LCZb{E0HdTn^EHMwM^@$pnOH-yx@aW73h2dST>^~!|U$skfxsb!s%1+ z2si3JDY0c_=23i#(FJj_9ryX7FC)pfz>y1dHk20c=7lNz@q!t@DYMs6{FSiD$?itz z`rMV`v@^RO`d^epA}`}9w3r2DIvJH1|5*=Hp893;{%L_0*R*?8`EM=0E`>qSI~pyQ z^COb9;ds{2B%u-$9wP*LXdJAe@As;^;zenR?*aC_3PF>{C+i>o1`~B#bkJSaZVodhXpfhKwbOK9WZ<8x|)s4<(?P3VX<%UQO2fH-3Yddkp zgO3OLNp!$?!xt`Ti8uYW{8*a7$!XPv`D*r7X*R>^S!}M9^j@%3IeB)W)2 zc|vmcFyIu5B(NRr5X^u>F<3J`0gEj< zdSD4i{7uemh5LjWwcL!|Ih!eEv5C~55$*X?gELqi-B`vp56 z6)xusB3n|yVh`R0+Us5_Xd-P}oH@dG11x)iFZNYioF`ayhV+6ZE!d)!2Iwhdf)CJx zvs6rx$wshhYp}yfBSDfl$t(7twW$ECiJ05tw$jee<%8uJsf}{5H_U52&oREODb&jq z6(I)ffdzdzdz*7p`$Z?tl<`KLUoBAA9jzUD>g14`Pq#SG!KZgR+2)5@1KZ<^U9g2^ z3U385x9pA3?NM>v*PD*cO1V!RSD|$!6NyIld7mzt-&CSIZd!5M+uIi!Tq1YY`{;*v zvxHgN!PRvQ8oakRMG^0x<#+O0iMeT1Zsb~Tcj(7i7wXu|q*#NbO{dR@+o7wvPgZ6h zTDl&0?D6T13|)|ES2H##xv}ZUOUSvyH{A4by7z0|dZP7Rv!7nek0aB~4yK;J-&5lt zac8G1NvpcUw_c@qmq$0USN9v2R!ri8+U$3B=^$Ez$I;ospmUjO7WH{&JicY~HOVUr zacCvA#$1UAF@Cbpw$4%7Ph;${3BF}rR^2x-!ImS9E+wlet=sJSBB=*Y>aAVq{iU2q5~(K$ukz zfYsyWngv^A8C)S;R$S6xlcA5Z5V+*;<<#P22d-K*z%=C#0+6o*TMeB6^A^Rvi(Qo+ z!#2oP$`;4w#-_u@L4HfFC0_!FZBw!c=`*Q?lubHDvLVS3SBc%k+r${+9--uiX_LxLAOW3g{Nu124Ku7VVFt(S0 zKlxmvh*_&a>EkkQ_GtaFIVWn|U@YC-Z|vKfVKP~PE`_6^l)El0z31od*b7h0dzs*7 z%(}DL-ejTz9R;nzP|Xhb{z`v~*8LxwYB7i2P1TH#kHenc*|61M+mbS!Kqvlv;jI6Sz^a2uJ$Q^GG2j>;$2~!)JDzodu84BYy6zM zo)gS0;@m3Fy{`1=&*i1O+@0@T$T$T$0KU>9G}|mF|_TxRjW6` zooN*9do9O}j8&imq%6P_W$~4p(>@SLmo$*gTMMgJlfH+SR#D8Wlr>}(%wE#AkTLRf zfTIN`Oeol5KA(_q>y5tbw5^;^DS#Y&pfL3OmAzJlj5bR56j`1QFt#v>U~ELkti!t$ ze9bR7eax=DeCE+ye??B9>T1p#p(^X&pv3|H7C@CCZho;E_RP^0AzJ@PdfZs<$4zP1 z!>dm}ej4qy;!)*C;#2_ZW3)2d5*bXyA&2v)+5Qog#GUDtW}4oKHxh!9?x=6>IGRJ^ zPynlAK!_bwIok5L!A$H&@UuG3Q`a}y9nm-{eqi?Q_~)e%xAZ$CFn$2A7u1^fZshsN zq)eSal{**3Mrj_3d=J};?vnzyJccUr=`1!laP> z+mcX@OUXNV-qf6RJ}*Q9)d8Sj&^50lce}Lx9)46ebf8W!#X)^5A?G`9mdKU4g&*c7 z_7oBrKL9Zddi&&z?_~U>fCH(yJKbU9{Ey3Nv~tX{_*cPW{hj;WtYS%E00BHPXrY^1 zc8h$tap%QCl!g5D8OH^-W;Y#vPMDioch5CGyp#k65C9v4*Fk6Rwxc2qSL(KH76=Q< z+(sueHz$9-x*(C`mL%A@{TfhYz&aQuym8l4X&a{LbNcP&316EIwWSFIJ zu5nsS0tpNtfKvuiwq%G{j|4nCQM|e=Qlc(-LB%ZmmcERv`T8AM57aEYNMHZ~2s3yc z*7x_=_J_-3X&He}PakFU={KwElnyt16)IJ?)#i04f$;z<0y7gx4@k9kHUn1yjfSCqe z(?2!SZga`CSP(h-On^#UcOMqQUF=bv{7IMZHwe=r(m-zmL^T*d_7i9AIex+f_QCM> zuL`GZT@Ff0MCTQZn6<&4WAZXY(AxlR4Tgnk<99jf@K>iXg5TL^J~2X+HJ|tU**t8E zO4^^BaPb@w2nnFrfaB6GsGBlfjIk(Ai)flcUMNL3?-k%8(;Va%i3DS3 zI?v_Je%pl1#ojS0QF|Sv+;kn_>FEIB4a)BF+!($%X(ey^NSvT~ujOf!)cXgY<@_Ia zMpR^7JNbwRIvYX>X_zB~GQO3Qi0_+5hI{tm$^mt)14^)i$Dqx=r`N-k3yD|-mW&vA zOZ)jZ>x**AB95JwIy2+p5C!u+uPj+ui`sQ&cXqW3Ko&%|Gy&dG|W zZ#89q`BfnpS7RSwcwu`$BnMGG3XqWq+-I9+uDw-}48vVJY_mt|*7rD05lu)TU}}4b zlk+XUOrpXmK!=1p0k%*Ar$QtuERy;b;N*dCLj)_8--n|R3G52tC}FA^iu-3%2tyP3 z!ocB!Kvp=3CIFZnBuEM;&_ta8N=8&T&LZmvZVv>4!Z9@Qg%gkZBOC?SFtX5zvc@Td zqB*IO=rdS_5H!I9d`U5nc;ghj(S)fu$^olz5KW{2G7=sm?*$UT(`FsmWWuxIu0Rg3 zIRu+?UnI?v+c86IvG`CTa4^AM0>Lc4VyHxRvJM(v3&iFq=VW57IK0R)+#~D;xDdFX zqYoS~a*ewZP9kJsDhMQwb721OBsOy&0S5y9##Z6+I1DzBQv$DoQv|1l`Edx7PH}A` z&f=J02rr;kw@Lm|1_?mk-#Tl*JlKavtiNyX}r>q5CK3RjKe8ocpKYbRUGc4|Zc!41XQisQd0>;3|{TJgueG{Q` zDC0kUzM!+n7IA>xAVxrPENlEeG- zkt9)qFhV2aj)HnJzLV=IZ1+GI}TL}tWQk*}CPjIIR5a%m`EoUib0x-jya*6||oL-K5 zAP|HXa2cQB5GAtV?h<#gFXEQjUy%0TFMzNRXMl0jfL(}f0YAjn$##oS$Hu^mvq|h{ z(_`Z&eS>iT>)KVHQXQ83Q!(O^(XZo z%0a0J|3{;z{RfpE%0kKhq|ZZFSRQ9}qy^BvwTORzs^ zvrsBauzyu$|C=TYrO4Az$^M|nLdh&L!%HqWW9cr2UJs$fcD{DURJbkenNa3!zt_wiDJ~e6=3VA*Ch@g6lz{3C2`%X?rBKqHSJ#2KJPVMsj$KvFLNkYe$Mz+BK12?K;25%Bpk z0+OE%aYD$a8G;Pp$U93g11e3b5zF3EY6R>DuZ@89f$|XuU~G(&M(rS|vIuBt#yBB> z0&JKErjJo$oEObET0@Lk?@tM0E@EjMi`X67$GNQ+#rF&P}P8 z3XKV#e7hly^;#qY#_}=G1TNRdAF5wUZm*vX7GE#-;G#_X`{2zHFV1dgd$EaMSYv1@ zGjXz$?{Z|faoi?sbLWMP!*|>Y!m2`T>^HP+o^4sbg=tW&)j`vZHyi8{OB$R&V5@=W zb;2^q`UkbvyPYHEHzraX^o|U7dj@*dC#;OdOUm-czd9f6{!%bYN@Dxd?Ym?6!eC_4 z*UVjViPeRyh`c;>0KDWv(U<@HC4ne`P^RbBzx(+N`@A;v(hb8&L7MJ?U2ZhUEgHcJ z>CnXa07p>76oqBFY{YE=O$O{|GDyuv+&1LEP0&YoZ-V6Wo2V@c3wi_QV}UupXyFbb zF^^Fj**`k8x)$IxCu<2?SaFb_;}NcUR&Qz66U>oV9h}USF&08n3JG=sg{jm5WF%@P zONBxA0-tFUwAWCD7>JOpi$i;uR~T5AmO&O0k_Wohbra}mo=RXT%HD)y2QFqwmwO<y>dB=G zd|#h(hJ*M?7><`5H#s6W>^bo4v|9EHV0P4IC$J5Jy*LJ&E*ptlM?OYYCA}e)lj2FP zr1d0-*h{=gbRz=KHV_f1h!9P1ASmHS@R#u3;P5|o+$gRD=Y!M7abYL0SFnLt8O$K2 z5EF&jfg!?!a5C%xZ-(Z9On?2X7$~yB2|M&E{j!c!huFag&rf`P4a7RWQXvJq*lWl4 zbX40et1w}v4nSy8fNhZWYsrd%;x3$so+pZmWqx_AAGtHilz4o7v7~j-aeG{&3D3Q3 z9h0tjnCSsi{^ziF?JM#O55S8H^D;rh(xlC4R^ik@eZe|e&pns5xh`_b7ZhFk^j5<< z7^BK?2Xtc4EG=XBbe}*XB-g+>EHoHJi*f7W?Y-7|+D&eD{7DT1VrrtNcv~<~=!YXt zKtn9PeFMSsk_oq`V85%yzNF(K{y~PGJNMURbr%CK_vzo~n8zrY-cH99RxTF>7sHjf z4QEx~Z5oA+cpA7Aw63Z$P|S!J?y_z1pdC-eh;;7tb1moN-hZn6ef-?g>Ky(@#fWWS zxFm!g*o^}PgOGFpIZtEk1CI&eN*SN3_B0WcZoIx>F#YXER&d`3

J&KP?@7jHh+;Q)ty(DZ^p)$v#$p1h?-6IdkbYldAYPhrR#CF!20M+;8%DN`{P3@1S6h&wz^ zd&rTewf_Y+S%`lk`XSTVs`^;2$Hp@a%oC&AGYzrQBd~mP{)i)2kNd5B8MWr+qz zk`o&Y%lD1Qf9aN;&$9Rd?G!CoBNcMTD&tj=h*Y*+4^t` zh`9>}C$K9sb^&T3%uLVs_JeSqZwxK##^7@`_~CJ>><|KN@3d59YvX=$48+_CPb)Ka zqQu0Iqe8pf-W0h!F4Y`Vf*dWU0b9erXxH>WjF#_2Yg+$NY7&O z2 zwU`RO7AH17n4mw?|MZH|f$2j9I~MP~VFr|rpRo!Mk%!RZ;F*vr!+}Mu_~k?aU%UR^ z8XI~RP7hx$K+P6pxzxUNN zKesOBT&OEyUV<9;-obroE6Ff9V)%5~ieUpdIbrjtgdz8r#+#oDO}#k!^km@mi}w|V zdK&J|&OD84{ZI%qtzpWjQ}@Wpk=)q`L5mMFV+vcwPKzFh8$Raqsiea@NHk_io?(q* z^|+u{m9xdqCqGGz?|T2J68Egd{ER2#?8(N_PiYDMfr-1p7s(2(EVd|U+Iw=o=cVmh z&E66p-Tqt0yo#Q_y?ZH-NNnoUNMD;tH?^E> zEOkuVbcZqKaVUQgW?H})8j4x80OUfLW*si$tA$m5P9GoRA9j`eq_$LRZMT>=b#7V= zRf`OTm{hnznL$NqiS5P_mvv4~G^@V4T_x<+JGyShI(wn~)`4|Dc4WK!;Dwmm;0LOV zZ7iWXqSAAta^DlrX>Ew?*f>7<-nFebYl)X>{*zrf#_(kc%-jk(6tB$Kin7KEm+Ip) zdy*~Vx(mMF8m@YCGlg#W{s(S*oWbT9uDekX(;W8N#xMu8_DJ*`UJ82^E0t<0K@<3- z%rIkdSnu)vjCKKMH-6YARrl_}bB0aJlN{T0MuU{=@84@xfS9Hz{L~Z`jwlr`_x`%3 z_r1ltf|2C`yBfs%C6a1y8ph35M^mqaZHJiP>w`j5O;FCn`GUKx8Ml)^E4bY&TB3ce z8oBL2>2R)Y8=U_{lQB07heKHa&Mhcwh{Gm#ZqG|>S0_iaqUMlQ{0rZeGffG)b{@WR z_D)7mAf_?QlmMM&j8elT=0+3sD?cBLJm|eG*daoGyMAk_Y2Czk9c#zePaTsX=4O~_ z1Ozcc1;I~AUoy$Oa7*FA@|#bSk9}`1>(aI_8VGNa`Ch%l=@oGYbeU-gSR10O@s}(f zY)vW;%B$p|h)0X6m#sbegw-s4rsd|nj(f*8E{LfQGY!BiVgRTeFpWzwt7S#i?Ani` zIM;PbMBDfbT%Io7DG^fcB)2rx1wP)t6z97chc3HxUmbHQYpNoQ@H`YL;Y=St23N$- z`y{VW8Tu&ZSymwkOIc{#aH4kBV>azer@-q=ha3pC#k#n3ESF-65VAqdf5n2a8J)vq z;hV@7fI{6KvueT619CWmFc4;XgZ@_GNV9HhJDamxCu#bY)SZ1_v)(wmuO1h{N@pos zE=D&kZ{20Q<;Flw30KsdTv=dqTE15PD53Xy&6a- zxJDtp>Y#nqksoMk1I$zd?Wcw)O#4T&|p3_%6{ zpvq7|EY`ro$}pw@r^--9j-x3>n5hIID1w;(g29`%me*yXB z5uaZGeJ_Vx_yu6d$_!a#2?bBf!1t6HGKe%2TuZ}uR2kBU9k?b@{;CTFi9~ax;7?SB z6nY%>Rx;a)Dnk;;CWh2aqOjs$5Ez!q3<>1jB%l6+F@Ez8 zs5uot)a=}*MqS8j#b! zk+_XGHf$X>1?!0w!3ci9VJe8jbwAZq5^E8w%yJ~8S;Q)#BMBm^ zN2@hn`?Ax_!N98cdF-U+>Ecb+XM(>juT=THNGrxlUs({m=DIK0`;@=flk|{M#a&<5 z*HqHI^-~tcq{1{Ac}(S4MYQt>ya9Q*%#9T_x&?SPF4Lit|ORNIgnAn4~%`t)9W;jES3r*L?^yXq3+`1~n>@&RMJC}dYCDuaBNSJv8R*jWM z$uVcY-3hc&;b+)RTYfIj)^t92ifR7oz=exnJoGm|pL_-}ucOd()mS-{nn*T1x|7+> zD=B|0uyvZYgMVFtffg@aeVcPGzb#RI8e$f}Od1Cumqp3(Vgol*c`&usPkY{AG9UcD zCl)AI8M9&h_Fnz9-UmU4Vdgb32r;U$GAvPya8H$$y){Nm2>WE3HQcv<9(t^qyZ5jj z#;jT{{c0}6%!8TvFf~>hP&*JZ0x?VF9nYP__xO3P4;MDzY#_}hhz{u%(uUNEWWJSR zDeHT)WLwulKlAITG;C`5QUA#n9QSyg|A|7KH(dUWZ;=`cP7nrPo><4V@7wAxs9mpq zwpW!{Oh`tqI?3|Z7BoG~FfVI%!BS*vpU2@(EP3gcH;WWJzU^`A*dvYxgzk$*nYsAer)V~1C*8!E z16H1+=akKS&6^&j_8yG+p&BcJ7L1c+7kzAbFn7wkCO6iCW)0ou`ZjIezrl^~=WLz3 zla?#Y%z}YSw`#07N={%T*}O~DtQrD@)|NfB!_*ryyiY$xKWZ zCR@8Qs@AMjJ6i36h>Kx-(nXkwu++L(VOG;{+@98do-<+CSi8>d@NL;y-5?J;g6ZN^ zj8DOEo)*wAFT>0$pa)U!5J#-B&~JmKR|WebKKk9Qz=^V94?b`!piOh~z1BH0W8M4) zEB)Fq=X)4!OMFEh&n8WyhcVwc)uk8JG`mznPS5E*=n`MGh($s5j)XAFgoI8#AH&@h zXO44-x8}t8U3;gY>$v~etKIC98zZsOdwzDQ;!oJW3p?Gy>`wbwIM$*UdYpJhkuEu2 zFYZ!4yQ&(CV(!K8G{v*>y;J+;uX&49x%T#Z+VAD``Q>8ZZKfuF=mcLJouvR=0`Dja zycdJ>9&(ISbFY_@CccTpxIuP(NyCO}CGD*;vWGo+wO1gfILy2VE61YXdm+4Tas1Ew zLk!p7@9SHuiegXUlsjWTa8?$Sc3;lb2K}m z@v3GzZY<^FNhpt*41{KR&0w~TyWquM+8;l|A5Im1a)8_F#yQg|+TnVCZQeTvnm0?q z%nN^gSy1G?7&KgaoqI*t?AY<0A)R90JwNNPuxrFxiB3@f=Dc-QE5uBMnc!fySQLHl zKzPx?j#m=0*+}{ZWjp&f=N1pLwNdS^Q#<3@9G-o?fTVo}R>^^H)L0aN?|^sz>hwJD z)8O^R?ce;uf8mMs@AHhGN*}r#f5n#5?@lI`LfNkNd~L(R;|oUv#3ngKbeWgf2+PMC z9NFgO=Ibt7f3t{1;rWi3ulV~3lEgldh^g?rqTz$@y048$ybW}z@}L^c3p~JMrQLog zNU82RQEi)agC=NWW^HC9vXCcLdD;OI{F%EIj{MEEh(*!+j(C5%YJZLLCVMlpN&X5a z3tvB(amQm4Jfe~^WBmAw;Q&%a&-eYpC<}Y`Qq|<$frpk$->&MrKK&l`pntrVquuc} z;DiGE9dX~%-`Ja)JY}aI^r^|~P>Ni8FODx~s?nO1?sLWMs9VKK|4wl>xc=B?vFmi3 zJ%7x8_rNw2wp|G#dM0y1cc1S4TESEWk+MK9U_6BO1)LS)D#*R0ymZ|-L?`HWv?Jw1D-|lb>IjcHeoMW() z_8nrLfthE)tBB(N9kI5qgKLwo{ddlAyM108dccsO`db#rf0yFRXi3NynZQa1;}E{M z5KDt?jGWW)HT*-H*6W)+9w?vZ)R)T~raj98ZzAdd;E471J^j%LEBfM_f@tisd571} zs%E$Ro_J`lzv&3yg~dRuv@h|&J(}VU{~udU6o}_v^B0=VP~goR`eh#@yL~!i-N82S zD&o*O5N7Y6=eyQ>$|U4qS-923cZS@Fw<|8kxQ(8Am-y=k!-=&*j|GDoi&8^9M|V&6 z*qLd8?cwJpLTD z?aFiQw~^w7PB1g(uh#`)Q44zNy2VQm-*aeG>)025qfqzw)Z2ozb3F4qum$pycOYgo zOk>i3N`|f?9dJs~b&bKH_5#;}ojLk{*>+`RXbAXzakYdi(rUh5Q^Qi?`8yuaY)%Dv zWepRm%&DApZ=2|kvx5Z~HC6^eHyoF!jA)b*=8ID_S8ZvRvRUxg>5Yk!{FS$a;47ET zE!;_mO|~&lf=IBx+m7!u5pq1wNz-sFf1#xRNbT+={VVE&|zBOOh^s6cmnc4&Au@!xC$6#eD^0osJr&2zArP&nxT=jaf-Pkxqy6n>L z+VV5E2|X$KpAJLJ<1jM<=&=!~Akf(OVIiS?3r?DszEnRre}%{u(=+>idu4>r^1J$H z2W%i_I2@wN2uE2H`j;*yvT0hcwnkW#H+|bsXy(%)5{~=)fIHY&b6|Tf%nSoF6>DIk9!`Wxmxh`0XTOYhK zGYTH9a<^X2y1PhaoIn{7_xcvEJG2W??!t@Ez?l%@syyp2Jgg^6?f>`!8JHhClWr`# zdh$Y6VI8~c_Kaxo*m3kR=q>Mtug33|J+`U^si0Iqk!Cz6|e$3h?NE!It@1Yp~?tlap8eOVs5W3 zdRA(Su5>J$sj}@Xd9|pXBY7X28}4=-tW87y+I0$HQHPtEeugcDJA9P%d6XQz1T@Zx z363{kzR57ZswSp$jkW6(4Ej76Pz%9VGpF82=nbBAI#r+eanxN#UF-EKL+JH6ng##i znzo#~5HkqfbqZp!mY%yU;vKlTreE)<^vY;Y7b*VGx;+)E%O?dB{oP4vFf)*)o-hJg z)W`UriR4>e4U!IONZiWK$59RIK-`V(Y2G^)7IyJ~Zz9-rIs(S(5tJI^y{aY?x2yiC z-5IOkX|rOG=diLzzGq0jGzfl>e2c-j^= zlQM%!sRsM-1?uJ-_yl*ia16k5oipvc>i{u~EV+x0N#{?xU(MHkd`6-&RqCwfNcW<%!eh*IgyE3*WSzT z{l^d8Ilj1WY5;8EMSWN>Y`*1sVvp)7#B_(5-k_(vQEHrz@ORz~pRtiA=fZlctd(m; zJGVOS>Mv-C3R^EH8n^{!9{g(*97L%J4HfGOzKj>U+Qz>BbUET>B5q);HrFmco2Hj{ z`%%uXn-J3zW_rP>X~r6Ag}7gH-vyXy5pYOdGl*KcgG-Tf4dA8E12ls$M;FI+j=daw z>@)0j;4lj-b}6r!PP9$*b8nRgDSr6yr(6iWd^5SkPo?&l;xSc zi(9@Q8X8fgqxKCxjJd(%WucJtyks#0-)rfD4=g*Xym4Hkfjxw9c@JArsCJl2N39zU zgjj#ut_%9sUW)#2x2#+Jee&85*8@Bb#pE|^gVtqrB$jJxv^lBmRAdQv-J&~Ud=t+H z8UZzS@Zk;Y!anE}huZRwEXf^tPDDzNOx%4R9q>_NfPg6|-Su(FAc05q38|n}Bjjqh)xo-Qkx%dd%RLmeaqwZE6n`|?sGU6)JgNKc!YLOk z>WaQ4VcMGO*Z3H>#y|wruk?^gM@<+G1ot)r_WrU4r=%L**pTZ7Ty&7tK{8L>7b7n5 zxhJ!KyL>9rQA-9Nan|wU(%UatH|JwN?VtJGo{)LWU#BUyu6~pG^|uPo3!t7?igeUa z!3U3a3gbQR1{NMIdK>zcmZ;rAwm%syvS6gTUj5L*${R@er6L_QQt)ASm&jTjrGxm| z8}J9$xkQ)PBuqFLPVk6w#|<#|DIAAXXnl%w)I`CD+aW!%YvyCc>HQa7a@aVf-S?Th zZS6}A9Ns_BD=+$J0lM2urLzna7$H{!zm2cesa~!hGoHrYYU;X-y`nxt>keDJaW%k8 z8B583o$vK7=6txa?}qji>GC$UfHRbo%$*&_Y@ALUoR42tq@$(^KHNk8Oq$j0INN-- zxEuWqD@kiZVhI+a*rxAK=Ef6B-)TZ!-HLS7PQi!i;o3g`$>=DqKd(Zw=Vxnq&AwXk z)pgsp&pYnBmniiXdU1jrXiqh`JGvQB*szvO6(NvCoqlM~EHVj&XOJGewwiYV`uj@1W9A!-WInbka&}W8w1ebHiVa z#T1rtPQFrXqX+VyZaWm7Ql z0O$p497F@d33?%RDk3d`(_8;F?QQ*q->Nm-r9l;3Qs;*JprMvuP)VC2a5Dwc5xE~8 zde0<|ZRf;34m)ksS{)KmW4I&Ik1P=`uAVE$2Gu=Nq+7Bmv8}yZpg4PCPO^S!SO)JQ zBcJEbX3vhVyJm?MJ-t~IdfKW;N6i&Be7M~B*ICYOTILa%@0L6wei1ij)&1Z>jIQAy zFZEo(C_;`cR61&{aDdP5ju9W_xMV;_qJkgDUhg>-%{X#}>j9r%*(TkHZMw*TJ9t?d z9ko{Q;i=i-Er+J%JmKI5N2LhyF$$sF2)P{Ey?dkpbLY1sgfuJCQ9}hE;V^rHzG2qT zZdF$vlU?ikw|*LPWNY7}`!;HTd*=ix5~_cqOh?TWLine~B0UX$B@;tbw}D;}Sv?26 zy?QTojVIqx#)M|4n&2j|`UamL)Ox{3EFnle+3R1idhe=^&XFX>WAY)2L{Ipc9}!<- z2^wcap@v2(-4swd5K5JghYoM5J$dJ-Tq;Dpw9f2ikoJL{jatxgV;noiiP1zr1y|@H5g>R#> ze@{Ajl=Yq9d%51@_YtV}qB0#dga|>#TVK1A#oyO`;M*NwSfhRJow4@Tlo+E+Djm(s zE4TIOewO6RO)iEhNS9Jm%k_yFrGG16i)gS_aKA?0U zm=`^HRlGD3-q5!r{Z`nTPs6D_+Ww`9sj0Wk&U>vrK?2jjJ_Q3S@APqp9QzV6xN$M+k>L)V?Z7<%r@ z(y{6#{j1ch9r%eDid) zh{+H~OJ~igz4pIDpIM5gs$1q)Ztxb^-=Z>3gG$S&bWK!V z;*EE`fya~%Ra{J_zA|BGBoBR5sSy0|Vb@@}U-8AuntuvE5CcZC28)qXw#~JOs^|Pu zJ2C``bSdw5!>$bJ3};4Cg6pe@nDzfP`~dCWhaXU6ML^<%>Q#SVx)l{_7!&n$7a?Nb z%VHVz%z!7s1zUIF&F4OBh3*t9($&y7)@RW&i>j0>N~%c#0~^&lLLjhR3VijMLKGM z;)9Yu1kUv(hlrbLa=vTq>kQZ#pGilkBd>(_igBPx7#d2OV&KrM^13K1T!W2Po2&P)h95sea{WGv=}>rE1o< zn(@P?R~QFu#}9kdT09~KOUytw!E}w9VjS=%g=zaYi>e&ndn6PW;3=~G{uZA1`&w&z zx7zgbq|wwWu#~lO(+I11zBgZN=DzPVv=w+^xIRs6+-u~k@+PkgXQ7`+rUR|90L-O*nk`1;u^F3zNj53TqnQ z(VkG1^Oq_|OZK@ZKVQPpQY_?^pn~gEI%>ahz)lD`E%h0-B!0i1w`402H?}$!msozG zey8B;wNRs#m;b}W|M$F)T6Xwwjd?OM#0GZb$W;S^V>{PM^GGaPQit;xZ{MI|fObMD zlz)v1yd=?jT9x@J`MIcw=cxq}xqWWPRlV&NIZ-cIxid?IU`jafr-@&1nF``(?Mw%~ z3%=vgJ%T@fU%N51{2tz9xm$r*J-IlU(`@iNmTw8ZSdaofBioHm9^C!wh?LY-Y{wQJXOAgL-g7u=xVki z@TCOBL+29oIVj6H<(u(?x|fUT@4HotIqpqg-VkxG*#FJG3dk{wN=I!m4p4}?o}^^G zTc6J@*;RL*o8w|eXHwff#ad^0l}d<7`C=(gylW#bwdNAGlMw$qcwriFtwc-6IM}#f zeEkh>2jL38i#oNAZ(_G#^LRG!yzlkuUh-CkkW5A3TL~x$4`%SKc1e{dep1>h-E2Ip zN-hs&>$ph%#3t~&^PU`mfG-+wt^~5+!fvUhpf^x#R7OKi(oz&hZ zcIC2vbZv{{-KVoLK(_}w+~j#zW&|7__8;eC)^z*|ywRvVMhG69up;OP#K+Di`0_Q` z6MKa$GbF-_Q(lLT(<%z@Kcc}`fDWSwTr@#VaVGUH?Cu^-p}Q};_dWjgT&=h`2W)H~ zJ-&B76>)t;gzPS(OJWQR4m=En!2)#2FeQkDhw>Hvi7lSsSTlu=g=4rVfAV1gkkfjC ztSKyd=1s$VSjFLp@xHFCggZ+q+7Zz=H{Z!L$W8ef?$S=dY00%kZ#ohsWc_$vY~lUC zoNkYl)ez(q{tEi?A6*Xr@;JmJ9%%rs>U@+SwI!0XR&1AlC`Dh~f$dkm!<&*oF&!QU z_nVcr8%NX%7o;}Lo+&;Qai#+EPLXk)Kfr8{$m)&ay{_G}rq+}zQDg)L0Ze>NVTkf4 zh&qUDKXd%H^J<<%lKX+&g?3{fY+qcSB*@aRMd3zYzWk!``kPP!JtL36@5J29XZCqz?V2JF zg!pDruxw>gS537~a%WF}XbnK}2ZZ#EoHs&r_WGsP_PEm{5&&xZCqGXdh48ISyV}0_ z; zq146g>uP_ke=&NVy4if|>kSVX9Z4{#SkjYR}h5hd*M<=iWgZ&~I zM1vIxf%$KdGh*_t3v(306`HKn-oXqRg*2fjD))aIp066LF=+E^3c*_Gt30d_{WqRY zw*S?`7KYpR3X#9{oP2lz6V{Pdy^X7RfvZ7y>DYfTTCYpfheci8lx7?ZN+km8ZWyNE^^L0SRed1rKWrF7Qh#u^!bx2eIbVFn_xq^Az?^i7~q}@~;h1 z@T%8U-`KYI8mS99T4)gsb3dijo;;`1H@>L1V)?sf0khJhvFch-(R=WS(p^_RN8Xv% zBxtQxCI%{b`-8$hPJy+3Pizt6IeoQk*NpsLg}!bpSB}BRr@mLlKICD)-0CAklz)1T z(OBNy|Kd7^lR9Cm`(k8Upp=gHR#ClG=jxG$u4GE(yt8PD#=OYaZBoiIm#u!K^Et14 z{2Y^9>a{=*poHxHF=}#9^|#@pRQM++`hMe~dX6_t1Ib9;VI^;Gxwc|Y$z@8|Q} zf6lslfA>1;?6dbeYoD{%T29Ecy|Zd+8NSEe4en*|7z+H>(@~06>m#W~PPHk6#+u1t zn*u-XE1!9!V}EzPk0hcl##xUEJosd_xDdQ_)dwJh2F*wi1A-zU==*4EWB&>2C)obf%HKLBjcCqnqX3wP#`q zl8$@&v5)nQGSNFjTd?=NQi#>(1sIYTy#PIbv}niEFKb_7rgvjMUG%ADDZMcm#w>hR z9gNf2y9%vl)+wJy3{!)rS2c`d6}MwDF9n>Nr!94r0`9tR$_N>4p!ND(Muq3v2#=*z zL0gV4F?kzlYWB&zl6A%RX7hrJwBCh#=hI1*u)&M=Th5su24Ae3aIfz9)mxcP3CY7Fwd~gEC00D0-TBn`l=4pJjL-Wo zPU)S?!<<`6PFz0zI7xa*du78w%d-IO9QJhj%aW3bQ~ETi>psAT@z}ZGKBIs3G6&Bj zG5VTEH@CHG8MV7QGp=@b6Ib291C%E&l|7HKrKhI* zU~Tf3X?tEwGHja|yNp_J)~fcbZPrs4bGs|c;1g7wuP_Llb{GT)=zLh^dMQD-XUvNe zkuMzX?dH*lBdcKSw=fD%aN=9)2r+J7;3m+Yx{nC7co(ncKEK&bX+=|pob4omx}9-; zcFdb=kCa|J73JjJ7%91TfnpJL_Grx^X4fqV)z?e2E?Legat!BDC?D4dS2Xo3FJHHO zwp;2GkI!%FC(IWphxUoTZ54T-@{a`%z zs<4u?HtXHxrQt!;PNxaEy$VM(vwOZ6pKV{Uq|0xbcH%OveXK05#N!E$o7Kf+zy>~K z;=Jgmi3UE0S6XSsiJf?W! zT&|f6-~wYh8@xAKw02;-TTI2V6VFnr+RSE^s7;8--0rhpxp`aCf}TgWYF}Llo2C?X zKFZ7~UAJ++0>>jNHe#N>M&#wj6(&o@@n~MHhstCt#u*G-wL#|XDVoNi3wQ%)>!z|rOR+urxzB+^kmO$3NJ(2 z>XsdyK;~W%9Fat9K985qpHw}@A@OY63FMely_vxi>oeV1(<(pa1XlQ^P?I-g>Bv*M z_bzq-QK_fpkJ31|4?Ro$+`5sFS!KI4J0 z+mmd2-RKU_n%V<=URlQZj*d@N>tr4<`!-7B&h}}ep|T_$>2-Nu?LyY zG3kG^*o!jIv;WOv|Nke8z4~PM{Kq9PAlw$Pt7kVilIzG-;|}K1IBlH6oE*+_jtj@0 zW5&_tC`)xo)k$Sag-Q8I&5+V(e`Pnb>)AWlo7ihX!eC?I9n&j$0fbnHmK5j-^~kZn zQdv$p(C}vb5lQ#Iq?l1=N`(vy?g}QscOM2IHQG z%F8iE#!5p4Zo8M7HPI{Xs_)1SJYDxzPpBor4vD17M>%+3T3GpbyBVHuWq!&=I8UP)w%60>(K#p*q=+sj!%lbo-p_d-=^uK@uwtNzREm zY0-@}b|nsEd1t%4a*?#w6RLfKMqsbw@12-dqw~>@%Csmu`OxX3;*FCo>YJxeXdJrc zL5iQAP*ns~UG%HBoPzIPb4mz@h{RD1W;~J*s)+uuLl}?rgvuf=L&C+qSJD$IiJ&TB zf4$xL-OC+4p(1(x&P?Tn;$E=m2^B69?4yPl9swqwZ5 z`)+M1_-=9e_u5iV$oYm1WQbX)N(iOMb1-Cl;3cu)9}Ytno2w_36!DOo%lQ40NkFcd z^n?=MumR#$PkKU@2&(8WZq3gWG9i@BkOz~3A!^0-`|;(*IMLgU?_X}{3F+UkzL=Od zB~T%aLIfa{1Ug+n^`k0d%~%q~r+lT9QWjD4(fjBQbOzXrSdRz*68`w3sHn3uQ`~D2 z%X z(r;ZhVuGc|y!t1GnKM6ltPGoL9ICRqcHAw#xd@y+i>9HI`zA*7fN|LF(Vq1W0^XWR zTc&=Jo;|WhE@i6gxrK@2_+}!ky@1XO!))T)mPVf*RF`OVr+(^bwcNHm%Z&+k^phd- zMRYu(nLmOA|L%juU*9W`l6l1bl%RtBxPDOJi1hsR@|XF#TTMNzB$72kFWCh0heI|D zA}t@)(0Q>cp=IRZmbl8hJpns3w`RJBj^pI4iRkpIo*nNde}iu-!fJd$dAb-@cYOT0 z#@8LP=O>m8Dc)XOL_6Lv_2nYNJDi6S=}roK6A>7`4hUmp&M)ju(UD{=-D7LK;-i(Y zzVK~XPvgh8QN8X{4fAXG!$`2WTS`CTx^Vxi`whdMclY^tWH=}c(vezDDGr=twKBKs zT)Z*g_}e*4=saDF|L_1p|ik-&o*6WE%kS9&-E#H2SrvNz@Nhl;?kw{7af|hB93n;0%tD=_mOgSq<45HvL(rN)4Me*wyp7^oPW9csvpsO zuH0PyRNG3v0SOlOosS+aKP~4%@0hnG!;SynmfD4D{I0*1kqH0o( zN`R-aVF4jMf~Ha0R*1?{2u&NB2C?HXXg=&PI*GXI3?%LVe`(`TD$|)*Gyr;%1O975 z6(z+EM7XE8i@AoJhny^qJBKUP08*J+vfr``*@3_ga$pPie0T~LBIbh&)@9b<6J9ZJ zw(|hJrswYXLsM-y0mbFark#Fw($p@O^2d>E#C9#;9+uQH!DyYHU2NG5pU`?B4#hBZ;U%%ZBI8u#+;NIv4U8+A@?Peiu- zvD~?dUbh+-E28cWXJTBhYL)0_%zpgL?-k#cWJ7Hs&SX*41k?j%lazZDI`2-JEvr1r zPMr|OJy&nupT|e`Eq}0liy_|z!kOacB621+UF%{~>coZG8Rz%PD2<%n<>(p~BN#nA zva6~sE}B1vWb=<3IP#}2-&FgV7ZSbSfA)Wins<%wopWM-@T*r3KEEp0I?tz_On=U| zCi#$aNkG*)X8g3=zDbQEIbvOVvrEcXRUfC6n3?6rW`3k!cy*0$MS>AAH;{UG=YvEd z-Dhv9pUY#L4@34G+{PJJ71OwQTvu|_njw$*mLfJkk72=2Uk$Mx_cY%4d>S~`;lb2D zU4O_t$aA3h?ZA*liIJ7Q7lX#}N0WU1F%W(l2bA}3f|Jxgp0+lqRJ`nc?p#Cblz?iz zu}d>*N6D9kb$Cfob&)a1Cxk25%<5vEqYtGsL6p{8)DHBf#AwPU`f_e5=PPznN>wU| z6)TklQWr)uXR#VlHIU#9#&^}C46sJBq@~7k_!JHFGo_76K|V3+Xa-n2nv5P{>;|!0 zZ%CC$9%Sv4xCr9A?!+9KIrM3i@stpWH|!#GKI%Y!3|2lLC$r~Lw_=VU3Bq9OvEv;bUKd2(RxMtLQZhHyTZRVsq1Gj4Ggr*^4osj7=$KpU46TqW(FQ)P;G!0tt4ExbCI6$}*%j zaJFBn5vwr}q9|el7tQ@IK*Itc2odw4T@XyNvT!?Qq546$Z_c`@ePtFKQa@zVXLk0s zE0rI7bIY^d&+oqg`T9bTxHtMfBNsvQPV2;n-0sxI&*TO>bjP#wtt?r&1h z(t>w}>2jxy=r53O@ywjPq%zk$JQ9K^{#H$`i)Mzqj|g$L-)PmgjySg`bos8_fordv zR$|?%OCSi%@5(J(wmN|SXwkxw#642_?wh>#=dR1OtVx&J8$N2`;-5gize0sZkV1*s z6tjpFO3W^Ua5xOL=XL9gpTp7Eb}r zf3(RY8SW0+xzJnz3+X(-&6zB<3k0Q;=rzbGVv`pjodCIosPYAgpBxF-3=F4-OeBGz zR-K+X0aR-f_dEd|Jf19%Sq+~|KoTJ|KsqVfShBbw9^*0W8PAZr z^rJA%lp5WR;f6TTVD=_;nk|W`aRlFmZHSt!Ahh!s0Cgh3PJJHDnylmk1~e;@+_N5# zmL#dR5{N#U`4Y&s0j7JB+J+errt2|v`je3)pxOrj%t;ay^N8$i*d!{;6X0WmK#eI` zW0wdT^qKrc^Jv2)WMOYD2EAoWmR{Y6@jwU{=}aJ+5m^FLGK2i2v9QEIVh(`&Yye{E zla&RK)RR;|!inyAphtBHza0Rl14^_BP9!+7Hi=7F0V=hKIom-cE_nsqfHCkynnV|1 z3(O;M!0A~$P@_%2oYpjTve_=kL5P69v^HV75ZHb; zJ24#^K!INVQVhZ)ChP>)F4YhEbRR{K<%T0h!Sw5aXG<&-xN!L<^WQgFdPF&DH0u#3L3JxIOv7!WKb$emO6t{ zkQ)B$E^ujqEV9TZ3Dh!4vIDkZaPNRN#u$jUNhf#^IEMyoL~Q~M20$tVg8hN&6dFdP z?gB!9Z>Jz6vZ@tLUB+7i(4!f3-$Ds67E!cYniB&US~jFJUHp~7LTLDs+lOVcIE@017}XCMWxXl{~~BCYWkscx7# z0n*}jLHLsmzMBGY=n(3=FxvOZ3C?@en%GSMsqDxWaQdTG#1%M=hL9!S%}zL#LBip0 zBL&BtS>#{rdqZUo1 z=l&jZMAr<^H8V*ryt&|%9>l1~224@~aH1mGY zeNcIz`a~r>uOrzAPGKDtNd97z!w-8L<`5>ZqehXaLbobXJ$pw*3;Ur~B!qwP(Jn1avR3d*T7VF4W96x^LiTLXrZ8_5OQD>Qrt ztcAgSY4~(VLaT&=Pa`_lf?+?ExC=O-Q{X|9o`}0b1T;k`_+-KbAgC^|gfv5NXGoG} z2tEmtq#1%cK@uty3hqc$tpnU95>wUzZXx)DZ&m78CO#e(YEk*HNMwHC8#sg+{#2#><*@BvHlMc6m?cfBV<6{6xC#pfZ1{!V+i=p1_d`E#w3DU z(1#C$gcfkW24JYYDELs~(ne5X1WQ(g!xBT{6}SkH@m#n89P|kp=37DZC-tkR%l0rT zusihA#ymiWs{dj|3p>@8yo*m+KvT31lT^y@M|XAaUy*JmSwF z0bCV@;)|eEV%9r&I220EdIt}Koc>+_{&M|?F9aZwStR)VmSQ{f&COFv8>nzS?1P&ktiI)h-RFS~7<@9nIINL$g2BuV-RH0OilsWq) zyApiG^=F$&K9{VL+#)$oQd#097-}aZ1cq_YwN{y-l977*Q%SI%&5@(8ST>J3vyT`= zCfvLf(V+3>>DtnAq`w^PuL2?7p!=*l+EjG7a8jJox$Badqqgy_^Xu%KJ{#yh$upA- zNzYsR5$&%;d6g%2W30)jZ?a12g#g3k`fkEtt#wqAiKgVSmffQQdky1&1FZnWA5(gG zd#mc!CvpW|$Q#2%no-8#7+DW|#?+Zc_nT}#<4Au6+7C7j$3ZVzS=6cY>)ZHIZH}+Y z?Cp=6(3$Sece9+#r|4&x4Ijvhe2Y;KOfL?)(aO@d7kqeZIbbhS^R8=P_rT3G)y>l^ zuCcQHO+!n@T=H0qQJ!ER_9FD66&C<&1&CS&!hCen{Cm>!-Gxn6p=mFkjEp><*xes= zxN5J`mVGd`o+aR`ky|xgrfbY$j}^d z(8*R=fVz}XyPh&`Xx_u_&UQYeU7)1h`Ps_vw9hx# z4tm=vQ=gqY9Pg}uD(gdl(Y)w+6OL?mIbG2Au5rMz`1wp{jSw=H`4P|`(BoE_A&2MH zBIOBKhRWkhcV^#wE}LNB-Z_(YD?8CoFxmT|4ANhW_LqPwB)x8__#?-r&(Zfz`43O) zI)F{T(|cM=FD`cYafbbS1I4<@Agc18H-~;4A4huKVhaMdtZV2lWgprfed41I|BAiz z)2*2?Zx39l96ik1nu_r%zY6~NF6EY(H;b?ymO0ffx7@_B_GG#JF?}N!3%7|4u;ngk`HQ{UebS57typnotxbe6L zmPSZ_A=-ZkT<%afwLsn}H{+LI)?Q_LyVkX>1uON(d0%I!Uma%TJ8ImaTNAfnyuX$H zFLge+Ug(@V1*wZ>#tm|-iKzIXrga{j&+%?;wW-;2sNT~tc#zX=5Om_7Dp>G?XA8Jq z=)9|pOb>fF1hX?}x^eG@U%P&ZL4A4c0#$R#u#Ilfg)NIINPhte+%{z6Gvg-EDv9bm7 zCX0lvDns2imOk?BgW8eKr6AnvVE;=j*PUy`r2yUcngb#kz+gIqDKTLx76pf0uTL7g z)bPQa`uf)nOB}``M$%LFVohP|1;~wuy27~-Nwc_LcJSiMp1Zj6p`6l%?3)cyUQ?|( z0+}{@mDmHdK}h=pU10!3Vls!TQzqI~%Z&)VoHM0Q;t8WQWd!Y2ipr`onUE<72a)^d zbOl0xlIg8;b7GVy8<$p8w3r6A7Eh}hg3LQ=WxDND!s5L8pcM$wsVnq@NV6+64pbY5O~-df4X34Hc1Tr+zR(vS2f%c6Dgk=%^Osf#pa4^Nqyc<1@@EAp0rIZq|dmhrpJ zAi1Phu5sg0baGGb*Ll>!9e$=HLwt40f&vi%9a?Lb^g%S4YIauq7>F)OTN z46cmsSnb(+c=O>b>+OEaoSvwS{w6N{x8g!B=`Z8lc+`V$G+oppm^XB}C2EQdFL%0y zZC`S>$#DAdFqNah)ZwJKl_^8hRe3W16n8MA|IimlpX1dJjdmRj2@Ixhj=b=E>(koS z2ex^NSFE~66MlU_Tt*a2iM5kKIYOa0beP~`-WyLpfX}=d2 za7pK-x~o_2R1S)bj!LSX%<+gm*xq_D#@pQ|V#lY-_~b}y)(TSE%j-12@fTx3i@v41 zo59YRN(|P@mZ~lvd*5ILUw8ft{;Cu=enIo7#pw};!B8l3z7a8m`oTZpz_N_+3qesU zG*1u9cue4vXz|H8gtfRh2d%*Lwba zr~i8O1Md!SZ_Xg4b>65!jP0iVLR@{?&AW+t_AbdhgMszyQ6&7ZNU9P?K((E$Q zy86CL`ofJo9+nkY-+DF{O_-(JGADb_vbGni9+Yz*7qb@LBfg1?{Z3rSBz@v~ zFmSJ1uDFskvpHJxWG2fccKGE$IjgA5*PD;pMaJg`Z1o|f{j&ZyeamD_d+Vil$slv` zgJOf_mc+>`FUoS_>g0PS#M>mSW$14wcwgW=GZxyNvCtXzOqyExKIKAO=dPz+!$a65 zo2b0^7GqFHk+e1xMQ7G8^)$&PeOder9wmEaZ=m+Gk|2q(U)Y*C!HLd|@qQ1J3Qsv~ z&Dyli2S!)3OOH;}Pgmk8{Bz*4X{}b3uikn_n@Tv8sE@?ky%+g(Zx>V~28`A0h;y(m zBGP)Gxc&1-91m|CyJ2bZy%kH}dd3_&t#e$Vt>@GtW7T!@rUZTy7gcm-{#snfCH-#V z3?A0&tG4CB%uO#t4;-U5FnsC5nd!GSeU!e~kd*quZvP2V+|5a(xTfe2iWosqB_zL) z*Oh*|bKRBg4^$sMAF=r5hv_|&%Zi9Y2Q^C$E>54Cq`{*g*-Lth8cUQT=5-ZUr8@ZM z+gayoZeB3k;;80ITNw%zcIy?CM&sBZ9Gj!kJTnf^?-IWo7?|4ZSyMarZeA0WYZ_a4 zYE8h^!j5<0*AdC<8Vfa)uE(;(Ss8v?n$5vEP1WDGpPzO2gHdO_!MW|X&jx-i-`uCP z`I2)qw{+{B9?6C9(lx{}Bh~vqABjvXH{ia61^?0Rtkn{sedhx{CEi+g)!t*-0Jrbj znY|azihHor-ZiI1-1M)wq@}X?wCfCw=Z5S>d)00nRrUDlg9f~k|KU!+MN7rXcEK@|pu@zN5F276;ef{E&F7`1CdNj>ekds00sts`lrQ3#1=r z=m{`6C@34%6q9EiBu*3eqK7ALn>1k$lA#gp@xTdJ>91^&zeyI!{PTx)p13{TG(`PR z={{9JGJ4Q)2M>O1X+D4F2;=&}CGj_lnqr%(KZ|@9wL#1Qad|I6{i-9}8XzEK#>Tm+Yyqx6yWB310oBw&D z?QW8Y{payR*Ves_eq=PwGU?KAUF~qLM^VQS8R=DThRxgDyhg4;294ca9YB33tVNE;gM)anvp@|9w(oe8A4 zkql)1vRL~b)6y5P3mGiD(ww)J)sv~k$sivRyKv=-l|c>L1Le5EWP6r=3>HH=#L)jR zHq^ytv+G(dmG4&d5mU?J1Izv1o-_4%HE2}gl+#n(GYVVvc8HorJQ-9$48oKv0t1z+ zS6eFU)>s%#wrBoLRg4(roHZlGM2{5vt7fSg!IBAz3FD)lvZ;*q!|v#uZJ^@jjepIl zQPq^cR)S)kF)>s{{cTlru0%OvT^|e6<;T1{yzkvft~}hhVYa~+lv-2NzcPLN&qX<3 zcj7Bl)!D9>C`%91wGn??n{vQLvGUu-K4R7z-xJ-U&K_^jZ!Ks$*XP@-VIPi14PPDq zXj}5S^=G^vs%qQ3Nuo6047GK~^bFw}8_XGV;YE!=4Afac* z1iHfD@5i+Nu6r<63{w_u5i4(QcK_dw*v@dW5Yzj+>h8m69k)r83%9z9@iCH;K&^c% zdew&AXZp7sSc9%Czxq4lan!KehXz}weN;`K(>6Op!R5d2TkEQk|69SeHx$13`%3-; znRV3v+baH3S+zId!i2MgzZs(2qCKKHqM@P)kyQ8_R{3)ws&&FhM8MjhSTZ68gAXK8 z-Z?Bo!<;c09FCA~56iXFoP&73}gS^Qo4e}x*G{}?e#ZjoHU(16G*AU&wFb#4ddT5X%;iEwu zGV)JE_^Oe}f$-KKd%{bNgngN#H7JkK^hcI<*Sea6GV}D8e$}wqd^g5 zmIn1AGc+ii9H>EoWTplMkm(xKlN_KyJ;*c-awoGjNJcnnkd$!HAWOoYL&5jQFoA^V z&ZR#DF5oa3lQj7pGWSnJ*r^d;OxUUs&x&x;AXCCwgG_Ks4dM`THR8Gvp&H~$glLc% z5v)PRpsCc5V?o$xkU3$kL1u)N2AL944eExAHHbsV)QD|P1Zj{FZlOUOB9IMZ;$SR8 zGYv2>)c}1H4bb5lYY@jASuexwgE;y$fsIjGdKw_o)&QZF2Jmz>fTN=Zq(Gzrd?8x2 z3^&dasM7?8s{w3|8sIFp1~6F~Kv5b%k{Un|8h|rd6bE{P;}pf=usQVhgo&CMq9)jG zwu&M`=WHKGGGbyFi3d;ZsU26XD_{GP@nN0qGsBmHjvoyh6AcgaX1fh=q=qcqAiuHl zskTFQ<+>REZAPhAu#!UFDcuhvF5PtXPT5(@(<0JD9Sz#|--!$v{iA5(nH0T%CnFP# z=P#mnq8HHQ4Ut~PWMd!>EKh=GdN~iC1HyM^l3F?}Q5(Ipd=|YGJr_L`-4V4xfx{xy zmVrCVwD7wOkxckNI2*q!>@BF^uL4b-%stQT$=St`u?MrZGaojr2;75nr(tZCp^Xhz z6NJ)oFbi8wNK#l(a+o+dC^#leY?(c@IHxdUa9&n!-taO@u`O2&{|0B34OY%!yDHn* zPD&O>jQZzdw#uO#Tjc_#F1neE8TJm2i4S%VtGM$<|&!^*O9MidkegBC_Yry}E$!+M1!iR0pv z#c_RNV#J|g;X!?3lEw0W8HV_O;%@w&hlOG&a1bU&;(UxkL4&XmqCDCN&CSF-F%xty z6RQ^(vJ8EFvB}totn8tANHh~OL={<>A!dx0WMShuJ_am9J3DN$Ba&oe63hsNWMdWR zT^`1@w<{Out3FtHs;Y3=h~nW{gYq(l7Z;Bx`| zOOqYf&WEEk=Xo1(6A6}~zCKn?sfIT^FTafbA(it5e(2FKER>T9D#qCvo1BH@#h8b_ zrCNq^^YROZ70@4)7E@)AP#?`I#zuoGorzePcz3(Q{Ca*7Ka_96`^Y=XTh1HJi{n{w ze{rvHH*(9l{kYCtCZ`=%$4pp8E*#v(4#|h$)`XXhExKEPTeIA3Y`}`dmD-*X4(4QI zO(&RWdjTaTF*_S8R1HjQv5ibcS4!N)98BLvN~b6l7YEa}u~a8%gO$y&k*HEiNv9ZR zbfAtQxGnpOjfI*x#~X+rQEVX)pWB$J%Srs0Kz)do72EU_`9lge6e3CXCpF+fM42)oi+AE+3CT880b);h|%nicaG69cVpxnHrH=-zBB9CJX{HCT6|+|nD}JB6E}?NdO) zy=g8pw0;V#L;h5(JML$X{AXg($h8m_=(3que=f0u!7|LT`S*&+^NgivX)_*xS)#km zcz=Anxze0)2X{bkBAmmPF&x8euyTFnW>SXxxPqXYqR{D>DUVfdD`6R?IAZ0g^@W%L zd|Bv*44o*%2H_Sa=+{il8Z$@IYHS>IX=ydq1G*P?8keEYYAg@yhH_@XYBNQ(v#_nE zf|(3sLoY9EvWIHr3zn z<&uJ7xp`y$)P*BUahe4y`cg;)%|^>XdV^?#j2V_+YRBUxc~Am}kLa#*9vR~{0ktvXGg zMwRIv63j*^@C?VSl^@*0DJwb;)fV8E%8Tw|Or}(Lgu@)X8;e^jW!_>YttVnA`d}+= zIN}N034In-j`fr;8uTo!hXm7CzVivk^prf`a3;O*)hA`;Azuk-c{nIrDMtg;R{qf= z9MghBayXM-^S}jbUK!&jVG7k1s)qgoDipwZIh;wad|-l=-*C%M4@XCqQGrUQfEZL7 zKuDCL0P(+^nUxCz#B1+k;<^%M1M}if5k1j_0spd+_1+OQ@=`t}H6R!ZnK!wI6x$81DWD_EsT^K@V7EVH$^)|)uKF6pvYMGS$F{s z`wC${p^K0!cqBL=m?sz}h!9Bl9sD-_R%me?UpXfNRH&Y^Cf=S_qD z(k$MZQd8)OVv+i)gzBSBieo9;;w+UtqpWa3s4R`OhK;=RAZ`tx?tP>cP8lc<$67P_ z>JqRE-~`NRj5Q^)f^#gw0ticx;TX0`o3lw7K#pdLG)>aN#m2cRCLG;AOj9{V zyDa(uIbBSH>2Wy4shi+ryW6=lQ^*==#rPb|W}rzQ@equGj(^0ba=>eeG0flxbsZoq zP{AkM67bAVcqaOFo)8l(a2%CfB`r9)Q2AS&!4RYE2M967LV^DDN+iV;8ul57jTsl? zXx3*u&XtX%g4xF$O43Y!Ofw5@fY0N!6(PvE62w(%f-bbH!IgI%PTiKJ2xvo+ttm} zCqFkU%hlV>BUhg54o3tr+G;}49h>k4w?vXHFq#8jz?){ke1{%sw=Qqb=yj*c_E-H-O6+EV{W^NuD3S=fIke(t#;YZ>5 zzfmB)1Y-UV{$>6q{#1TC-;*!mJqP#QLS7j!28`j~+#B2-+!@>)u0Pk1^PY1S)>tt| ziA6go>m$TC+3oLC4U1{3)-v=i!=lXpO5mi?zbhf8l{#Ccy1SxSDjm)uP1K48_7fH% z`Uh63)f6ZnedWbC8_-tPuvLQ2QpQHJlp#^jBV)A+(*hM{g)?nbFUmsoA`NN1hDt!I z;Y>SPyMbOaQ@=@5wYC$2wzGykS*7!&DxD`#H?Pupx+YYHaK& zC+c``=cM^QcTLOn}5ap%kB{&;|&a-IGumS!hlGL0eg37Hu#6X+sEUzgs$9qq!DB z!a!^nSlJgaN}8@R+hi12c?0T0Fd3Of6KXC{V)Q#0lM#cZsb~%WtRK1^j9C)Dq$xCz zVSQ0C%?_F~1_jyE<&#ltC}i|T59n7Cow8)jlqLcP3mtW&EGTIL&HWoH$EERTFO@v5-L_r?sR7E{#FgkWem)E+>X^lr&NUEr>#C1WEu-vKLLd(?Zu8 zPE#z21=27yF#^CdX($b3SO`j`U%}{PFL(u^<@75MxzMiw)E8c4Pr91Gb96QS=p|4n zKcG-3rX7qesV`s#D!NHpu%tZzGaJywo1{4|^+8L*p%QQ81TQ3v0OM5D3!F?IG{KTN zTIx>Md+0h`qOFL);!#T^WXjRnNGRltGNRz+g!Ji`BYF=+%0boAqzZa2?9m&#+uf0W zG`#HSmnCzF)RwOD{TaHeHs}*w)*7|I3$2d=rm1ubQZ#^W!4i2z!aV$xN@yU1^V(FJ z-2z>L3Q2Rio|0C2Ud?D22v#O8HAS20OcR=dK_==MgEcWvst*Z8Fwe*|7DPiJ)kWq2 zNcx~q{}rshnXy69VUkMuCgPt*^I6x4b-#APbhPvV~ zse)dt>3A(HR?Z~$Y1RvNKekYKlf*e|2pMY}y?DubL9oD#dqGgmWHTL^N$l684bhI* z^89#)u)vF`?#$5?#as-_`Wo*GN1Ho^b%MQ;{Rej-ADq^rI<^~c6HCrFrVeq^Sbg~4 z`B(V4WGUW-@5XNtAxhy8DxMla-{z9{MUBKTB2p+8hH;v?A9)v9Jk~PiQ=&=G!Cg;1 zq82cZ@+#p7;K_N%)#Fd+`jGp1S^NN4oGoh2fh4dtv2?Wk0wu!+pqcb5jZR7N!TnKf z9U;R~QP1;`{MC73=P!_6g)bnbjPCTmlQ9IMDg>Wp$K%N z1*pB~l$5z3TvfI_0<&N~3;Pp`sZbRh5CM&bsNk7i;0_H&%Mer%gyis|0#zBQbVdO3 zI|p6usiM50DgJ0SG{f|xDe4u9`J$usP^^cFX?ZwQ?W2M&0FhumDBpRs(0l$#v|x_* zP+|2lolmHishDz65jR9aD{?gHEVS#aq8LR1#Ry>g)<-Uipfaqr} zf=OT8oqp3x_w^YlZmVMW3dJcK6}$uxty>5^n!11xQ!*7X0~RpShRR|2!b{5hAyLt* z=w`*}B3+>c8rlkzYmTz$=`d5}Z;OU}Q`Ff)*`bidL<4@(02RR8Q2MHDNi0mgK<`g1 zrgUkjUb#z%NSvXCb}S)e=;so`9<`mL%<*&qx(L-{d;|e5GA57a*DP8tjsIM6|G+&{Rb+L+!PT1zdYW>b@?ev}hsNq#3^lgG&>ayB`c97PtA8Dwv=7wJoC6VHg-#15j0NGIF~ zE`A?x!#CmOcna=_lU(c;wjHa)#$$BAP`)L%oIzh`YB2>I++KalEM)3(@@yy4WChHY4bC6lIW%Y8@zO zr%aRI8%=bi#5~*KnsgG0N>mJ`8bTt{UJXr^Xp#x&*(yS8z%8cJT9iQy>$L4Kb)J4a z`l%XfkvgpxhvcgWN%~MVt6nVYG`CQVg&eXkcZeEmMYEo83p6;E^@KB+#zIP$lgk~X zCJSPKC0~Q1p~XBJ$B=NPjZChQ7W$lART)yi$+gL$VH7EdM81$^z|B^bBL(iPC$?Fd zauFyK&TIzUOif-O)S02l>xI(VNcdm_HR*7av4#*kbJI1v0nnEL8r&0xl%~Nwp#J{q zGWuaS+IWYAX-?G;LQ(S_y3&4{bO_qAj-JL8bzyxk))Sk)G>#&L!D!4ax@;dcuP_Kr z0GlL~o2=#)20{mVYp^HlG&_mLd{W5aCTgew(5(aw?g`zBr*RCa=Z|8cRjzHEI<4!6 zHnr2eiq)ij(X5;Fw8W^(>h^$XiPq%#pv<*&*(h~d%NyNULrD5Zs#&!_`neGr>;=vD z(qK;*SU8QNNG%WB8P7~io~=%taK}B|;F81A$0^ru^r<~Nt+8#84|Q!OH%wE=9oh{= zTI&g0+%^P-uP5vrf>l`r*9-<9q{guLPjdokEF{>RT-yK|#t=F#XvPMR*q)lC9A(l; ze|1vZ8D&l(#S*R`oz^0>9iS3l8Vd<6ST{Y?Y+6p}?go0eKAN;6GMq+Fjkmg($N{F+ zOPwdON7tv))99&A3&9B71TA{d$tXf-hX!q;m$thm+ZOGGs<^gpnzRjiv5{V)t{Q%8 zG<^nXVa0W!(^>?*RycAR3ke=4kL#=^^Gu-!PH6N-!j@~}s4gmyA%_igQx0@G1`I!B zJ%et_UX!*&Th|j3UUyAWLYkqiGf8tjjvY-A5(1b3TXiNMU8*D{Tw5D;l4F4mO(w-M zt~H(3A~@#oA*?jm3_iF_gFz6`)lI}MKHD533NRsylV@#)W^5*G*rqfghGl@Ze8yqC zChDZV%C%^r$1$eUS}YybX`61UET$eh_ywXcY>d=NT_oN@NVtY7KBf-q2^z75kkg9S zMvr1c)sz;A`v1T!EI7J!T8rX=I?+)>noC>FB|_Vw9&r42Y=Kx1YY|Nag@GO@C<~5_ zP{l;@kzyMmF%!@!A<4<(@M)w)@}PS>H47KTe}{pvxhfullZ&*r!L_RmTa^ugbTL~A z37e%(G12?2gjkQmq?1}W4(kHqF`(0MOM05-Bh@JqWrFFS@P>VPLBQPiOnzIRp_!3FVo-df%yUVolDPsn}FJ;q(k9mP%HcIT3uyPPIY z9cL&foFiucWM5@(Wlv*gv;EixtoN*D)@s&7RzH?2i_d(@Ji=Va9KnoZ+A?wK4z-7> zrG`+UlsRm;m&wg!C7DU~AoYm1#2I2GF@fky$YGOxj2|+>=i|fi81Py#V3Xa2&Bg{} z!I&xID{Qouq%5C^LHS*zB`(WD0j6-apk%pd_E9jpC|M5JD(GGpDWPQ9G>owWqmhzj zK@n^RVabwZ0+kqsX0?;%6e-I9dLqzq*+4Y(7$GG{Svs1d1_MwL9CUyw4VXTtnEKZr zCumA4P=a>S#rmNrH7Ny1bAUw2`qJ%w0QCu#^+A)mK?g}$GPkWJ^4Rp~YG#xq* zwIu?n97s%A0u*JSW!FgyC=!pf)n((*Kx0)UvFO1G7$qr-p_@=#1GhXWi-yvryXXp| zP{K)7_acFGR7HwFuZ>hid!fUS2Svl_qRX$SiiQDS#%@B2g(9a@P?C^^0A<^5y0T!D z2CV}r2uNKjQXuj(QIP_G6u*b&>WTQLVb%zlKXL&8wxSnweD!5I?oOAoSW$Bb)_3fuYl<#WG*O6 z%_m1@W~#0^179of;W8&Q;H;`^j;LG>9MErb6_Y(M74M~)x}*1y2mP`G(qR?J7WHUW zm9s$^09b^~8d`+gl1Xq{AY@j+n6Zzrgp>ORW3bZ+8F+2bHlQ$NmcU4%&i?TE2$=-= zpHo#WhJ362bk{BDt{)i)UB_kSXftF3FhiHrz!cR$_kd!8OwJQhHZC;=e-KM`uK^j| zAfdYaU`dStV(KdtN@_^cr^9UxeLDz{zPO=p2LaM|JCsz9hLQ_3)TLqhNBTgn19x+% zP({_IVd+5v&gNPG8At$UbNb2<$cKSU-x&hzQZWl?C^$qj^8w;$1)K#aDUT)(Yazre zDVHYOps^h|^qND{Hvl^&Wz+DY3bJTuW=cCTm^9p>f)ov7T4`RAhNUV<(6AmL3p@vK z-{yc?zTxJi6oYBKcbKpc2Ij+s6=<;Qv<3@qZ zJQZU_WpHIVNR%Z?6ZH|r!-*wC@vlBX9$~?#4%6Z*W_C<|vg)AE|ndP(903 z&*7?Psp?sxdZHO`a9>?2k4{jzs%MVsiEg~Xw?GU`fvdw zdYY-8rm82J_zvRAIhY=7C1}k%JgvS2c9ne4(yaHM|J_2Qpu+#Vh5UC5`R^9;|I;mm zqPGw>x-X(6X@ru|6ANKrA)-1?Gky!a$8s`;Xy<;!!&yAR4p9X7qjw29h)B*P@I{wm z*ZEi2D@BdiEaqZ-Hw!>hN?D*NtB{-f?V*750 zAsBUvh$dO%psD~9sNm%(q85!~Q4hcse7_O$C!B$N&KkyCm61ic0xX7$3%ZUqP)KY> zsgM=A*aY1D&l1sS4Tp+_;?WjRpHVYWOY#hXGjkv0pKm6jDPAQbN^KV*-*doU$pikq z>X3%dJW7hTn^P=0AR?TEARsH+%CSPT&J&Wxt$b=EPTvYz(Kdn=iV;ywgg?s}wiq*# zbzIPf0<|a;=VabzUMo9=w-J8%CX?sOGvI!JK<{VQmUEWv%2~}(u*R~S zIsF?KYEerGxa+eZRCj%iZidulHjAar;5}q%!}dzC{u@>E->91ZtD|ac|0Svhf|dky z&=Qk@F_=ei+O>~kB@A{3Rst7WxtQUsp%jHJ@^qq9VN9!d>7I;T*#ZWNwxcAf@G<({ z3Dww9`E0BkRKj40>0`JU8FZ%t=sT+`q$L{DoeG1n9pIyLZg;2X*r(3!)HIx7fg+9) z7EqG~Iv4^sC8zDF5ER%&T0mJk1`WkJz^MRCQk7RaK>9yMpItDfm8=+N;YjQUzooC961$KejO!ifqb z!P6o}@xMt4dg4S$=$_xfC<_IKcWnIU5I0vo!)JN9l?)=$8ZMhnf=D!3s#h4%t2Z-N zXX&^e?(##jkH<&NKNxCmnh`)9dVK!L*(r}+tz)pCuPGz-tLG(ESDes!=9m1N9jUdr z-2T}atMoGIeFd?dgkrpk)sltVwh-^K&b*6lBHBW#CY%Mq~%2l zJ4L6Il0aW9WK^C?lCmI&qyr4m@*5ywi@L*A3LR?*F@_BIG;st)OOU{dGIYqza(9ut zxw^Y%x#YXJWaqf#XL;tj`gqf!g?U-|-np*1J`m031O6~E(uR7WU>x`->F`Ga4YmTm zl`foyRM#_TjTI%sjhN_y6%_!-Be5z-1P@>!M{89FA@J0|Rt|A+-Vn-|mklv??pe9= z?0k6+gx_Ul<#^>od}DTwcV4c$CmSvNr4saM)*$o~4OLx!X$==ss&Grb4Hd$q9eiRY zinpNxGphQ9C%AnoUpc#O5uUtz^(RT#xChsI`{=K&($(MF*#Y5t>r20)+Y^5b`F?my z^;=iZ>HBvqGtTn%a>@7ff!Io~ynG0@gr4PR<$CAj<+(werMFLZcD9R8zDpJ#9fV0k zuWYCsMZog%4Z0Ds53S2L^eDlnhhM%#1eLPi3={6$^(m^b*N;^MPDVQkSe|kvXu+pfDWN{gOB__ z$v)OG0)+lgv6s7`cSfK!ZeJy{Xqi%5W`Ri224NyB5@8UaiH@|9Mvhp1em=xZLda{L zkKElo-zyisjGKF|PZmT}=4HF)yMX`!11-~H(oPz67XcF9QL1&j=BMrMV zvgC3f&m4IU2)c`#n>@z@mRWvYzI%SY2Z*>!b{>Re=6QKDn^Ds>h=c*og5%mK?zpPGrdwnVVW2Ys zncgM?RLvRhB*Z}=%nogw`%iwp&Rn%>^Sv%=*p?I9$DVV|$@P%?_;)-dUaNgzG=0-nzxVpRRv?@20OSvLO~0 z;)}iHd0F21ZXP}me4HiE$&-8Jx_IQ|Wcy@g=R;7km$yDDwV*6;&_Q(2CXAW7L*}5u zJES?21rhwPE@$2$vvFfXRB}wSi3#C44cmj9+Q|$|7 zhh=E>w8O`yPROJ~mGu{}bTZw8HQ<`C)FQ2?ZWE5DIQF^JoWvuC+xAo+k0Svwl*;-mZFJxZE;V7D>I{Z?>YZeSXO24eV?$O{KY+Z%A-NYvqG2m?9O{U;M6Kt z?R7EOO1)PlTK6t~JbhJrz45oqj>@o;cf@Q7|qHRfld61 zvcVkqa~Ku56->6uoWPjy$5O-!mGd7NvjE@h;QV9!oMu&0+R^ zVyHbw|3iD?`Sa(EV;LcgNXCqgjV*63Y z%s%ND!0FD|_;QcOvrp_jX(wVe?3xxbJ5ip=IO44}nAFk}%bCMU-(;;+_{`YnEvdLL z`bA{S33*YLgGc?Txu;B5maKo({&M|~52ks2iaI|R4-8*#xNL0VPwB9`{%>2KHM-5~ z5Puu;P;&kI`sp`qh{X=phxg97J|trh@5-@QA$d0L2vu4e8B9unP&BrfoK4Du>AWV$tHy};CaJ7T? z{A+cUp~Jl&&s}+~zegLNpcu~jOPh+iotrr`)m^Y~>+;pA*=A!)I1HwKfMr6#CIuml z>!v;8cJ?{p=jmk1qo@-rMwtPxxj$1Un>XIT_2bn3^J} zDVg7k>p&XJoWz)19&>g_N}1)FV>kE2)P)4kTjL|XD(kgRr#i9Lb>qeS7ly00>x$)* z`5rw?n@aeD=dy%nWB2b#uAKGu%(r>U&?);`E}4z>mAtB%^Ezhi^FPLX|FPEnxnmBq za^fIbimNg!#BAE%Ao)v%-(r-AvG&z4Nnr71=DpGzx{WI@4W7C%e-jlbkNL6i&VZZk zN00Zf&ua22V5Bgz8P(18dDA!TuQ<@Yp!MOgtNXth4}W{FsC{r=#+{vcSK_voJ^51o zrQhj62itdce7`p8_V^UHx2_J4e;*#&tGu-S>||3@gQvFZY+uTS3i6?3`s(Sc7UNUO ziAgwF9?mEa3O;%yYq4IUi;n@ed%@QUUc+(@zu7F``)b{&l=y3h2A1uO-{G|1Yxk?4 zvxgh-96JWi>R))X@ru;A_(eg^L@&dPcW;ZL8&2Ihf6yuIq=lkQt7iB|Th^v8skcrS zI<&mpJMIpY@}WzX z0Vj8Q%a=f!P!st*0?vdw{J~S_|uBS z@MVdrCB(rfF5_vRV9UVieOU_Ml`|67q?q5HGio-Pq*rBKjtq;s94iEOZTfZ=ggVCiUBo2M=#g^n(`{~bKt(u zDci5Fy|b2!ao4PQ(=_zdID_vyT+S976#KJ#JQbJ}eG{;9JG`cd7lalzUT;vif342T zD497<=kTL;vvC)>LlTYZ%Fa&SHT8C`Xr^iM!`h_kVbw(!9HT+9u9@tixO0*M(s#Ox3E>O9~e^*U6q}(FpQLm$5_K<(AsP7Dl$EY+* zvPg)(b;b4C({<1MLPF=>y8oy(H_jq$j=uJT?QLhyFP6tKf*IkA+v|(l7a8tP(RQ^6 zZycj#5gxF5rtPN*+njxi=ZKbtKZ}?ZeEiqPb4MS{%bLH+`-+@>A|&$0)U5fMMT7NH zEt*LMMh1yH6h<>&&3APCGPZTbvAW%N=WMT#pOhP%-yr>Hd1CawMH42(<=8LVxG#H8 z_D9RUcb}BiNE_2q5z;lJBg*AEH#eGd44` z9BkeB(X#qpM&{g{3qc=5m08JVX>U@a4|gY+^oFpjsN29hBj?_{HtpID@#Y||N}wrh z9*1#FFThehy-5(NurS>6w#>29MQ%50e`rNA-)Q-j8|U9k%velj6i2-$Bp!Zxw`1YP zSNWfM4q2ApT9_b_WV9DfShvT&^K*?fDX_M7yrLvjK2|pW{b;>&*B7qW67(36pfmh) z{_bfGuhTc)4VhJz*19)r!?-EyH+-6MFYQBp zLVh;5G@`O#gyYPH2w8KG*3-rD~5#G{6%2^=RL zWISJ3mhxs|NfnzZ$Bp<9UWvt=+19Wi^85E&ERo>K`lm^zp&y-fD#?|J24uFKUToM- zwyroKINw;f+pFY`OVsyDt+naByC1K7+f?JuFTWVQ#@}TAwjmM8KDP9{? zoaI(AE>9)GBWMw(y)hW^mk7IIeRvFJX0mx^>}8Db4u3VtNWYt87B`wXcZ{%n77s`-_lkkKfSzRW!dxFPv_PQskakvtDPint=-=kIj7!& zwJ)IH)x_%bYtuF_&aZCox%=F;-7R-!terL>@6ed0aW`jd9{6PDA7P30Zd-1}^hx=V z^sYtM*QIg4+vpt~Iy=rcEBdCk&%Exuq^L1Im3Iyc{P9>+sk=tJkujIb!3vgFt+ZwJ zIKL$6*zZTnA5|LN{8~D0dDzCA1Lo&n8j)1?tLMi>OV0iZF|4k5?6>oKc#X!yHSs=^`##fN$%*e&W zlpCRz*NjgJrGd+$o9~voQj&Il+UMs1fh)w*rhtLt1_n+dZQ$HX%Gh+__SHS39DAIt ztvj4KVZ!>ayJx@R#vflUw)$rNpy2U}lHBI5#JC*i*h*0kvUc_3=NE=t8GIw8&%r7^ z+Q13hcTKc%U5n$2MXMC&uZgZ1MyK!Ke&e^AuPI%fY(@ymY+0-o-WM9YW3)HQXNael zdE2HLY@WI10M{gPfH4>{V=F+KUne~NBibtNVBcwv_BST*yjg9gFVl|7A%ja!YWYrYsBjTgU70Xda>~|7&d4-nyW?!GmJzXMyJG&BYRiL%H-GBv zJMx8M-nj0(qK!{`K7P7;nBDKfy>Z<=d|SK<$FBNu@<7hC$?5hdY#%IXp2~5t%Gq}+ zBsWj6dHp_{S?3R5-1czR#IcdaipXipb_KtR&e%92cI%c+!=2I8^<({-y$)O%V?^8K zm;BC~WH(#g88}~e#U}mAHw#yfi>qskc;ei#S7G}gTalL*`L)!?{^0xtx3>)aSm$l#v;r1m+==h~5(kd+?{?F*-q7&XrJ(y; zFoDMS-U7z=Jlgoa&6HNg&Fm@LA+22hV|ZGjaTay#;*`o+`Cf53`T6e%HxNxv#_Wj~ z4l2HR^tpcF>By@+E-Y@W{ZrBU(26!Y0Tk`BlF;|>y)%z>1@4%zWjY^gwK%y_mV0_p zo8{&0qc@D}lDBLrZMiUKW3LkTk)!l(rJkK-9&|5ZW9?A_0)A-@TaC-3Yzv$ z>szI1QzO5RX%Je+SYE^5pXe`G#c&7+%(y6;VOgLv!zkTWZGQhRt?zCc^P6_2Q;b^- z0hg=KJV{(&+=8>^8L%RX{<0##pDf0Be@SfzZAEx4?JW+Bzk=N>bvM+k=(rb@#I9e>Y_6-X^=0;@-ohQv0nR&rVq+ z-|%qcF?XtR?4xr>kIs{f(i_B26PuoYWW(R_ueSFjm)Fwn3gZTL)Zwa9siZ% zSnM~^vDIj2=JfI2iTet7J(oQ$v-=`@_wvKao||TluIe%JQ{39O9;?nxeS0}C-#Lo4 zMpD1eL}a*TW>oR8$#Wm>DzN+fOeLZ>5gmp01e|%UmKib$+~f+w9Vk zp1hidkIBh9JC|HPsC@Wj@SFY4LoyGn*b(S${N((@np3qDRoNM`@?2<40^U@rz$#66{s!n&#DSw8NKE zitcyZ{5q5obx9+e9r`d5$}c~kmzr#HB7461jp1!i?q`m4c+%%aMCo*IL$4<$%@ca< z@SXF`agFzv{*u$9TUY4dyg!eYijw&e-3?}(w49^GJ~P)eZIO1=5AWOc=UV5la+_A3 zIm(hrD2OR9Pz*yJ%WZP7o z2i=o5j@crQKlX6Uqs=RhUJq)CypwABJ-!h`E+maHh?=b~Z}9GtD=wMN z7=Ph+{idl%Qu(98rP!f$?jYgHk`eP3q!et*kE>`(vl=+?ai2Q}_pcs2@6wLT7f!r= zop<6~v#m0*$B8MXZS7;4HowQWczHe8UDyAl!OyjahO+bOLC21??uPj6tZoNW|%*YK;|+~-!UN^P+{2Zy+xC7twD#q`n+In1U$@|s(V5!XZErme^}fA$ z;^e4 z3`?!*s076Z5it5GoZZD+xp`~WqYon=h)QB2Y1uqmm^adFMc2FI`?hsi=lUK4!XX0=;?_a zFRu-|HS^i+g6D5;Utibt{13&M)*IuW6{Gs!?H4BYT2Y!XrEUrlC46L5vE)>u*!=uT zYvCh3Bfcp^p(kd0<7~bTml^kH{j?cR`;oJ&Mn7$SHE3wTx?ffIem&c8yQBI1^FdVy zXDSZ<-tzS8*+pB5Hoa`WbM5Qwa|aF_KC^9%(X;pupPjBF3xP$~4v&luw~o63AGWr; zB{mRy&#haqz}-nJcBQG_BXB0csV=)fZ={ip%i%XC7ms;9UeOj+*?0G$z2m|b$nN}l z^mML+Rm7USH}5X+SJlt0%}I<*JfG!Y+H3#yp|@{8n>f_HSKj_pt-c*Sg@)_O<~>ZC zwk2g>-@Pt#DxUYRXc`w^;eN~KXLFlvY4rKsvjzMiCAL~M)z~RE^o)Bs>IjmI z%&U{m1(ln()LxzaKIw#FTFxiS=svD-*WUJLCmmQecfs0u6COU)Vrw-u8pk*_eF_v# zmJ6r84I7*r2mPKrVC-kS zXkzb_(^J=Ez1>tJ|6|7J6w|Ov9vHngyczx`{;MXuQ&blnbS%8v_|;=V!zFUfFY7^% z&+Y#3W$5SuiCB5k+k;2-?H&duzj$Ym+hB5KYU{F-%jcRD58U*82O9e2cjDqzpIYV| z?eDuvJEh}?+2=wd-#0UK!aG)Ov9CYtf6lkqHhAt>>0DFC)lPfTJ)h#TKp{f zb=9JRu{GL1hwtk>;*9RtR{t+DhuK4t3kN5>7C)1{b(sCJM1HQ9>0>#U$!8d`@L7z> zIC<(w>SD!w#b9Arb(_iW*QjjyavzJ9!E`mMbq zM|)WfTA6z{{PlrFw>K!g{H?+`AH=6S^?XDdSPQNVN$alN)~<}}pR#}TCC~H|Pr>I= zdHQ*hSGu{NqjHe(tcPR#+^y@we@rpGHZ<#UWb>ejoZkkKYZPPCPZm7Jzuxo?W>%LB|@);{u_?ZfNSFfyp zD&}%#e&U6$noF@hEpwO6JsvcBXGi_|`1<|^|KtUC>L1X)lWC{Y^DNp6{xwYiv-m$_uWxRpMK1@A>AW6`x!9ZOb2mdqb0h><*vp z_G5U_1pnJ6*QaC#+I{9cS2 zY*FDGX8eF>RTsBqy}l*5)vf4{9g3K@sdqas5sCZ8FJ4?=*+>RsV?Zl6oLt=)u_FqCA%!pw8I56ECrN2vA@=o``O8=)hCv)c> zn6bC><>|NI+s`hldiz1<5I5xVkiiiz{~^P?nSW!fyr%DR^stRrtj{c+H!$1j`1ZUl zSNq*aGAh%m`1IVZ!>Q|@vOUo=!2YLI$4575Zg}UrdlwcyJ3M3SVJ~gN9gjW+wcJ}- z_#{tco0=XO724VL^nyp~^~IxKSdQDaFQ@x~ry++%jCG&kAzypRcJ}Fz98mD1YF7=g zx^ZAd?#peG=%223itr_iM%2A>S+llB>an}V`hx-#i+Xg=nPL9Kv0h$qZ`{j4lQ%hg z-2HZH=x(-&OUj%1<&k{BlLv#7v*+$oSu3V{0^K81OXStN>1&)bi6e*ZKGDCfZyuA; z-~N5{Hk<#$*?Yi6(Q|#nJG(pE3jzWnW$B=xfS@3v6sZDIEQq2~r56hdmUU4PM3ic< zD~N&!_6`dm2b(`UPIy?I^g2r#KmF3jOM2E<{cjAq zl&|jOp&n`5{7A7iaNQu$N#hESW$A0{tXmA*eSL+T)&cWNEVM%}Z9dmmGa&bLB>Ty! z@~K=)XvM+Mil^aLoWW9~3*ba!f(UNhJ8{t7nIEQYG|Y)nWY%dkthC7~z4|G|D%^ux zn(L~$f@;MM>i<-qU;3P9+Ec_RKxS3nNO-s@l!pKt?S8!1nsfIrd|%d3s0yWI^A(< zq;!Td5&y#D^@P}*TloV|{e0At^E5d1nZgq1A{)O?TMXYCj+~dY+td5#9BZAXMNR%3 zasQxx<8_*gu5P{Byx(25VoY;sjP)CC?YRkY*C%ZB8tZADu;apRi=dkqpX_T~XXN3< zQ9U|frD0o7Dee5W_Q6EMH7_=VzmpzVWO?ORQxPd%O+&zPya>|c7kI{ilT zJLU~e{BXiupWRWtbkg2sJiGt($7Kv&uZz!WU3iY#%gJ7^aYx{?pFMR z)&bjB|5{P@kkLIR8dyf(eEs@do0{#F=+L<_G4~(4q|_f-fL<=Iscg8J+5XeYx?~tP zyHx$Ox_a}_2HPyV?9yQx7e4l4+b&Nh7_>7o2fc-xXflf=l~!#-=^E!=YaiMLrDYu! zeBy^~-#si~gKcJ=W0Uvi$ikz=%Z)oOZynqEx#o*ceO7Ki!wat`#@Bw1cYP9m{MGAG z_t>_}Qj#YI*jJkR$#+%^Uk8;&9Lc;`dB2#y@=96ck^ixttQ?Z}2^=`fKs5){>oXLSZp%=Won)NGI1saGBVReFP4{R ziDOs%iEe_X6wGYR&+0|Huv$-vw@T+=;>q5?Vd2h|Mr}_hP}q-me7_OL^rOnOmi?XDD*9HFlSv+)0fXO zKs*orFLcp~Nn8M*Swig%Vq3$tiQU#ohTm2zV{QHApX=}f&zaMRwJLR3WkJl;l#-oR-E3_;7ZywqFJF#WbwIk1JMyXzz zdtEc&AL|Dn%DD9<#f$(A{>O!xNn`Z+b%ImIYIjiC zU!l-6WA~=Z=B9ap3U%*&)nY}w&zn|MWdx3j8oNQ6q_tX1y1Ob_CfrqXXprFnmRw=o z)qr&zJoXEZN}JcHTz?pvU9@0wb@dAF`ElVcyLPX7yfM4f_=q9iOBqZHpf(1pVPnuW zwlwm}j9f=*WANYsmyXZbrO8t_VSTmL`#_EI_e)h^W3WNN`^Y|hUTWH>AS?B+1IE8A zt3Pai<*;`}+Tx0;8Cx#r75seO>oYHSbleg9qtX!@FWV?rFCctM&DU^kg{3*ad==~+ zJo1gl(?vX=C5DMdTop4|VTrwm=UqTIX1w??IG8)JO119$4wD<{e9(oAg^j@}Z$Mqm z5=mGJ9B9AvF|aY%$M4Yhy}btWPi?6imNO&V!SZ}aYW3x-4!ngLh3x3Ut_D2#zt~L4 zNK52;alA6LGA5zl9ZWc#w^Unh?_@!A@M&kJPLP-<-XZjkEZ7K#`BvmbbGGNikJ++3`vH+TD&K;gq8#7IDklk7*x#mH2B~ z=*#5agDXwUUyt;eo@BGk04t{FI@qXWuGv-36R<=yo&r|uu>dlHamKv2UoVSne#(nm ziNhz}et5s7eeEmj9mTKLZ~buL!TL2b_tz`@uFaY8XidbT*k2u@qJXsB6Z6vzty>lC! zCxn{3>%C!eL&4js^NAh9GDb*~E9%)M8@5+0HQGP?fzhnS zb22Y;9eYujUyQYJ(;|bd4>yNve7SS2;hI(H{(iyzvu)WlyDI~0-)@;m_Lb_G4jiz& z_KR%KxK06GNqiB|w%#ABgQw6igRb4Q#qVsUR}uMXaqB|H>XwC4=wkJjf-o;nP)ou8 z6sd5@bL}Jd6;jV!Yx`EOUU7R|>ByWX^K$ZDe_b&tdS=kfmybs#D6J`b*T>_>?UI=> z&Ex0g(97e`WG%Zc2=0Ll#m{DN&S-2>Bz*Xp5pzp zjZ6GeCXO!HUokCbN!pXmI$m5&f6u*EA3P^c3hOoE@bx(jN&Pm?=-2Ds{EER|+v7fNat4K{OkKE$uvqx1gNvSUFm0grX}+m8)WNx5o&Wo~L1XL;GU z3kJF8wVwEYx;#XDt@$6jA>!6H*D}XM??D-w3Dk5(A!7gs-NkwnIIy_Od~hf2G?zB` zw)lxrlaZALFr4eVA=+x$;}4gM1J$33Z9!l1YO7`M)>l=&r9q~Cg&GDP z&eGN2ukIZ)#B1ou1fOr$_wG>Zdtb@FWyKKI8EWQjK23Sb8oWXIjX&~nWBQ`DL}3=C z4p|M1M{WsI~}e^fI&Zzcwa%fd2-|{0Vzb*lleo zH(ti4yTG!>jmCPDnXn*pn6&iw=hjQQVYgjhIajXp2`jo^qjznkceZee_pHjmueH;P z-tBd{efiFY_bw#!{P3Iw53_H$s~z28cTF<>PR+8FYxRuwF5B`@mlHebhWO_@pSf?F zSMFJx?tOLDL%YJvRpw=noBSL0{I=;(Q8gh9|G>TN|AZPm$7STYKdNd$?-+LY)&+X*4y3^1>ID`{L@R z#H~__Y&@C&=!cG*Y~{CNXZw`Qf0U!nY&knQG|uE1(;~lYN4?^Ve|(EAiXEA15=r_+ z-(lI}&pQr%nxL4rSn`3#ioJIt;A->w!#A!On8nR>n?A+XLsI@q<9^;jrHG3bit8(u zPF}jyUcM2J7pF{yI!*ADdz$6MPzRfP9z&2}kb-+b#xSg2zhD5pfc-5;@%v9L_B#CT!RNT<+ol+2NlrTp5G)aty7dW1Fc6LnW7snRo;E3Us3o5JMuKc~U z>Fvh5b8me*a`0~OgG+l_`~T9@dNyIfQ}2MOYB9dklN^@mq!_QTh?6Cmi>*U4dt=o) zXx6Qya@G3skbWg47TK>-E833KZfqTxcj?8)<3`h3CxpAsdm1-v{`HH;?~e_(s&7(g zALaJO>)FiTqt1MJXFshaKK}LiZ{vn{28UBEZ#{l&^(y)Pe`JV)`1=bQEZ+ec35 zNHXwnoO9W7dD@o6>f$1m)3EPanff}jm{2`%MDhCGoJ!?c8YVGqL1i!2EB^D&um8=+ z?UP$-Z{IdXMO!|qt2JK^lxXCn7nAnt&ML}kTeTF0L24Sy{VPGKF0gp&lYd8P@-my` zxR&q=&!y6${YS^8(^<5D_&M~Nh&jnO{UYxUJTOFak9wDyrc-V1o3)*x=VaY$-i~3O z&JBi2cn#2h;p*&!MUuCR!RG!8VFfFIAv8^GP3?J!^BSw~t~nTTcVBFYuIjjtz0aIo z8aL&dpEz{`V^Pt)xrfT>+&W8P%QE)k@wj^?ODr;oHM^_vd*N;Zd?9)-u-h4 z-`AYWikq`yX_cWx^zo_#53)8si@7u{{^ctor{NUQ>q@zF;Yh7Ueb=(mt3wUm`TM^u zA8;?g^4pK{MgBoCnlAnFEcXx3%X5m=+9|kI>1Vf?5y$7+yWNWt(QDT~NaI2NTxRn@Uw9d$pfj>^Vr!$+P0<McM-lwTark+)-)2ZfJeoekH zZZ)6Hr^ij+H^v|=;!X9E6^}Z$T(B~Gs-)UFU}NOgs~lOTaigOdA>b(NTF0Kr{9X1e zOYKVFWaWaRs|sS54@tj2w^BGk2aIo78@h~dpMqLwtkhA*A!`iVuH^bM?pR)R%$EE8 zF1j>2SDqDnS==c;^2m5uHP;$?lPzs~np2uR&50Z;Hu(-eNm+TL8U*+Kn9+sbK-^?YBH-F)9M~nGDa|4^XUzm&+%CE=}Icoc`gpLlM4-> z-mdIpkaO?NHHLH6tb5U%#iFp^y~*UL#!z>6#idUK56$;c1_%GG;cbTIOx&Xu$7T3) zkT(NH67tVTl1sDZ(lS(C{+dr+?J_u+l2>%UQ_1N7%RM{VvYBrtL=RCat)Dy7%&>5o zkwvP`_L(0$^_qK~MF#Ixwow|Pr5dGEPtX=7OD=OH#tu^j_a6NUDY0F)RKwZl{#NCo zw`0FZ*M_}H3~D&;ulLODO3?n|m?FEOufAoR&mM1aVc^^Qfh|STM1D5jYXbwsEWD|| z@08BAe3x48tqxn@?H#HJ2-1Uo4Bx$oQCK zwWCiN0yXR6(;f6H`}REKP=+rGcmlk5+A2Juyo8 z_TodH!4K2(cHANt%)Ym&-!i(_C|E*_qFoOu9$Z8GB#!mfy)ij zZmM~F7wL^xU$46UhfS}&%OYOZR1*n&P-n&*?f7)K&{tea}jFz2mIZ>#UHXl|Jbq6;Xy$TR_=!pl0cs|b^x~eR5 z{EtWK+27CYJmZ`ZaJ#8ur1|0@4h^$kN|@4DqCPh(i!vTmD4ni~yl32CeBF3*!_a)E zLlMEHQ}e=df@8R=;#ZBP>)i+}Rxa9?u{+Ep#Vqw&R>6>ZgQuc@7+*a_Hv>m4v%ax@ zC@dl#?SVza_*U`XiwLU;gFmcj+J45wBx(1cetuO}i(bF=)i^k0lvhl6_{pMi&i9U* zq?nc$KY3VRtREsO6z4`iIm}U&IHaHV*RtpvWtRMNkiMxdbMpAl2bXPqq`QAZzle}g zvyZJTW^$VQp|25-Yj)VWmJE7&`R+geG!417>EMw*8EP{+*AMy}guiqJL-THR=q|i| z$XM;6;}Cy$#E@$f7oDjMjmR;77C)-esI1V)WAjDtoc>{I83debVMiN5s~XempK9HH zuC+O}tBO|ln(@}@Rt-1rWlD*&@46998gO=z?cx&w8lR$UE{@QcE=YGFXr~+`Ts4W! zR8ybi2MQn7jXV}OKkWHSTdCRWDd%Q|xW9N5ap7r%;P#_S>eF{Vo3$=c?bDXjGc_~Z z>z?YIJ?!tN>$;_4Rm{L417m}-=z^tSylF6d$&7D(()Qbwj=!til(@c)H*iah#Y_Lu zZ?X!4w);*riqX3CamDQXrP0IG^E01?{K}oUDgk$mpn_*4HR_lJRa+pK) z0QnN4Pi?2!3_u8NAz=*KKylA&fmrSSf;gv}T9z+&(oGV!z6_1-fecIYH z+VgU}v-UOpTIpE5??XSjY5u1wC(R_l{hs-i^>#u1z0x~=gHs#~O#*g_pUMop`q&Ru zsK4#HZg6(2=_8{->e_u;t~w@WJ-m^yF!!}k$M?DPpOiN&V(ByI$17j~p$~0bDLlu% zS<&WIU>(zS$8IwV&k2bWa-Ea+7LGuHZJulDYdqAV-}>f8n)R=UFl#cn`TcF#X{!rC zM@FoF4C{xPuztAo{O8G5^}fCwV`Vr}!PzGPLGW59zDP*yeg@y2k4ACr4B-03xXzDs*atz_eRU{qEt(_U;P8>)^!YV z+GsUPBQYqp?8BKu&=EcuE}4T^!m}N{Gb3%DC~j5UzjfNoudY=WF05S7Pks9M-PS3E zmZJj0Dyl9WsF}6@>*9-#C*1pI#YgiTw?&FqhIuKSJ=PJLowclB@}$*ktv-*dEdID3 zy)cy4j|*z5$on+tieY1ApqWkWCi9hN_U=11^OHvY!c8`b8XF>&=~ROkP;2H%T_S%6 z7Wp?oFEw!xya|-hz{O(RMviS zci-@UHqYNHraq1;zMU=IHT&ws)NNt2_rw>83RdJa#qWqZ)N;k{l}W+1-8b!%2lF`U z)#H9WjC-GFbLb!KJ-2QiiF7fEPE`1seBz1D{td@&zFw`ohhEm;x_tcLdCoU~EUg`J zu<=FUvbdm((E01ws~lQCu`4qAKRRG}?u^rh!FxZh{Abqi)>*oD4)>K9&YdNtd#m2# zSeEZUW18jpa`ySyTPnA>jGgQgB44$RE@< zYCtkO$~|DDWli<-#NOVgU1IFVHrB0rJ5Sq!t5806z?<`DQsKjlkv%@lm^CQFBn>zC zjN!0opg#B42D9L9neR=^7tmIP{@+&18aMU`PSp|kW4LNVgY zsUm%H^X3S(FYa7xt$t?9Y#Zuq#}4MJx{WUIzu~KM+*@JD&MBjtYrd)Ix~#uiF>qG@ ztUdXYE3YRsjcfoTv?x=B6|RmK(zPx+!g|@wUSEBmxs6)cxJ7h(jVC+C9b2kaG39ho zb&QC+a(h}vLB1H}u=eDC`Y;@;X!XsdUf8e@<4z*|I@ zXZeyT{I=`jg5Q%SMMjPrZcQ1PjcUqRXFmBDH+B7k#s%@UhQVu>TN=Nav2K_4%|+fm z$LhXls2Rt%tw^#@GuGJtSvuPEV8F+I&hF7e2F$g$7rskI^AeR4?b+dPgniD>b7c(p zb=08YOx&Pj8|ohpoD=+E@Jq)QyA8FQTYc5n1v;4;MB1EGEUeZyF{9D4O|x6b#N#=N zIzB6Bd@`vGJ^H}(Yvk8OH3|neO={2)4Y=X7_~P1y2EwL`+pOx;j)xDcE0t<#ItV0i zS}n=kzHi&4+Z>azHgmm|3U}?DE_uN4FAlz%&+!#`P1ndzS8Vv`2cIgZUr1sf!Dg6- z)Ms`0GgZ#pi?^@Gx<_5_|7jU9+tPH!Y^{mU;lsMjj*DPtmcYs|PNf?E&?V{|_6&N- zfc=cej{7!`NEtE_E&R#s1@^@~xe#;*Dv6*+(F>hkF0Uw3B2zI+i2OJ-9eTN{m~ z)RI{ZzPZclxnh>ezf`4`%)8aH+qUJ*Zo0GjSn9j#m1oa1{AfIQukHEyV_PeaPrKRl zEfE=7oPB=##nHRR&Rkl1bVKy_%nfs2{VtDe_|~}Ud|U0kWjD6&e0g@8+PVBaGhO#t zwH+AxN<7$U?3N#v^xZavi~QC3Pw=`q(^O%Jk)KgfLx)6aH|v4-*^|ef?|8Lj)wc05 z?+X2OpeHQJf4ph^SmoXxFRXO?ITnWIlb`0Cd)8Og^~J#9bH7p-I}*S9x7s;aT?eyDj6F$d$4@;th0!-j4E)kg=k9_<=w; zb^qr8j{)OevxP%GkNA~3OD8h&YiCFB^1&V9y$Z=Gv3Eai=AH}ko*a{1JhUM8EW$xu;Ga_)0SN zd_Cx#yW)1jo!(~IDK257-@Cdjd+_06pJi9C@=Hub)8pzixYPCf6!+iXPv?Be^RfHl zTa3kDlxv5izEHZ`vD8``IVjG{o@HRSA;c(a$)AnE5z2m@u z^4i^Vg~}?Z;wMu9Oq9F3!Vi29fuYuz9xLA-2NCozUK z|I{F|_<;E;^-*D-VPI^p@K&egzK-ssw{Kl;yXQUL@JrM4Qi|K;v#-2IW^ccB=Vz4l z-oqo5EVQ1O-kG&;1(DU{r;-M3wA#a zeXMKuw7mF7^PcOT<@u)@`x;D}sz@0}6sZ}=jU%?1DR}#-q2SX~*mel(`dp;jdJW_}97G^D!89}r)%QGb!>W$ASjM{Y`rD;xjrfx?`QCDm zg9w_zHliV4$6!hb{!Y(Y^Gi-^^3>e?xf|aPQ;e3Z{y5(FVCXB)3~U-<(v}LKq3U0g zDq8HaKgC|e2N7`KAOhFTeGeRZ(d)Bd?3CBi^tl;{o#s8f7>=FfhFYk7Nb!Jr@%u)Yj{lAfB}|83Sp*Zk93Sz zXeTUPgq%f=o2b=n&Y#t+L)U7y1+QjXFSfv3K4oBor*wYMq3{9WukRgsuyO3CxD`K# z1YE7@8055Z-*k<{BNL))`pg!2*hFxK#0O;(I~~kV=^(3BnPbyj#!svF9Q!42hTjlv zY1L(=i@-YRZ>Grc`PedA6OJ-+>h^(O1UiiM{C zY@0V_H#;Wf%Jxmxu%HeE3*p=si<{C#^L--DN2MG)r{4NB{mSP3^FP@0IBV7`|9Uvr zG-y-&*V(U<%W_b!3%0Hey@OVN+OzhiQrSdTrxvYuV{g6gHLP{xb?M8+yUm_dxsOl# zG|+Hd`tnN~3$5&WZ<}$_Uj2Q(>Jq8K#@*{LmBVUwQ|7B{j+=;%G7IUmTYc2`zAV!@ z>$|t)$nCYePk-@qJK}b--#M-9Ee25sY>zR=z22R;;AnGx;gN};r^ z)Nb4@rIeXx_1$ImIp6xljdL|QUjFmsQ?0&992*V!N_HrUR84&TnqQ@VV?bM-@4@`y z*>#6*@ccZi_pXcP&ls2KKV^l^s`7NlkosI53)?GmoL1$S853$W3#BNtx(^NMCsdqW zII=0@KVR>+MqRbyO~utcy9(Z(m_O2+u2kOp@V?SG4b#}9<*=arblFbaz59!ow1kjL z6LR51$=?FOl9}_mWjd9_cTC$@f1FnN>Lk4eJ-`(EoXk8 zBKuy69dnenKGqppvv0#b3)`b5!)I0=+kw>D9zF61)VN-r80Gs%hW{@X$dnAPG}|`K zyU)N=yPi(5R{6@q54Y6Ji*7yUu05b>nRmWdi-us!5Df6R@kXMB&@9#E3_LLhTFPBREIo+J;Wa}^Ty5g=?qjhAC>TBlVk?XS8eq3>S zp6l*GdEY9WK!#^MKKkunpIW=~%$aJ-J54mlf6}48wZ1t)af+hAK=HJbqdtv2^OmDP zQ-y!6l*Pv~TnaM$5T2($`+xh|8d^2}>kC&1Tt0C5!xaG6Xt=!L@`7s=T%K^n!W9SC z7`Vp4H36bk2v@uV{3O5?3RgH>5pYGqH4UyPxT4{TfvYPae3LF*xO)lU5~ExP z!axro+Zg(Q=XM7W-E!%%bXU3#sH$%gdx#uBk8mcmXlKI;q=bh{`}E%b08(Ib@G9m?r;vAbQYZWo2GMP>UZWeQ;n^9t@B zH^5-o`h<&|jFb~+IdPH`M>%njlM!+(4`n831j; z(w~dWRY^TIOFs>Im0Z6?vh-6iR>MyW=rRXwgm7TZ2G$8+k@_hRlQJ6t?kwOcz)m_{ zes0Mmy0tsHH_dBCwp2gKF%$$ROd1Z@`wO!>QbfO#neycbZ{{4C}yU-^eha zAZD1j7hp&i9bz!btz(!_*dqX-TX`PbQ_ev&j1)r*LlT_-(|TY;o`)1l&w|Sd&VK}? z%ok??Bu@@A!o(sNza1SS7OiZ8G^X!^IHQo}MYw&&DY(;eFTjTcKr}f%4&jAQQa3@2 zQ;;A&kC+HU@t5F62RQ1Sutk#_!5s>SDNNiYF&K^x(*?VrN$C(X(7H1a4qrSPcEOI& zaUcNE=r9Li*UlO!n+m!~KpwO3$?O0I!$l9@!BLS5&%v#*No|0EQw%5!6oQLH4ZsDA z69cp#bH!(&((vax%;{8eP}Kkfdmle?I+O$@)iYSg`YeD)$^mGYhBz945$P}|Vi%y; z=m2hIJ}wfRM5V*VEmB#i0WH!5`rc~+HQu3xYUB~5s0PoHe-bLGL={|<`@rk7QYH0K zGG2(KB55CJ4+L%-iAendROqKZj zRw9`8g_Z+D@<2``fRcH6c`+!UWHi)+&?bIhcrpnm67b3X3IV)xIZzqL4S>qLyhQSw zAQ^2o85s$PWzaz9(e)6<-9#SSaT~H{g!_{_TgapGH!+EuU@kU07fLm~Egf0bjOiad5Z&2xg^@b}Q;&xWL8(_ZS>cV&9 z=fc$m*l*OW&^0k8G6ez0b_EDHxE19=J;?73kKke!po8J6x$grH)YeXyup$5)4qi+W zyszCC;CZ|N-HjCi7;%4?+C&6}6~}f1tT>!Kpo9~0Bvz>M2tbX)=_Zc=l%phoY{KYq zkOUlr(U}B>UZz0g*bx9C*B$raB{<(1gUNB-089?jWoW=yOV{d58gT6*09p>Bcky9V z1&}8SXpV>F0l~_pv2sDaWUy58<00v?p#vy|M5IEzC#^6;5l-Iz=N-I4{cm)9#JlS^ z`VtWV_t7-rqR}LBY3cw$qVMz&gf20(RpE%<2Jf34&f8krC#LnTZ$H-S{E_(GM^?p= zI)mXJ?e24x!)&a=!-qtMTMY^s5*`s5Y8^Tx1kUTViVO{nu&@XV4TWR9LoGr>=qMiG zp{TROlbQh!Jp3&jE3A#YAu~AN*tU%h2psr4d1!iJqzZMkGRC2jAL+|PkJ{*=M*pA3 z%wJI>p*$3p6<`+1PabEXE63od-`pQ`Co0G7*!TGdRTV1jkQv+|2gt%TaJZcwiqB8R z;92lo_CM*)$PrGgCwLfI%N}C1{G^8>5hOyg=>#D_L=yuJ9>$0CODKFU78~UEwo7P7 z2VIi^MWWgadfq|TWhjeii2sX@kLah`4V++nkt%-tFZxIn2X1uj7hP96c2kI%{s-y4 zy3f=>#Or4{CMS)4_SFByPUg*dBQK{Ii^e^C-`3W2Vzg23>shZKa-Xl%J^0Yx-}ljZ z?e))>^ZOLuBVPSTV)hcB-mmFD+B|=Ha!OFg9nL0Qhqhi-8&tSetnB^m%Nf)6al+nx zpuKM^T-b0tQSk&jfUe75utl1A*kR1ZOqJg5AoNvQ=3-S#IP*at=9=d6qea@r|(( zAWBcr1Bv&y8nDp?d)=`Y;wH4Y2rglVYZbg*F55EEW-LG9q#T9uUNX zVL$-va0(}w6FHw65LjqeG_bq@6oH7wh`}(d%SU>o{|b9F~&2epocjTGjT*ff}u*}I}#)zh9D** z0fJZ_8J>5E%hpn; zDtAC9hBwfeB?duiZQ=Hrvruu%m&nANCRMtaT=J7 zMdVhdHW%gvu@n~(v2I`8MqsuVQyy0W<%Ka610ZwU!l{(yA^2@T$`OEs3>E(; zwl>V4F(xv{J(NnmGXz7a!^1^}^i15o5hfFBD41l9`_vxMVf}`WjK>u^rRNRznxOVE zaxy1!a!)=aB%u(LIqtzdlA%tZ#}x%o%23UnkK%A1&&bZY1Ez8}lg`?UF5hNq;YS2~ zWvcveAiEAqcZcTDDo0x8L;}+CUqRXw;xZ=^5SRb(g8|c?LSB~Q8S@a@YMS_vI7w_Q zdMGLu`HDEg6T(!XrQnrdiy&Cgn|~32h3t9Xc)NJfJYDWxZXws3%L4rIIUo;SvP;-O zY(-WhE1flz{77ymLrHZ2I90OLYi9wPx zx;XMekSX9VIO#2432+$3;PoPig5S`!M*!?IhhhJR1BonL0_X&cVfdzBUV7-gJ7mik z(&_CCXTw>jkO~(Oc*cb%*#M^UH%G$i`-jmvJ!fhDVd2>_OZ|@@KP-pT!VC`4H8<7f zOcwpkk+8)*7}Nw*@P`49hbN$H{vQ@jmq!ZajIL>1=gg7rf^*|`apjXc7|n3~{8_1R z=sG=Fcs0afIQ3*zEyS#mJz3|W+PDm7dA6O?csvE?DE!631>h+C#e!vkHkau2Z=Qss z+><4*E=75k1qyZ&R6#ZB;XOD_6lX*yr)$y2VHow`$V+3;gM$}h9J-_j2d@b^48tBA zywc+^^tw6JdQb`ousrc0*pXaL{7Zn$5t<903pNP?1q%EIz*4s1z2}wlrt;Ldm${i- z2hMlSE>09j!oJ1MWxKO!tV66MmN85!ib-FR!#u&9!!&0+XKZ2wG8E_y^o4XAm|T<- zA%r^Z8f`gbN&V%yDKMal3baS-QR^;z(hi9$wILbo%5K*;`%|`8bHjf9_9K z>c4mr4r|UImJY&#lfw+`$pY~UtO@^OX>nVbL4SA>YM|wi2|ZZQaPee>!%F*`3E&x! z;CMN+fPi)T4p`1}7z2862>g2Jx;;4Z*E8@B2Nx26ug!r|unRghUR9HH?Jh5V!6d`6 z)7u%@0ec-hK?1;Pon%;bc^yW9Y+cW=?%{{v!E&!YH4u`_Fi51UzgYMJP0|a zZ|A+8lP8ijyOTj7q>#*jF7H<;EXf$u<-J0AExWuEsX<|EmltEHkj!zNUU@0V*uT6? z|G&Hp3+(k9rB6*|NHV(1+s6~0nlV)F)$4pJH}Im9wRC-S90q%m>Cze8J^aLU#m7@u zlC_{K9M*R58cC*mSGa{MRN-uxo4-BPNSjkO97OdVcW@(mxbx^mzQCOy)agG z&OS)y)J_Jd9Z7B08fHijKcqjo%Zq_-NM>-Sw`(pzGADLLz*7#AIjPG#1>^|HFzxcf z97~%W%M61~=LSzUa+LzXH0;H9_

K71jZ6ua)49V5?w?K#hNypUHQCg|Z62Q^0o5 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 + .chunks(pathfinder_storage::BLOCK_RANGE_LEN as usize) + .map(|range| *range.last().unwrap() as u64) + .for_each(|block| { + let block = BlockNumber::new_or_panic(block); + transaction + .insert_transaction_data(block, &[], None) + .unwrap(); + }); + let block: starknet_gateway_types::reply::Block = serde_json::from_str(include_str!("../../../fixtures/mainnet-619596.json")).unwrap(); let transaction_count = block.transactions.len(); diff --git a/crates/rpc/src/v06/method/trace_transaction.rs b/crates/rpc/src/v06/method/trace_transaction.rs index 722c9783a4..cbc9fbdc9f 100644 --- a/crates/rpc/src/v06/method/trace_transaction.rs +++ b/crates/rpc/src/v06/method/trace_transaction.rs @@ -257,7 +257,14 @@ pub async fn trace_transaction_impl( #[cfg(test)] pub mod tests { - use pathfinder_common::{block_hash, transaction_hash, BlockHeader, Chain, SequencerAddress}; + use pathfinder_common::{ + block_hash, + transaction_hash, + BlockHeader, + BlockNumber, + Chain, + SequencerAddress, + }; use pathfinder_crypto::Felt; use super::super::trace_block_transactions::tests::{ @@ -305,6 +312,19 @@ pub mod tests { let context = RpcContext::for_tests_on(Chain::Mainnet); let mut connection = context.storage.connection().unwrap(); let transaction = connection.transaction().unwrap(); + + // Need to avoid skipping blocks for `insert_transaction_data`. + (0..619596) + .collect::>() + .chunks(pathfinder_storage::BLOCK_RANGE_LEN as usize) + .map(|range| *range.last().unwrap() as u64) + .for_each(|block| { + let block = BlockNumber::new_or_panic(block); + transaction + .insert_transaction_data(block, &[], None) + .unwrap(); + }); + let block: starknet_gateway_types::reply::Block = serde_json::from_str(include_str!("../../../fixtures/mainnet-619596.json")).unwrap(); let transaction_count = block.transactions.len(); diff --git a/crates/storage/src/bloom.rs b/crates/storage/src/bloom.rs index b0ce2adcf8..6b238f5852 100644 --- a/crates/storage/src/bloom.rs +++ b/crates/storage/src/bloom.rs @@ -68,7 +68,7 @@ use cached::{Cached, SizedCache}; use pathfinder_common::{BlockNumber, ContractAddress, EventKey}; use pathfinder_crypto::Felt; -use crate::ReorgCounter; +use crate::{EventFilter, ReorgCounter}; // We're using the upper 4 bits of the 32 byte representation of a felt // to store the index of the key in the values set in the Bloom filter. @@ -76,10 +76,12 @@ use crate::ReorgCounter; // filter. pub const EVENT_KEY_FILTER_LIMIT: usize = 16; +pub const BLOCK_RANGE_LEN: u64 = AggregateBloom::BLOCK_RANGE_LEN; + /// An aggregate of all Bloom filters for a given range of blocks. /// Before being added to `AggregateBloom`, each [`BloomFilter`] is /// rotated by 90 degrees (transposed). -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AggregateBloom { /// A [Self::BLOCK_RANGE_LEN] by [BloomFilter::BITVEC_LEN] matrix stored in /// a single array. @@ -109,13 +111,10 @@ impl AggregateBloom { } pub fn from_existing_compressed( - from_block: u64, - to_block: u64, + from_block: BlockNumber, + to_block: BlockNumber, compressed_bitmap: Vec, ) -> Self { - let from_block = BlockNumber::new_or_panic(from_block); - let to_block = BlockNumber::new_or_panic(to_block); - let bitmap = zstd::bulk::decompress( &compressed_bitmap, AggregateBloom::BLOCK_RANGE_BYTES as usize * BloomFilter::BITVEC_LEN as usize, @@ -126,15 +125,10 @@ impl AggregateBloom { } fn from_parts(from_block: BlockNumber, to_block: BlockNumber, bitmap: Vec) -> Self { - assert_eq!( - from_block + Self::BLOCK_RANGE_LEN - 1, - to_block, - "Block range mismatch" - ); + assert_eq!(from_block + Self::BLOCK_RANGE_LEN - 1, to_block); assert_eq!( bitmap.len() as u64, - Self::BLOCK_RANGE_BYTES * BloomFilter::BITVEC_LEN, - "Bitmap length mismatch" + Self::BLOCK_RANGE_BYTES * BloomFilter::BITVEC_LEN ); Self { @@ -148,24 +142,21 @@ impl AggregateBloom { zstd::bulk::compress(&self.bitmap, 10).expect("Compressing aggregate Bloom filter") } - /// Rotate the bloom filter by 90 degrees and add it to the aggregate. + /// Rotate the [`BloomFilter`] by 90 degrees (transpose) and add it to the + /// aggregate. It is up to the user to keep track of when the aggregate + /// filter's block range has been exhausted and respond accordingly. pub fn add_bloom(&mut self, bloom: &BloomFilter, block_number: BlockNumber) { assert!( (self.from_block..=self.to_block).contains(&block_number), - "Invalid block number", - ); - assert_eq!( - bloom.0.number_of_hash_functions(), - BloomFilter::K_NUM, - "Hash function count mismatch" + "Block number {} is not in the range {}..={}", + block_number, + self.from_block, + self.to_block ); + assert_eq!(bloom.0.number_of_hash_functions(), BloomFilter::K_NUM); let bloom = bloom.0.bit_vec().to_bytes(); - assert_eq!( - bloom.len() as u64, - BloomFilter::BITVEC_BYTES, - "Bit vector length mismatch" - ); + assert_eq!(bloom.len() as u64, BloomFilter::BITVEC_BYTES); let relative_block_number = block_number.get() - self.from_block.get(); let byte_idx = (relative_block_number / 8) as usize; @@ -183,7 +174,9 @@ impl AggregateBloom { } } - pub fn blocks_for_filter(&self, filter: &crate::EventFilter) -> BTreeSet { + /// Returns a set of [block numbers](BlockNumber) for which the given keys + /// are present in the aggregate. + pub fn blocks_for_filter(&self, filter: &EventFilter) -> BTreeSet { // Empty filters are considered present in all blocks. if filter.contract_address.is_none() && (filter.keys.iter().flatten().count() == 0) { return (self.from_block.get()..=self.to_block.get()) @@ -449,7 +442,7 @@ mod tests { } #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] + #[cfg(feature = "aggregate_bloom")] fn add_bloom_and_check_single_block_found() { let from_block = BlockNumber::new_or_panic(0); let mut aggregate_bloom_filter = AggregateBloom::new(from_block); @@ -460,43 +453,14 @@ mod tests { aggregate_bloom_filter.add_bloom(&bloom, from_block); - let filter = crate::EventFilter { - keys: vec![vec![EventKey(KEY)]], - contract_address: None, - ..Default::default() - }; - let block_matches = aggregate_bloom_filter.blocks_for_keys(&[KEY]); assert_eq!(block_matches, vec![from_block]); - let block_matches: Vec<_> = aggregate_bloom_filter - .blocks_for_filter(&filter) - .into_iter() - .collect(); - assert_eq!(block_matches, vec![from_block]); - } - - #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] - fn aggregate_bloom_past_first_range() { - let from_block = BlockNumber::new_or_panic(AggregateBloom::BLOCK_RANGE_LEN); - let mut aggregate_bloom_filter = AggregateBloom::new(from_block); - - let mut bloom = BloomFilter::new(); - bloom.set(&KEY); - bloom.set(&KEY1); - - let filter = crate::EventFilter { + let filter = EventFilter { keys: vec![vec![EventKey(KEY)]], contract_address: None, ..Default::default() }; - - aggregate_bloom_filter.add_bloom(&bloom, from_block); - - let block_matches = aggregate_bloom_filter.blocks_for_keys(&[KEY]); - assert_eq!(block_matches, vec![from_block]); - let block_matches: Vec<_> = aggregate_bloom_filter .blocks_for_filter(&filter) .into_iter() @@ -505,7 +469,7 @@ mod tests { } #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] + #[cfg(feature = "aggregate_bloom")] fn add_blooms_and_check_multiple_blocks_found() { let from_block = BlockNumber::new_or_panic(0); let mut aggregate_bloom_filter = AggregateBloom::new(from_block); @@ -516,15 +480,14 @@ mod tests { aggregate_bloom_filter.add_bloom(&bloom, from_block); aggregate_bloom_filter.add_bloom(&bloom, from_block + 1); - let filter = crate::EventFilter { + let block_matches = aggregate_bloom_filter.blocks_for_keys(&[KEY]); + assert_eq!(block_matches, vec![from_block, from_block + 1]); + + let filter = EventFilter { keys: vec![vec![EventKey(KEY)]], contract_address: None, ..Default::default() }; - - let block_matches = aggregate_bloom_filter.blocks_for_keys(&[KEY]); - assert_eq!(block_matches, vec![from_block, from_block + 1]); - let block_matches: Vec<_> = aggregate_bloom_filter .blocks_for_filter(&filter) .into_iter() @@ -533,7 +496,7 @@ mod tests { } #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] + #[cfg(feature = "aggregate_bloom")] fn key_not_in_filter_returns_empty_vec() { let from_block = BlockNumber::new_or_panic(0); let mut aggregate_bloom_filter = AggregateBloom::new(from_block); @@ -546,12 +509,11 @@ mod tests { aggregate_bloom_filter.add_bloom(&bloom, from_block + 1); let block_matches_empty = aggregate_bloom_filter.blocks_for_keys(&[KEY_NOT_IN_FILTER]); - assert_eq!(block_matches_empty, Vec::::new()); } #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] + #[cfg(feature = "aggregate_bloom")] fn serialize_aggregate_roundtrip() { let from_block = BlockNumber::new_or_panic(0); let mut aggregate_bloom_filter = AggregateBloom::new(from_block); @@ -564,15 +526,14 @@ mod tests { let compressed_bitmap = aggregate_bloom_filter.compress_bitmap(); let mut decompressed = AggregateBloom::from_existing_compressed( - aggregate_bloom_filter.from_block.get(), - aggregate_bloom_filter.to_block.get(), + aggregate_bloom_filter.from_block, + aggregate_bloom_filter.to_block, compressed_bitmap, ); decompressed.add_bloom(&bloom, from_block + 2); let block_matches = decompressed.blocks_for_keys(&[KEY]); let block_matches_empty = decompressed.blocks_for_keys(&[KEY_NOT_IN_FILTER]); - assert_eq!( block_matches, vec![from_block, from_block + 1, from_block + 2] @@ -581,7 +542,7 @@ mod tests { } #[test] - #[cfg_attr(not(feature = "aggregate_bloom"), ignore)] + #[cfg(feature = "aggregate_bloom")] #[should_panic] fn invalid_insert_pos() { let from_block = BlockNumber::new_or_panic(0); diff --git a/crates/storage/src/connection.rs b/crates/storage/src/connection.rs index 60159f265e..55defedb46 100644 --- a/crates/storage/src/connection.rs +++ b/crates/storage/src/connection.rs @@ -1,4 +1,6 @@ use std::sync::Arc; +#[cfg(feature = "aggregate_bloom")] +use std::sync::Mutex; mod block; mod class; @@ -27,11 +29,16 @@ pub(crate) use reorg_counter::ReorgCounter; pub use rusqlite::TransactionBehavior; pub use trie::{Node, NodeRef, RootIndexUpdate, StoredNode, TrieUpdate}; +#[cfg(feature = "aggregate_bloom")] +use crate::bloom::AggregateBloom; + type PooledConnection = r2d2::PooledConnection; pub struct Connection { connection: PooledConnection, bloom_filter_cache: Arc, + #[cfg(feature = "aggregate_bloom")] + running_aggregate: Arc>, trie_prune_mode: TriePruneMode, } @@ -39,11 +46,14 @@ impl Connection { pub(crate) fn new( connection: PooledConnection, bloom_filter_cache: Arc, + #[cfg(feature = "aggregate_bloom")] running_aggregate: Arc>, trie_prune_mode: TriePruneMode, ) -> Self { Self { connection, bloom_filter_cache, + #[cfg(feature = "aggregate_bloom")] + running_aggregate, trie_prune_mode, } } @@ -53,6 +63,8 @@ impl Connection { Ok(Transaction { transaction: tx, bloom_filter_cache: self.bloom_filter_cache.clone(), + #[cfg(feature = "aggregate_bloom")] + running_aggregate: self.running_aggregate.clone(), trie_prune_mode: self.trie_prune_mode, }) } @@ -65,6 +77,8 @@ impl Connection { Ok(Transaction { transaction: tx, bloom_filter_cache: self.bloom_filter_cache.clone(), + #[cfg(feature = "aggregate_bloom")] + running_aggregate: self.running_aggregate.clone(), trie_prune_mode: self.trie_prune_mode, }) } @@ -73,6 +87,8 @@ impl Connection { pub struct Transaction<'inner> { transaction: rusqlite::Transaction<'inner>, bloom_filter_cache: Arc, + #[cfg(feature = "aggregate_bloom")] + running_aggregate: Arc>, trie_prune_mode: TriePruneMode, } diff --git a/crates/storage/src/connection/event.rs b/crates/storage/src/connection/event.rs index e6fe68711a..f28a84e813 100644 --- a/crates/storage/src/connection/event.rs +++ b/crates/storage/src/connection/event.rs @@ -69,22 +69,32 @@ pub struct PageOfEvents { impl Transaction<'_> { #[cfg(feature = "aggregate_bloom")] - pub(super) fn upsert_block_events_aggregate( + /// Upsert the [aggregate event bloom filter](AggregateBloom) for the given + /// block number. This function operates under the assumption that + /// blocks are _never_ skipped so even if there are no events for a + /// block, this function should still be called with an empty iterator. + /// When testing it is fine to skip blocks, as long as the block at the end + /// of the current range is not skipped. + pub(super) fn upsert_block_events_aggregate<'a>( &self, block_number: BlockNumber, - events: &[Event], + events: impl Iterator, ) -> anyhow::Result<()> { - #[rustfmt::skip] let mut stmt = self.inner().prepare_cached( - "INSERT INTO starknet_event_filters_aggregate (from_block, to_block, bloom) \ - VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET bloom=excluded.bloom", + r" + INSERT INTO starknet_events_filters_aggregate + (from_block, to_block, bitmap) + VALUES (?, ?, ?) + ON CONFLICT DO UPDATE SET bitmap=excluded.bitmap + ", )?; - let mut running_aggregate = match self.load_aggregate_bloom(block_number)? { - // Loading existing block range - Some(aggregate) => aggregate, - // New block range reached - None => AggregateBloom::new(block_number), + let mut running_aggregate = match self.running_aggregate.lock() { + Ok(guard) => guard, + Err(poisoned) => { + tracing::error!("Poisoned lock in upsert_block_events_aggregate"); + poisoned.into_inner() + } }; let mut bloom = BloomFilter::new(); @@ -94,12 +104,17 @@ impl Transaction<'_> { } running_aggregate.add_bloom(&bloom, block_number); - - stmt.execute(params![ - &running_aggregate.from_block, - &running_aggregate.to_block, - &running_aggregate.compress_bitmap() - ])?; + // This check is the reason that blocks cannot be skipped, if they were we would + // risk missing the last block of the current aggregate's range. + if block_number == running_aggregate.to_block { + stmt.execute(params![ + &running_aggregate.from_block, + &running_aggregate.to_block, + &running_aggregate.compress_bitmap() + ])?; + + *running_aggregate = AggregateBloom::new(running_aggregate.to_block + 1); + } Ok(()) } @@ -331,8 +346,7 @@ impl Transaction<'_> { } // TODO: - // This function is temporarily here to compare the performance of the new - // aggregate bloom filter. + // Add a limit to how many aggregate_bloom ranges can be loaded #[cfg(feature = "aggregate_bloom")] pub fn events_from_aggregate( &self, @@ -550,56 +564,30 @@ impl Transaction<'_> { }) } - #[cfg(feature = "aggregate_bloom")] - fn load_aggregate_bloom( - &self, - block_number: BlockNumber, - ) -> anyhow::Result> { - #[rustfmt::skip] - let mut select_stmt = self.inner().prepare_cached( - "SELECT from_block, to_block, bloom FROM starknet_event_filters_aggregate \ - WHERE from_block <= ? AND to_block >= ?", - )?; - - let aggregate = select_stmt - .query_row(params![&block_number, &block_number], |row| { - let from_block: u64 = row.get(0)?; - let to_block: u64 = row.get(1)?; - let compressed_bitmap: Vec = row.get(2)?; - - Ok((from_block, to_block, compressed_bitmap)) - }) - .optional() - .context("Querying running bloom aggregate")? - .map(|(from_block, to_block, compressed_bitmap)| { - AggregateBloom::from_existing_compressed(from_block, to_block, compressed_bitmap) - }); - - Ok(aggregate) - } - #[cfg(feature = "aggregate_bloom")] fn load_aggregate_bloom_range( &self, start_block: BlockNumber, end_block: BlockNumber, ) -> anyhow::Result> { - #[rustfmt::skip] let mut stmt = self.inner().prepare_cached( - "SELECT from_block, to_block, bloom FROM starknet_event_filters_aggregate \ - WHERE from_block <= :end_block AND to_block >= :start_block \ - ORDER BY from_block", + r" + SELECT from_block, to_block, bitmap + FROM starknet_events_filters_aggregate + WHERE from_block <= :end_block AND to_block >= :start_block + ORDER BY from_block + ", )?; - let aggregates = stmt + let mut aggregates = stmt .query_map( named_params![ ":end_block": &end_block, ":start_block": &start_block ], |row| { - let from_block: u64 = row.get(0)?; - let to_block: u64 = row.get(1)?; + let from_block = row.get_block_number(0)?; + let to_block = row.get_block_number(1)?; let compressed_bitmap: Vec = row.get(2)?; Ok(AggregateBloom::from_existing_compressed( @@ -612,6 +600,21 @@ impl Transaction<'_> { .context("Querying bloom filter range")? .collect::, _>>()?; + // There are no aggregates in the database yet or the loaded aggregates + // don't cover the requested range. + let should_include_running = aggregates.last().map_or(true, |a| end_block > a.to_block); + + if should_include_running { + let running_aggregate = match self.running_aggregate.lock() { + Ok(guard) => guard, + Err(poisoned) => { + tracing::error!("Poisoned lock in load_aggregate_bloom_range"); + poisoned.into_inner() + } + }; + aggregates.push(running_aggregate.clone()); + } + Ok(aggregates) } } @@ -1522,6 +1525,52 @@ mod tests { } } + #[test] + #[cfg(feature = "aggregate_bloom")] + fn crossing_aggregate_filter_range_stores_and_updates_running() { + let blocks: Vec = [ + // First aggregate filter start. + BlockNumber::GENESIS, + BlockNumber::GENESIS + 1, + BlockNumber::GENESIS + 2, + BlockNumber::GENESIS + 3, + // End. + BlockNumber::GENESIS + AggregateBloom::BLOCK_RANGE_LEN - 1, + // Second aggregate filter start. + BlockNumber::GENESIS + AggregateBloom::BLOCK_RANGE_LEN, + BlockNumber::GENESIS + AggregateBloom::BLOCK_RANGE_LEN + 1, + BlockNumber::GENESIS + AggregateBloom::BLOCK_RANGE_LEN + 2, + BlockNumber::GENESIS + AggregateBloom::BLOCK_RANGE_LEN + 3, + // End. + BlockNumber::GENESIS + 2 * AggregateBloom::BLOCK_RANGE_LEN - 1, + // Third aggregate filter start. + BlockNumber::GENESIS + 2 * AggregateBloom::BLOCK_RANGE_LEN, + BlockNumber::GENESIS + 2 * AggregateBloom::BLOCK_RANGE_LEN + 1, + ] + .iter() + .map(|&n| n.get() as usize) + .collect(); + + let (storage, _) = test_utils::setup_custom_test_storage(&blocks, 2); + let mut connection = storage.connection().unwrap(); + let tx = connection.transaction().unwrap(); + + let inserted_aggregate_filter_count = tx + .inner() + .prepare("SELECT COUNT(*) FROM starknet_events_filters_aggregate") + .unwrap() + .query_row([], |row| row.get::<_, u64>(0)) + .unwrap(); + assert_eq!(inserted_aggregate_filter_count, 2); + + let running_aggregate = tx.running_aggregate.lock().unwrap(); + // Running aggregate starts from next block range. + assert_eq!( + running_aggregate.from_block, + 2 * AggregateBloom::BLOCK_RANGE_LEN + ); + } + #[test] fn bloom_filter_load_limit() { let (storage, test_data) = test_utils::setup_test_storage(); diff --git a/crates/storage/src/connection/transaction.rs b/crates/storage/src/connection/transaction.rs index 91894e86dc..018f830f18 100644 --- a/crates/storage/src/connection/transaction.rs +++ b/crates/storage/src/connection/transaction.rs @@ -100,6 +100,10 @@ impl Transaction<'_> { events: Option<&[Vec]>, ) -> anyhow::Result<()> { if transactions.is_empty() && events.map_or(true, |x| x.is_empty()) { + // Advance the running event bloom filter even if there's nothing to add since + // it requires that no blocks are skipped. + #[cfg(feature = "aggregate_bloom")] + self.upsert_block_events_aggregate(block_number, std::iter::empty())?; return Ok(()); } @@ -166,13 +170,14 @@ impl Transaction<'_> { ]) .context("Inserting transaction data")?; + #[cfg(feature = "aggregate_bloom")] + { + let events = events.unwrap_or_default().iter().flatten(); + self.upsert_block_events_aggregate(block_number, events) + .context("Inserting events into Bloom filter aggregate")?; + } + if let Some(events) = events { - #[cfg(feature = "aggregate_bloom")] - { - let events: Vec = events.iter().flatten().cloned().collect(); - self.upsert_block_events_aggregate(block_number, &events) - .context("Inserting events into Bloom filter aggregate")?; - } let events = events.iter().flatten(); self.upsert_block_events(block_number, events) .context("Inserting events into Bloom filter")?; @@ -218,8 +223,8 @@ impl Transaction<'_> { #[cfg(feature = "aggregate_bloom")] { - let events: Vec = events.iter().flatten().cloned().collect(); - self.upsert_block_events_aggregate(block_number, &events) + let events = events.iter().flatten(); + self.upsert_block_events_aggregate(block_number, events) .context("Inserting events into Bloom filter aggregate")?; } self.upsert_block_events(block_number, events.iter().flatten()) diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 0dde97aca1..47bd04ac77 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -6,6 +6,7 @@ mod prelude; mod bloom; +pub use bloom::BLOCK_RANGE_LEN; mod connection; pub mod fake; mod params; @@ -15,8 +16,12 @@ pub mod test_utils; use std::num::NonZeroU32; use std::path::{Path, PathBuf}; use std::sync::Arc; +#[cfg(feature = "aggregate_bloom")] +use std::sync::Mutex; use anyhow::Context; +#[cfg(feature = "aggregate_bloom")] +use bloom::AggregateBloom; pub use bloom::EVENT_KEY_FILTER_LIMIT; pub use connection::*; use pathfinder_common::{BlockHash, BlockNumber}; @@ -90,6 +95,8 @@ struct Inner { database_path: Arc, pool: Pool, bloom_filter_cache: Arc, + #[cfg(feature = "aggregate_bloom")] + running_aggregate: Arc>, trie_prune_mode: TriePruneMode, } @@ -97,6 +104,8 @@ pub struct StorageManager { database_path: PathBuf, journal_mode: JournalMode, bloom_filter_cache: Arc, + #[cfg(feature = "aggregate_bloom")] + running_aggregate: Arc>, trie_prune_mode: TriePruneMode, } @@ -128,6 +137,8 @@ impl StorageManager { database_path: Arc::new(self.database_path.clone()), pool, bloom_filter_cache: self.bloom_filter_cache.clone(), + #[cfg(feature = "aggregate_bloom")] + running_aggregate: self.running_aggregate.clone(), trie_prune_mode: self.trie_prune_mode, })) } @@ -260,6 +271,10 @@ impl StorageBuilder { tracing::info!("Merkle trie pruning disabled"); } + #[cfg(feature = "aggregate_bloom")] + let running_aggregate = reconstruct_running_aggregate(&mut connection) + .context("Reconstructing running aggregate bloom filter")?; + connection .close() .map_err(|(_connection, error)| error) @@ -269,6 +284,8 @@ impl StorageBuilder { database_path: self.database_path, journal_mode: self.journal_mode, bloom_filter_cache: Arc::new(bloom::Cache::with_size(self.bloom_filter_cache_size)), + #[cfg(feature = "aggregate_bloom")] + running_aggregate: Arc::new(Mutex::new(running_aggregate)), trie_prune_mode, }) } @@ -340,6 +357,8 @@ impl Storage { Ok(Connection::new( conn, self.0.bloom_filter_cache.clone(), + #[cfg(feature = "aggregate_bloom")] + self.0.running_aggregate.clone(), self.0.trie_prune_mode, )) } @@ -495,6 +514,100 @@ fn schema_version(connection: &rusqlite::Connection) -> anyhow::Result { Ok(version) } +/// Reconstruct the [aggregate](bloom::AggregateBloom) for the range of blocks +/// between the last stored to_block in the aggregate Bloom filter table and the +/// last overall block in the database. This is needed because the aggregate +/// Bloom filter for each [block range](bloom::AggregateBloom::BLOCK_RANGE_LEN) +/// is stored once the range is complete, before that it is kept in memory and +/// can be lost upon shutdown. +#[cfg(feature = "aggregate_bloom")] +fn reconstruct_running_aggregate( + connection: &mut rusqlite::Connection, +) -> anyhow::Result { + use bloom::BloomFilter; + use params::{named_params, RowExt}; + use pathfinder_common::event::Event; + use transaction; + + let tx = connection + .transaction() + .context("Creating database transaction")?; + let mut select_last_to_block_stmt = tx.prepare( + r" + SELECT to_block + FROM starknet_events_filters_aggregate + ORDER BY from_block DESC LIMIT 1 + ", + )?; + let mut events_to_reconstruct_stmt = tx.prepare( + r" + SELECT events + FROM transactions + WHERE block_number >= :first_running_aggregate_block + ", + )?; + + let last_to_block = select_last_to_block_stmt + .query_row([], |row| row.get::<_, u64>(0)) + .optional() + .context("Querying last stored aggregate to_block")?; + + let first_running_aggregate_block = match last_to_block { + Some(last_to_block) => BlockNumber::new_or_panic(last_to_block + 1), + // Aggregate Bloom filter table is empty -> reconstruct running aggregate + // from the genesis block. + None => BlockNumber::GENESIS, + }; + + let events_to_reconstruct: Vec>>> = events_to_reconstruct_stmt + .query_and_then( + named_params![":first_running_aggregate_block": &first_running_aggregate_block], + |row| { + let events: Option = row + .get_optional_blob(0)? + .map(|events_blob| -> anyhow::Result<_> { + let events = transaction::compression::decompress_events(events_blob) + .context("Decompressing events")?; + let events: transaction::dto::EventsForBlock = + bincode::serde::decode_from_slice(&events, bincode::config::standard()) + .context("Deserializing events")? + .0; + + Ok(events) + }) + .transpose()?; + + Ok(events.map(|events| { + events + .events() + .into_iter() + .map(|e| e.into_iter().map(Into::into).collect()) + .collect() + })) + }, + ) + .context("Querying events to reconstruct")? + .collect::>()?; + + let mut running_aggregate = AggregateBloom::new(first_running_aggregate_block); + + for (block, events_for_block) in events_to_reconstruct.iter().enumerate() { + if let Some(events) = events_for_block { + let block_number = first_running_aggregate_block + block as u64; + + let mut bloom = BloomFilter::new(); + for event in events.iter().flatten() { + bloom.set_keys(&event.keys); + bloom.set_address(&event.from_address); + } + + running_aggregate.add_bloom(&bloom, block_number); + } + } + + Ok(running_aggregate) +} + #[cfg(test)] mod tests { use super::*; @@ -611,4 +724,132 @@ mod tests { "Cannot enable Merkle trie pruning on a database that was not created with it enabled." ); } + + #[test] + #[cfg(feature = "aggregate_bloom")] + fn running_aggregate_reconstructed_after_shutdown() { + use std::num::NonZeroUsize; + use std::sync::LazyLock; + + use test_utils::*; + + static MAX_BLOCKS_TO_SCAN: LazyLock = + LazyLock::new(|| NonZeroUsize::new(10).unwrap()); + static MAX_BLOOM_FILTERS_TO_LOAD: LazyLock = + LazyLock::new(|| NonZeroUsize::new(1000).unwrap()); + + let blocks = [0, 1, 2, 3, 4, 5]; + let transactions_per_block = 2; + let headers = create_blocks(&blocks); + let transactions_and_receipts = + create_transactions_and_receipts(blocks.len() * transactions_per_block); + let emitted_events = extract_events(&headers, &transactions_and_receipts); + let insert_block_data = |tx: &Transaction<'_>, idx: usize| { + let header = &headers[idx]; + + tx.insert_block_header(header).unwrap(); + tx.insert_transaction_data( + header.number, + &transactions_and_receipts + [idx * transactions_per_block..(idx + 1) * transactions_per_block] + .iter() + .cloned() + .map(|(tx, receipt, ..)| (tx, receipt)) + .collect::>(), + Some( + &transactions_and_receipts + [idx * transactions_per_block..(idx + 1) * transactions_per_block] + .iter() + .cloned() + .map(|(_, _, events)| events) + .collect::>(), + ), + ) + .unwrap(); + }; + + // First run starts here... + let db = crate::StorageBuilder::in_memory().unwrap(); + let db_path = Arc::clone(&db.0.database_path).to_path_buf(); + + // Keep this around so that the in-memory database doesn't get dropped. + let mut rsqlite_conn = rusqlite::Connection::open(&db_path).unwrap(); + + let mut conn = db.connection().unwrap(); + let tx = conn.transaction().unwrap(); + + // ...we add two blocks. + for i in 0..2 { + insert_block_data(&tx, i); + } + + let filter = EventFilter { + keys: vec![ + vec![], + // Key present in all events as the 2nd key. + vec![pathfinder_common::macro_prelude::event_key!("0xdeadbeef")], + ], + page_size: emitted_events.len(), + ..Default::default() + }; + + let events_from_aggregate_before = tx + .events_from_aggregate(&filter, *MAX_BLOCKS_TO_SCAN) + .unwrap() + .events; + let events_before = tx + .events(&filter, *MAX_BLOCKS_TO_SCAN, *MAX_BLOOM_FILTERS_TO_LOAD) + .unwrap() + .events; + + assert_eq!(events_before, events_from_aggregate_before); + + // Pretend like we shut down by dropping these. + tx.commit().unwrap(); + drop(conn); + drop(db); + + // Second run starts here (same database)... + let db = crate::StorageBuilder::file(db_path) + .journal_mode(JournalMode::Rollback) + .migrate() + .unwrap() + .create_pool(NonZeroU32::new(5).unwrap()) + .unwrap(); + + let mut conn = db.connection().unwrap(); + let tx = conn.transaction().unwrap(); + + // ...we add the rest of the blocks. + for i in 2..headers.len() { + insert_block_data(&tx, i); + } + + let events_from_aggregate_after = tx + .events_from_aggregate(&filter, *MAX_BLOCKS_TO_SCAN) + .unwrap() + .events; + let events_after = tx + .events(&filter, *MAX_BLOCKS_TO_SCAN, *MAX_BLOOM_FILTERS_TO_LOAD) + .unwrap() + .events; + + assert_eq!(events_after, events_from_aggregate_after); + + let inserted_aggregate_filter_count = rsqlite_conn + .transaction() + .unwrap() + .prepare("SELECT COUNT(*) FROM starknet_events_filters_aggregate") + .unwrap() + .query_row([], |row| row.get::<_, u64>(0)) + .unwrap(); + + // We are using only the running aggregate. + assert!(inserted_aggregate_filter_count == 0); + assert!(events_from_aggregate_after.len() > events_from_aggregate_before.len()); + // Events added in the first run are present in the running aggregate. + for e in events_from_aggregate_before { + assert!(events_from_aggregate_after.contains(&e)); + } + } } diff --git a/crates/storage/src/schema/revision_0066.rs b/crates/storage/src/schema/revision_0066.rs index 52159524a0..99440ca0ab 100644 --- a/crates/storage/src/schema/revision_0066.rs +++ b/crates/storage/src/schema/revision_0066.rs @@ -1,77 +1,83 @@ use anyhow::Context; use pathfinder_common::BlockNumber; -use rusqlite::params; use crate::bloom::{AggregateBloom, BloomFilter}; +use crate::params::params; #[allow(dead_code)] pub(crate) fn migrate(tx: &rusqlite::Transaction<'_>) -> anyhow::Result<()> { - tracing::warn!("Creating starknet_event_filters table with aggregate bloom filters"); - - let mut select_old_filters_query = - tx.prepare("SELECT bloom FROM starknet_events_filters ORDER BY block_number")?; - - let mut bloom_filters_bytes = select_old_filters_query - .query_map(params![], |row| { - let bytes = row.get::<_, Vec>(0)?; - - Ok(bytes) - }) - .context("Selecting old filters")?; - - let mut bloom_filters = vec![]; - loop { - let Some(bloom) = bloom_filters_bytes.next().transpose()? else { - break; - }; - - bloom_filters.push(BloomFilter::from_compressed_bytes(&bloom)); - } + tracing::info!("Creating starknet_events_filters_aggregate table and migrating filters"); tx.execute( - "CREATE TABLE starknet_event_filters_aggregate ( - from_block INTEGER NOT NULL, - to_block INTEGER NOT NULL, - bloom BLOB, + r" + CREATE TABLE starknet_events_filters_aggregate ( + from_block INTEGER NOT NULL, + to_block INTEGER NOT NULL, + bitmap BLOB NOT NULL, UNIQUE(from_block, to_block) - )", + ) + ", params![], ) - .context("Creating starknet_event_filters_aggregate table")?; + .context("Creating starknet_events_filters_aggregate table")?; - bloom_filters - .chunks(AggregateBloom::BLOCK_RANGE_LEN as usize) - .enumerate() - .try_for_each(|(i, bloom_filter_chunk)| -> anyhow::Result<()> { - let from_block = i as u64 * AggregateBloom::BLOCK_RANGE_LEN; - let to_block = from_block + AggregateBloom::BLOCK_RANGE_LEN - 1; - let from_block = BlockNumber::new_or_panic(from_block); - let to_block = BlockNumber::new_or_panic(to_block); + migrate_individual_filters(tx)?; - let mut aggregate = AggregateBloom::new(from_block); + // TODO: + // Delete old filters table - for (j, bloom_filter) in bloom_filter_chunk.iter().enumerate() { - let block_number = from_block + j as u64; + Ok(()) +} - aggregate.add_bloom(bloom_filter, block_number); - } +/// Migrate individual bloom filters to the new aggregate table. We only need to +/// migrate all of the [BLOCK_RANGE_LEN](AggregateBloom::BLOCK_RANGE_LEN) sized +/// chunks. The remainder will be reconstructed by the +/// [StorageManager](crate::StorageManager) as the running aggregate. +fn migrate_individual_filters(tx: &rusqlite::Transaction<'_>) -> anyhow::Result<()> { + let mut select_old_bloom_stmt = + tx.prepare("SELECT bloom FROM starknet_events_filters ORDER BY block_number")?; + let bloom_filters: Vec = select_old_bloom_stmt + .query_and_then(params![], |row| { + let bytes: Vec = row.get(0)?; + Ok(BloomFilter::from_compressed_bytes(&bytes)) + }) + .context("Querying old Bloom filters")? + .collect::>()?; + + if bloom_filters.is_empty() { + // There are no bloom filters to migrate. + return Ok(()); + } - tx.execute( - "INSERT INTO starknet_event_filters_aggregate (from_block, to_block, bloom) - VALUES (?, ?, ?)", - params![ - &from_block.get(), - &to_block.get(), - &aggregate.compress_bitmap() - ], - ) - .context("Inserting aggregate bloom filter")?; + let mut insert_aggregate_stmt = tx.prepare( + r" + INSERT INTO starknet_events_filters_aggregate + (from_block, to_block, bitmap) + VALUES (?, ?, ?) + ", + )?; + let mut aggregate = AggregateBloom::new(BlockNumber::GENESIS); + bloom_filters + .iter() + .enumerate() + .try_for_each(|(i, bloom_filter)| -> anyhow::Result<()> { + let block_number = BlockNumber::new_or_panic(i as u64); + + aggregate.add_bloom(bloom_filter, block_number); + if block_number == aggregate.to_block { + insert_aggregate_stmt + .execute(params![ + &aggregate.from_block, + &aggregate.to_block, + &aggregate.compress_bitmap() + ]) + .context("Inserting aggregate bloom filter")?; + + aggregate = AggregateBloom::new(block_number + 1); + } Ok(()) })?; - // TODO: - // Delete old filters table - Ok(()) } diff --git a/crates/storage/src/test_utils.rs b/crates/storage/src/test_utils.rs index 9036342af4..4cd3e1860e 100644 --- a/crates/storage/src/test_utils.rs +++ b/crates/storage/src/test_utils.rs @@ -24,18 +24,19 @@ pub const EVENTS_PER_BLOCK: usize = INVOKE_TRANSACTIONS_PER_BLOCK + DECLARE_TRAN pub const NUM_TRANSACTIONS: usize = NUM_BLOCKS * TRANSACTIONS_PER_BLOCK; pub const NUM_EVENTS: usize = NUM_BLOCKS * EVENTS_PER_BLOCK; -/// Creates a set of consecutive [BlockHeader]s starting from L2 genesis, -/// with arbitrary other values. -pub(crate) fn create_blocks() -> [BlockHeader; NUM_BLOCKS] { - (0..NUM_BLOCKS) - .map(|i| { +/// Creates a custom set of [BlockHeader]s with arbitrary values. +pub(crate) fn create_blocks(block_numbers: &[usize]) -> Vec { + block_numbers + .iter() + .enumerate() + .map(|(i, &block_number)| { let storage_commitment = StorageCommitment(Felt::from_hex_str(&"b".repeat(i + 3)).unwrap()); let class_commitment = ClassCommitment(Felt::from_hex_str(&"c".repeat(i + 3)).unwrap()); let index_as_felt = Felt::from_be_slice(&[i as u8]).unwrap(); BlockHeader::builder() - .number(BlockNumber::GENESIS + i as u64) + .number(BlockNumber::GENESIS + block_number as u64) .timestamp(BlockTimestamp::new_or_panic(i as u64 + 500)) .class_commitment(class_commitment) .storage_commitment(storage_commitment) @@ -47,14 +48,13 @@ pub(crate) fn create_blocks() -> [BlockHeader; NUM_BLOCKS] { .finalize_with_hash(BlockHash(Felt::from_hex_str(&"a".repeat(i + 3)).unwrap())) }) .collect::>() - .try_into() - .unwrap() } -/// Creates a set of test transactions and receipts. +/// Creates a custom test set of N transactions and receipts. pub(crate) fn create_transactions_and_receipts( -) -> [(Transaction, Receipt, Vec); NUM_TRANSACTIONS] { - let transactions = (0..NUM_TRANSACTIONS).map(|i| match i % TRANSACTIONS_PER_BLOCK { + n: usize, +) -> Vec<(Transaction, Receipt, Vec)> { + let transactions = (0..n).map(|i| match i % TRANSACTIONS_PER_BLOCK { x if x < INVOKE_TRANSACTIONS_PER_BLOCK => Transaction { hash: TransactionHash(Felt::from_hex_str(&"4".repeat(i + 3)).unwrap()), variant: TransactionVariant::InvokeV0(InvokeTransactionV0 { @@ -149,7 +149,7 @@ pub(crate) fn create_transactions_and_receipts( (tx, receipt, events) }); - tx_receipt.collect::>().try_into().unwrap() + tx_receipt.collect::>() } /// Creates a set of emitted events from given blocks and transactions. @@ -187,28 +187,42 @@ pub struct TestData { pub events: Vec, } -// Creates a storage instance in memory with a set of expected emitted event +/// Creates an in-memory storage instance that contains a set of consecutive +/// [BlockHeader]s starting from L2 genesis, with arbitrary other values and a +/// set of expected emitted events. pub fn setup_test_storage() -> (crate::Storage, TestData) { + let block_numbers: Vec = (0..NUM_BLOCKS).collect(); + setup_custom_test_storage(&block_numbers, TRANSACTIONS_PER_BLOCK) +} + +// Creates an in-memory storage instance with custom block numbers (not +// necessarily consecutive) and a custom number of transactions per block, with +// a set of expected emitted events. +pub fn setup_custom_test_storage( + block_numbers: &[usize], + transactions_per_block: usize, +) -> (crate::Storage, TestData) { let storage = crate::StorageBuilder::in_memory().unwrap(); let mut connection = storage.connection().unwrap(); let tx = connection.transaction().unwrap(); - let headers = create_blocks(); - let transactions_and_receipts = create_transactions_and_receipts(); + let headers = create_blocks(block_numbers); + let transactions_and_receipts = + create_transactions_and_receipts(block_numbers.len() * transactions_per_block); for (i, header) in headers.iter().enumerate() { tx.insert_block_header(header).unwrap(); tx.insert_transaction_data( header.number, &transactions_and_receipts - [i * TRANSACTIONS_PER_BLOCK..(i + 1) * TRANSACTIONS_PER_BLOCK] + [i * transactions_per_block..(i + 1) * transactions_per_block] .iter() .cloned() .map(|(tx, receipt, ..)| (tx, receipt)) .collect::>(), Some( &transactions_and_receipts - [i * TRANSACTIONS_PER_BLOCK..(i + 1) * TRANSACTIONS_PER_BLOCK] + [i * transactions_per_block..(i + 1) * transactions_per_block] .iter() .cloned() .map(|(_, _, events)| events)