From 3721245e1d8acfd48f93a5a4ad2dadb149bb032d Mon Sep 17 00:00:00 2001 From: James <91348155+FrogAi@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:00:00 -0700 Subject: [PATCH] Speed Limit Controller Co-Authored-By: eFini <16603033+efinilan@users.noreply.github.com> Co-Authored-By: Jacob Pfeifer --- .../assets/other_images/dashboard_icon.png | Bin 0 -> 14680 bytes frogpilot/assets/other_images/mapbox_icon.png | Bin 0 -> 33259 bytes .../assets/other_images/next_maps_icon.png | Bin 0 -> 16061 bytes .../assets/other_images/offline_maps_icon.png | Bin 0 -> 17244 bytes frogpilot/controls/frogpilot_planner.py | 12 +- frogpilot/controls/lib/frogpilot_events.py | 3 + frogpilot/controls/lib/frogpilot_vcruise.py | 22 ++ .../controls/lib/speed_limit_controller.py | 348 ++++++++++++++++++ .../qt/onroad/frogpilot_annotated_camera.cc | 226 +++++++++++- .../ui/qt/onroad/frogpilot_annotated_camera.h | 23 ++ opendbc_repo/opendbc/car/toyota/carstate.py | 15 + selfdrive/selfdrived/events.py | 8 + selfdrive/ui/qt/onroad/hud.cc | 6 + 13 files changed, 661 insertions(+), 2 deletions(-) create mode 100644 frogpilot/assets/other_images/dashboard_icon.png create mode 100644 frogpilot/assets/other_images/mapbox_icon.png create mode 100644 frogpilot/assets/other_images/next_maps_icon.png create mode 100644 frogpilot/assets/other_images/offline_maps_icon.png create mode 100644 frogpilot/controls/lib/speed_limit_controller.py diff --git a/frogpilot/assets/other_images/dashboard_icon.png b/frogpilot/assets/other_images/dashboard_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..34ab75cbae40c174f4888ff99c508c87a1598495 GIT binary patch literal 14680 zcmeHO5n$xCeKKAPMenK^6!aG>_ju z@#=keA7-XzZq-cPzTG|D=bjU(rXq)pL5hKZfPk$aFRk%<9sTbBqQ0K1b!&28FNp3M za*_x&Q{+bo2w(&SX$dWFBX|yarhRDBG1N zJ=>C!PigS|4KwEQ)XcVDk|xnf=rlDMg{=uJj82wsQZZf5@Bj6j@AHe@agJy5jGJcM z`HOM7A9fxZ=H4x*-|ZQ+A_0&9QIcR{69O;@tVWIm6-W9E0Hy?qgA;-%5&yfz0RW3b z#1p_rTSU2bct8M>ZDK6W_y;to`2Rop-?K#8arthX@Y$a5S?g|R;L|QuO=IJ1Q&UqL zV-)^P>iE8aj!wG0v$I#0Q+qj?pxYjbj)CjYSR^G*tcDaAByabN&d+757drj##L9eSbKLwKU?c%xtp!Xgfcr+WV~G0xHRbTVc|s7Dv}>IB6J75VFYs}2MT zl0k!#U!vzgL#Sj9TN{6e`P(P?RwtBD#O|{&3m@)!{=r}Fe+CLcrUGBCs8kJTFqAAv zqc{KZe?DCT8!|7|YA7Q^=YR>1r;V!DbDFC68{%eoQgU(;>7XvZyU%{-T`gGxu7zZL z4jBx<6_=qBjGjL+TAT{l4`D7uY$!5^XdFo2& zM;@eua^Zbl?HpkL-E1hTNsA8TnQr*mHSb}rbZvFxmQNK-6j}0dtQmBdK3LD33S+M^ zk|3$=y8X(KLG`qS_M*w0Mc07MzxGS?&me(mt>&pGf*wZxpV2zgfkI+qe=cV^VBh9t zT1%^@s^~n0_ZGDKusGbCT3C9J)~{qq%X0O?=P<{s-8t}a_3WrLZ%P>99ok%r996)( zQy&yR${=mTIvXLM%e_xmhd%>b=19sFXk}wuHt(mrIw=sX(+$<+19mdgjeN&MFGp$5 z`p~F6a7C{ZT6pUCKh_$-twRGOjX`T$eId_1hFCMA;T%YD5j>3{#RL> z9{lSbnz-h<*5TM>%{$>_PD{o8B`p?L;$T7t;o%mb6nWs@XyTdQ=40-vO;N0`JVCk4 z5*Ym4b+>U+(|*kCb+_)-@#-BtMh)`Ex-<=+BOwu5EH}%gs#N^`e_igNxcEdizrXyIA~wx2I3u8a=szFZXr-EN|ZZd5Ul}o;;RBAXOBQ>WjxRLZ?1~km72+QpJO(HF0KleWr zUTokH|JbQ${Io;dRSY9Q3ZJD%+x=S5C+MLDm&t zv?m)6<=|}*)${1jF4ezQzrWiNSE8RhfffM+WE)#fZTm$3Sd;qPZvS2kYHZwf-`zJa zk%|zj%HbWB)}|YOS$@7Qeebzw;2z76`!~#DN6eL2fkS~$OZ=1okND2m|Ibhm0V)iU z1O*5fqqYbZ|MGH`M{ur-sa~p_b@1giGx-6el(*j|5TC_X-q2Fr!x&I&-v7J*c{b_% z#eF#?360XbH{iDv)#Y>0T;TnvMm=fy?iLi1u04_oDBq@YXl~ zy?Ald|JA?!(O@}_WK@M{fgl|t{&06rRc+8V^DhdU(|U@$7RQyzEVj>Y$uz>pFGR_x z%&~cSm~6j{7fcRgK&AkM>8E#`wV$v%)4JTiR~N}1!fINrPbkj4Bhu*&mgGowxkq`O zMI7Zxi=#X-z&_rnWOB_vd47rzxP-nFc`jmyJY#^|%WYdHL*q%R`gKlSDK z9Aw@Dq(In5u78Gz{nA+m=?LvMI(^O>^Pa1mmEMB`4Gg0O(hu*Jt*JO{RfOU1?q0*| z{W+qPhM~`Xs>P+Rxj{g|#`dKQ-QH$(%f>OB7zkjx{lQYZ2d5Z*oUj(#4NPZdJc8QY zP4N8Oixg{xB18B7xK~y;|2WJk!oV|0&MAQL;kjv1x>``?zOzlzam1h&>w!)*y#4LI z8_PdMHawJp3yYqP*~2&GMuQ>56A`;&YjJ)FyzAJ6r?KfDJgx`at*`H=8=tMpKR=AA ze%WDf|6Tah`lLd>-!RU81+Q`pEvCib=-6Af&U3V-Uzv#}4wfX}d5}jI=m~bdeCQp^ z^WKRR+4&=!3o-aE$?fxfjw3J-Zg1?~Xf^5P>^qxxW8Te0R`sKEz z=cWQ54wl3ljOE|>y-U+B-70;y`M7cCD`gZwFmOMc_ma}01W~8|kg-*#?9tTVRk@FD z(E)G1LKP&IhMv5cs`kJGIjpt1Zft-WOzoeJb$Tx0Ti1Vp%`z%y1$@(dD&h)rI_3Vx z_dj&jZWfL6bXrE+l90D5jh`zR`lpNJIxyK=^CQSs6#2S$5qi5=X!Cp~bA%TwBMqV& z6O>T7#y(9~g!?HGj9zRH6dmRTisjuR^2<0T+M)EvyyY2|D}c+0gq(iDQx}vb9$}Z2 zFUd`}p#4l!{7n=pf5X7>RXK!dt`xRk)i`wtQ1N?+5O9&bDAyu}g?PICA;Z7ej-YZ9 z^tWw8(H}fotj*J@Zk-anGl*c2mmgp#C;KV(Sk>bU#{tCx?+(T8+L|=vf8Z&V+`T>F z@41gv4NXyoVuT@6vr`mXp^ru5p@-hQI^4wbt4~gl9S<5LKopIky7J!!&k977F6#zx z9tY{t4cuHy>}?Ki*_6P1ig77_&%AoF7AGY*g98U4K>T3X2)TPq$7_c4{}F*@-T5Jc z>WlaCk}NNp!2NV}7+rsef`Qu@o!A0xR@MAz zVj2WwCn(Q`h6r;rPg!k6@D=Hk&_>yLf%ITNXqusV^yQZ$?_Z`FSQj3W;1bMF^&^y7 z42lN|z2`96IC;xi@i&|Rw{B^2J>k%Jxa)?`;lAN;FL0Ogj>A1z?KPLKaC^^kV>SU< z)7b%>?gt-QZWiXYWcNYCGUA0Pp8wRdY>_Qav92Z4-RxpGj0Gg zdm(W#kn~_sF81Zs*!$qHxq$I;8R71Mfcp0?wfHVddc3;mgwQJKW?Z4)`A_B=iE1}H zw($LjgJll?ZA={gI#`HDMhLFh($BmJgtaA%n4L*lt0LZiJH$N~ap;ECk`Oi-ux6=> zsOP~%rgbFQfZ!1Cy>x0HXV^fn8U(|vlZfZDjP8B{5B!-xRb<-wN>M|8pVm^e{JJh& zSYj$!RcyHyn+&z3jvYvK(7V#+0@#`-sQSAnHUl`|^8b60R;K8{gw$tG2MO^@3WVq| zIn9=9mL7CzlfXYt+Pq5)3MIcaF&qG)oPF$6ZctCc&+F6#3uqvr7u&QI116vXku)d9 zFPG_E7N+bXA+tNbi{hu^*D*4C*rdSn@h7!CFRTez#Pk5_`N+-DK$?6sHy2WLPpalO zd`vVSS5=ar??9IwI{-swX8dO{T+$PQdKGSed11dyg6wx>kUFQ&f;@ixlg_RRNI_MFj}mT zeCB=8hbHH0mVZN3Z&GO;JK?V=Q($x@tv}z#$efd#^ssWNs@Aj;r;!X z2x9uB4g58j@OZht_RP4aWP(~ss%8f((#yKs52qXjy7~NjrEmoI%EK^;op8Z}1#Afx z((aoJcB|alu3sKk6R4$(y8jH5VYX*;P7TEhevu10OrApUISFTg&jp&hhOEFN#R=5uerki$WSM-Y9(Oel{^MFZ-0z{xn<_*rxb)Rl5b+WnvBtGs`mxCqQ*lEPZ# zGb*H}@pXLJXgf7alJTOq8MtR$atAoj#Rq)X;H@P}c}ykFm|2>`-+XTLi*P^0tTrNj9o5Qk)f>1Qk*!>f;0Ko?7 z!{3_SC+KIRlH^=hc;i9r^15ogh<^-tL!)b+B z;Uky)6jj6k1Mx>_;JUKUK!&uU;ht${Akl12luS7V^YKpgs_S8sqbDjk#f zQE{T^I(5a-mDjEok@J~A;_$ahjh_3V+tpkEGtz7OQG56nb94Kv7N(twi%T}zO-7N5 zT|poOy@GDHXt>{HNi}D-L~tM`!Ol!gLpiyoZRNAZzKetS*4&wH77^S2>%6NlMk;+X zPglQ}wgDoq1aXPTXX$K8Q+CKCVJSNRT6Bn<@)}qk+}C^DxK{ zncVVNf*62UvUT01Mi$?8+=zF(!FomNL=)b)n2Sh*I0xC74eYMe^^%ba(f?*CwfYxL2XxP=>l<4A;3b^?SKiG;{NA0UouY zlWj{S4EF;iN%|Qg@;T@i+jvezWiUqf(K|65!QEnPGS^sIf#eZ_%UEZB+Q1L7C{_m5 z4H^oA1EroLmlO(NcM34!Jg3TP)di`n%BHQf)#?PWUq3pfrE0_-9>Np=&-9)V^`y-DY-la1XO#OOJvJvc{J zUu=_DH1UA8WGe!wRz=g9@Zn*+yh7R^{BR~P4e$gWyrUj=k-6J7E&4@nf9I_})l|T; zsQ;mw)gWsYOh4gviKe+YjqmSrCT&TR&{*9D84J^>02;YVP&z3_GZ%3&z&^x#=VEt> zIygMZ_a?UzPPNmEGWArBS3gof!&}GG7LG%4Ykl<0|*gdkcNL+cuq}i@cLYC-JUxr!CZpvFN z*zW>kY4Aa@0W(k4ii&CkQWTOSVi3Y_cw9xr_Gssf_PyT-8>NZbC36H7~%dB%Y{pa@U8Y@6=0+;M%8+A(csq z3T6i6nYuYPt(s*ZlUc3=QsFRIOZv}FKSsuuu`wf;##p#+3xGb<9R|^HO34%t8y*+1 zNsgQ4{^8=aKGw_izZ~mqPE3)7if-qUV{taP84b(+%y~mZf+s-d zZ>FpOgWy5QJ$^~xi8)h~G>y4->F4+}pQuiLyl86xd!6nqbQ&HeRgp_rYKrTwH*svj*6 z*#(v+TyWJer8Fww`h0Z2b#=Xa0EN`>{oac7s*gzEk^C#Hcy! z`k)G{9+%@dl%0pQNVyOSWO#zRqxvsdy&*4lPyckgkKitB8NvK9c|x8NsNQkHG=F+il#=4 z`GSthY_#Jn;6d#@Z&%a%knKM8=bR32*VKR{Is=U`Q~nJ+`IKN~)xTPAvOMWa2FqLg zQ|cIvR^&$6rlIQibS53Lx^-2ran1wC0bcS+bghykjpht6yI7b5jH_+cC}W z*w2!c79Y61$Duk^vb^Lb&XSTHMslf64`pc~o*)cv+D&FsIaT5kegFpycW0Mnh-xH> zJ5cbWMJM)fNY4S}ZD_xN3qgN?u7X10U^7t;>X>T}1gI!J1IU$Fgwy~e$_z~qe`twq zoO9c_#;rQ-z)^3MPy4PRk(8-=ZLKYZFWgtI_7n3Uc%@=V!@uV~OK|JfQgmV!f3zJe z@k`Ny!iHHfa&T@3_y#Jlt+ zzHR=G$rzbVZ5ub=A6gBAQ(+CU=Ob0WbarCPAHG}(mn#7K17P2x=8sf8VNSr2dTFnI zXY$ZN@UFrL$+R^!lSYF0D<0pPY(0_TN|`m*gMvv^Rl-2~U}=M_73?UjeIFMfaS=bZ zqrOu{qVCeg_wm`TNDKeRISr_r06d73pR=$a?-}G;&a>UAPgZG4%k`OBj}J#*ft(ac zf_I>D5eCjau*6dm)QO2FqcY5m7R3Lp6@$(Yyuqt+-o-xG!`4{$RidD|3gw%Mf9Sc5 zP&5(Su>=7Mdg!j8r7C!*o{JUfl7hch^6wz-#p3D*6)dB(O2s<$-Uc({(P9B$U?V%0 zZ~41em1$I*3FL1fAq5#qa}}Q~m`%#~g9R|)1e`+@xR+dLM*n2P@*%mrGm%P`ygwCP zV?6O}=v~IW4k%RZSmnXqHQoqbIT(@e1jHqqBps7NsdE6w1a69YZ?2}a+7lF4l-84l zQ+t@=3i-sPp}zTTiKGQQb58W<7diY4$TNshGUhAGZx$FxxAUw3>QkP*xTs~!XEk_a~ zlES`|A9T`E)kUZ2m`tO7cg5WD4+TUp?}m(?Yz**QYjvnM$=(~5#H=Aiv`4r$qvy!lIwY7!EGf-X%)e2DrK=ZEz( zB`AN_q(Qt+(%kNBLNkJ5tBJ-`-Tj~#FSsBhI*k+`t1lD+J>=b8H|iycmF9W>_&i$cOxCNW$MF1 z0(iwn6wBb>vYallQveZs)Cnz?%G_uPd51h+UNpci75BbZrxG$DGTIotr z!z(!fu*2U^hza=FWruXw2%WSbdFjpEaXQ&JtpWCa+k|T*W0NF+H6ek|78K3R2kB_D zC-CJ4;azdRDxjaC#!!WMyT2rQ%1X)5J~NjZkBwJ_yb8bTBMwS2g8Oa)nvr|Hm_jRU zrKUI`_&y6um1e}EE{co}#e?NL0Z-qVG(}jZ+AQ)0WSy3};{kuS;nBpDDI46GU!;2z zbDQ$SS)p^|EA*Ueq57NZhICT?>+CCPVZ;C``92F$$)5$kL-$a_Uv4dn{nnt2>X?MC zH52~Daup`POeaos{|faSY@&{DdhaihJRFiUUPeMhIrkzzB7=vue81F5ENlo;vk0pC zD)R}6VbP7z6_KLH$;DJ5Hhe{wbt6Jd*I1ZOo7M8Z3E>?Lqpeu1Ad+7gK+$J> z3JP~4VEG(48Ty0>a^-SFl&66lEhq>G|8(`{P6~}ipw5;jZaV%EhcfEm*7`#VVToW{ z2SbpVpNnDKV5iFlH<5%P$3r9DPHPCI1=PYC}okE}o(}5$`lTP?Y(gp~D&BE?8!cAC(lW8^z%RYHj z2XZz_oPPRz9enH|&2U}fnk;LCEfpv9V|xRd;@Usc5-rw8u;7MBb#uk@^C=0K#+6R=G;5Etb)Rso%Dzpk32 zm=O?o^NUEA@GCwX5s-T~092WHhO>g7f(wf4+AK?djuM zkQTn25`nr*)nKd%UcqLU;ln%Y(}>O~4Q_cTvu~rJJSnejAIfDMT31$AD!p z2ngH?0nIR$7dxIE+6HHj;5Ga2gP(PSkhE8+(}lgitnVo$V^CjzryE=nJu1+um#H zES?#1B)LV~kyy7@7R#kU>dV7UU<^;>(8D~8RohvRCrFrda-vIa2_5t%u>ye$Q_+(t zgZf6i$fDIq5&4pcWIMlQZ&bDw@FZu^{S&Hyz$)cg`EV%NZx|GQ`1?p;c7LX3Qo+K% ziZXOX>ccacg|k2x+vkX6k)E13;`I? zQ#T2tb0cYf$1S~roU*NjNte&{dK#^ID?M%^!VD@Mtd@a>-C7gMMlDT($S*_8Ax+$!_)<_3d=zn#WI z05n@%#9iIG!`~F0c#ZiYe^&#=&AD+-WZR>9qxwt&AfPUMoYuh;`y?>KW7Icsa)1~6 zW(o7a^>xb@WnigA5?6)ez!$eAYQA5jebdU|WCqn-(ZH9d`VSc)0GCEReiA4FmX44b za};RZ&GwO^7J*!~A`)8s2csSp%SwR;NZF)3 zIwc^_0sDF$1GOh9GODGFCR_d!%OnR*qx~_jJ9(f-uO8>BErjz--Ij_vwI*09UfZv< zk{9+?16gX5o79hLSYCt1yaXMUK1Z|Qb4q52aj(Z~EmFFtsjL;PGDHG%#G8@L&aA^x zOJk-vV>vz)lbZwLeTQC$$RV{bqw@H62j!3L42P8!pO5Pv5+usOWt%t}ipi6Iw+j#- zp^zWYa}c;|sNh`fcwIYRA3*Rc$)PS(i+r~_Y(T(Y0{(tcN^668Fy^zFUy>m8JjOYt zO3HV;_}qabWOwPX`3^i&Ujf7hR;__qdf_Iy2AUxa1jGUP8evSpxaaghq*!ubh#{0c zN`-knDYgNXTwa_L$6TkBM{%ejTgna>?s{I&RSZVvJpO?P_pMopjR@M$EK4uTay{4U2I%)L^HqScjR~Rfw|%Qjr14GT74KcJLVzleS6OW&0ck93nSC9@ zTo}jN?bpNGf)0IgmBomop(&H&UJAhRbr1RPAU#Y1>w-FC_%=o{YMsq7GpPKdrBXv( z>veq+Wb7(z#m4M0+z;Xb?oU7XLKHWo9d^o-*jj2@$qQ@^fE7Ht^N7>Fk; z2J80oRx)-Z)PTUrWe8!#(t=&@nM`hO^~+T>Fz@#Q&H##@@tz4^gT71Lh&*4<5@2h_ ziGL@Go>)L_*c?)yZ&nk|3RlP6li8lp#T@<$P+g0DI`M7)x55 zU6+feq!@ESqP8K~Wb`XUKVk%6Z`XZ_8dBUE^}YU4X9ly_^tH!p!yjak1E)^3sXOi` z1dS!eqq;Jd`R+tAJX~Miqzaf^pK^>Pm)+Vs{IdTKprl+Wj)qNf%Z!<;8Z_v~*0K`CEk@MFt3w2X&}@lY zyB2kJer-rXe0ix(7M68icUE|O_Fo2rnyle7cud5telo7U#L|t3s)HX)65fEyL%B!W zRMVIvT=hM<(Ot{Bls_7zYbq}(K~Ojo^ymb($f?U#9Uikh^5){6hp7wru^Yd1X?CGO z84FdARmk^rrv)PDRKv;`#0TIC74A*prX`pa&7xP9v8`_+p%*#3cB7Mpy^0@@YO(ty z-qt9LIMSmdxq-Q)$uH^aKVMFNb#zWXOLQFhlBiM=x@`D)8Rf3IG`1;@a=%1SL|iS# zfT2y)(%PhofImSeD#gjj{M+~fb}9N^$tRN)YXOU(y>|ok$sY9LMP!++mC3I&K5;SXdsLMZXsh^CR{r>N45a0<+m zT|Cs1D&GP_n@m-iYYGB1@>?}JSkMhf*TVzAo}sTEqx2v|sn?l$>j{O(S44#%uB( zJg_DnaexG*GgHw=s6iitdZzsYm1_CqD85v;zx)!uut#P^pb0wLr@ZPjRBbruNY} z-f0d79*@~4E;aVZ|32&qJ^x!L)~J5;o7y)>`6r6=&FLsT+O0yvg2cvdQ0Rr zuifK9K>@ea_Ffox-@qU*NqBZS_O;Kw?TVnMD50FW0cXD8<8KsNNgc)ve>&Kp)dtOs z!#4);e0*%K1I@3WgjgmCB&PNYYamF4)>#I7D@F6&dGl}fuBMes|YI*_)*e9Z4M>n68%f29DPvlHT{YB*O_ zdq@RQxN@1%=S(vnuIRKG%x$j}s-#D+Gc~6zo5bPh zEiz^?gB^F=TRa?9!emObRTMvZlrOK8s25fRMyz;1$6=J|qj6_L3KDZ<| zm1F6&I~?l=f92ks(G{gC{`?+Cs4_VC`9zv))Kg%QsLkLoyoT0y-m{5t5k1Pe?XY;g zX2D>}Q>NBud1jul&2@L!&n%`lCp6t91t#d@rN@x>$BUq1ylJiTGixzSBT*1TK0{;U0T_J;zr&s)cP zSBSGnc=MU;d$o(Nlv-;i+xwsc-ud&=>_zCP@2&h}i)rraXY40VsGn~5jyTPI`R zTQRh>SH_>fK6bD}By2tJe#DrOR6yD`%^H-CWGSgx7+H(`nb^YNlAh=mOJN4Bbg#8pZ8~!E zS{WiRtXX_-sIzEC+=gvnt?$MTj+@z791~p6>`qX`FV;|z ze-ImGW3qkT9%}CGmvc$9EBccNW~QhH9Q|mpArT)-hVHn0 z3U9Rl{n<={X({GxgDy^8Hw%LVw7sOY!E?n&@!oir#e=Ed$;_VoNrPbtQ}``AJpTPR z0tI1FBd>(iL03IkLzxWLacx4BE>_>`-u}Py|Ck$9Vi$AEB)WoTmfdew3asu;c467V zPuDMJ^UX#ZIJU-<)1r}m%jN=ASPyyb%kF0zS$VF8IbG|ob8UOYyH;7q6gVza2}8^% zOJKX6_-s5#hgS7m4Y!`{9RXEuN)w~@8RicO@3v=gT+~M}7Wer))N5v&#z|)FI@T%d zGvrO1SPoZSmvc{Ol2k~oSfX9|=4VgK<@larkMvG2zLV(|H_&Ep0R8G=> zN)Nklq7Zq8X@XGtE>tiy-#z15r`~oCx5PONG6n*$PTdI04#@afXO7coK=%+W^tE=BP6bPeTX5Up8`%ogh$0 z5xEXii`Dw(t2t>G*h5)}`2YO9^xc;jJFDhWk$Rp)XT94D4BUBU<%1awl=gESo0pAQ z|BP0UpQIuS4a}YX-sD5jzA8xKJ+YDU9f}t*5NpCX7@7v8)8ZUy}e-~A@$6n3{7_hfAtOiwH z6f!*6j63cLt^QlAK9m)#eJMaqla$4lA={z%C0r%duB6G(;)a_Jgv$tBztz51*lZ&zzOv78RBSWrT1*xto&Mq~8 z`t&V7a@;P6FoX4T>pH&TTyJ z63PC&qw98QyvXHfrLpf($sMb4_Y=172yGoo&8yDg=;n2K^K|1~m2SqwSl~IIg_Mn< zLy|v(V*s>*QY^~k%wwc|Suf8mHyS-}G!xq=wn^l9S$uAy4k5eRAHW_M5%q_prWuJ3 z>@9^9x&|5&{6|{~^g0S;js|NX7LN&Ry_lXC!%X59yGW@qZQU(HQVg=l2HX8}A5w%c zv|<{_Ln=@|U)rVi(+wYzqdZcAzUL}BHok`a&K9SJc}9#;=Rgt}^{FM)RV(U}@9hK4={@%GB2*$ba;J-;koAxd7C`Nueu}J|OqYp6gnD}RU#DplR z^0`mv{N6yWEt!FJ2T}jl%EO7wWQ;{kQ44;bU-=S=_(XJi>gW$kVVDU> zAmzhr)WuLpa<$PU0Avbz9xL!zP5kXfq2$A0nyVmF5Gt@+{gm@|k&=m12sqJ^=`Oe- z)%vkrrfIkN$JOoAJCjHY>c-4VOO*E~mihW5;~V>Lr151--rA@@Cd6L;?rQIP%~LEX ztcoXVk(Fgn=&DQ*p%L$2Ih5PDX%rKT;9D1bP2^VU8qnW$`S&Wis5$zYJmnf-*RuKL zQ3m|&Ry_tSQW6}OWN7(O*H$CaqJAafMqGghJSd*lHYHc*t)=8_VVE%VEsR!sua8v7 zqYQW9f(c1+z)>3b(X+@ujW{-m;M=S&>-HS;-xe`y-zNKMYN~5ZeSRizO2@RO}-7#+pliq7%^jAHA}!A3ryZf^s=<5P>M`#sCvK%pTVR zE-nTwt|a>Ue>*j1Tpwgk{zxY}%YG9$6rp9Z*wxeF{+Vd|Vjpr?oWdKu+Tb(}ktrzP?^ceTh=Qj6zwPyA07*naRCr$OeFb0?$@2Gab~i?V5D4z>;W+GYcP9jbySux)9}b5*1b25h4m|eEN)$(t@ z+W+q_{zo7&eEtm@H0To?96aXZ$B!PNp`p<0^(J2s1P*eU7>xh7{NIgVu=bqs??g=T z;>AP4gb6=p%$RXhU|`_17~azV^>qIc$X6VH>C&aoK7018@TX6oV2s@6NKZ&^3;B)z zZcYp`At}8pzh}M`Pft&zOque}D#AN4 z)i-~Xh={mN%9JTzojZ3f**9On5BkCX2*l38Rjpce-OZaf1Bf6D6X#id#~$10x>&*vSrJjzH{eJF>7Lty~7f?ehrZkkq#>M zFa1S;2mws>kd$x{(!=y8{s{d)!|(iIX+%9rvqb%!%}4Lj?0}xcIF%ZLp8hDZbyyqr z#Jz0Uvi*Ma=+WZe8!6+`_x~dh0M)8hTXp5im8SB+I7=K1w>fzbszN2IAqq0Si_o~b zBA#agc*ggHhetekc_&8V#7U6I%M0-m@V~jZxI&|GVY6tpk%)|pKv-B9LO*>%NXUD< zeftLQ-@QdxSSUV!`h?KWAEAxZvQH8%5P^zeon7jD;`mWxyXHT=Zgsk6V zEzU#nd#o~+zL`05W|5FkHmlmYT^;$npc0TEQDP)dnHo8BW@ z@hJ$CMIs?n4#F>cF22c+NiRfk6*B+ItcIC{_4xehGhV-Xj^|IG;?9j*xN-A3UOjt) zk0EcNXPm_OVGIKjE8X4Q5gZ)+e~TyocN?!>z54R2SFg5U_q~LWw$O+WStT&EWC%&O zvZ&WXR0)Wm&wo6F-*k>lyMj)2J(H`8!|`r_4Qq`d-v{^v9RPgJLUfkffO!W`0bM?PrPMi)e$j@ z6NbocZrAvpj4PC@SOt0W{e*+mV$9j;#f6Gsm2 z$KSVZAmY;mE^*x2aO|umj-6`EZ!&M*yyCle@7Toh zAro=PRwZ*4*hF4=j-b$ zzIpS8=@<4yDQ*nnVIG1?gZPOOp?sC9s9K{I68j|o;rt!W5-9Ea&8z1)uzNQS?cI$J zA@3lFdWj)Q<~191FJClCk|e>^t5^S(xb&~v6gl&rJb9ATSd^0gyE*dkzXPsrZYWZ` z6#N=BLh7{Xp;o&vvngG-ik!w-jEqCi%RU|1U3oc!{)Ir8S>lP{6uFl|_NR~1s0-CfyvC`$?;^rpd zi4;4tcp|8fzofHT0qV``Ovy!7o^Xeg^N%E z9rwQi6iVmq@b9u;N7U(YFX%RwFIvp#IPs$d@ml z`10jT3JX^_c+#rXC{(N@TD9wl9lNpYPAL~ zF7zK47Ba&9i1e=tBrZ!2wjo2c3TJyWtZ?I_YOk6m9QW9C!Y>#?9 z1#$9R7UMx{di3bg4~c6(+@f>j$RSdAxvjzFVSe%D{kaIb^ym$r6sb5e#v-R+Vu!Jh zT&Kg+#}D!B`7=BWx`zk%g7E0!1AO}U5s~3vq;Qf*=prMn>Nwo0CNP76#>E9Lt}f8H zx+7u21W21U9nxjWgmjs*B1MW6NR=iHR4R?Z6~i?I*()FO+>yZ%0`uB0m@y64E}v(f zdnMme{P^*4>(;Fw3g>>P#S+($O^db^XCjVNX)>YPfPu)AH9J&v#ge~*ZgvHeZ*y$}& z@#rDvj%GtXD}q0r!{f(~e;B;`Ar@MoK!NZVFJ7>;RPzKQew*o;=;G!XAMLyLK&c89 z0a5+c&TVr4$#GL;+meA5@g)qmZd}LF0|#*9>J@~%c^T()7=lZpB*~E5Hy_GYs)QW5 z^1{X4ofD!F0vqo!x)%heT@fdb?8m}cGw~(#Bfvs*qAmt`ZutyTq(~8V;lhP@U-ivz z@N%3d5SzM&nYdJ{VimOS)(fug9_+AeM0-00QiJ?20&iYE$LZroapb^0<||+}QSzoU zJ=Rk0cIrd1^)Q9OuoG6XHKt0J0c9&zLdh~^kvLgOZn9+&^IvbEU>s3UA>vCY7R{N3 z;|KO}_(+^Fieqx3k#)DYuA9VZ0!f`ZH4~{UdA4*73IbES2M!;L4B4~ut5IVA+}q*A zFc@sOZ=FWS>(@AT@BsGj-hr1do)DU`TaktL8Hr6S;Li?4MsdEVxuaHQjGXd{t^Lg) zbQy)TSeuVO$G=HY1fU!XYQn?W-OwS+eByd-v|;wl)ciu%$={wF*^g)8r-{kn;}+obQfznlB@^G2yD7P^P{l5)n)xl z)~lkdR>}&pHCS>(fC?G1WJiO>P4IK!B2Z~u+23Tv$OJ}CoGObQoLVKJiT+jdKK&En7D6^XJbtZ)CctJrjCk@Tf7!k~0rfYBiVt zzd}qP#C!kt6}D_xk3)O+ATsPT*F9tf*+$U?fyl;!3p$Wa<5tvJ48Sy052au zz|w{D8P|YL4$_m;S?x{E#TzbL^AHR#uSCd@AtQ21E;a_Xa_CgK+86C7eEe5)bd+$NTs1nIOn9oJ>tz*|2Hm zHf(`zc|vRrelAi3tvYl@iZto@JZy2?oaRjEjm!rlzl34Rm|?hl{v7mD)}?%S#`Do7 z@!-J&XO7hg3~>I@II|F2$~bL_Ooy&&cQotJ3DxWRF&#ifik=gzk2(ZGPCXtz4922g zXW-`b>s(2dlgbrgC`JAXDkStugr5r*M3pMl@N>aJ@b>mLG1V&h1r$H!-~*5F;l~N9L^A*lou20!jpu ztR#+nQocQ3K7WF_)2HJ0<;&1ZIowp+P2~WA&>X2!r$${re^jkf4arlIB}g)7vPE#K zk?b^!O~<6XXXB_^=E*0hFsD6TBHX+E{P{EN-@6xEH*Lhzr%xO+NgIomqJUiae@5qC zeUL0=YODSl`@B{KiWFu>0|58_zJpQy`y%x7dyCg(YZr0cxM1P>?gV1aIb=nYB_RoV zq{);SqbJWqf`kbjp##%rm=3|MHdN$b`TW^9e)u2@2eCOMd7LSyI_?EYnk*^&8#RSr zJwJFQA}- zsY%i)N|vvPww=4f&BK#9VeP+R2`AYw@bP^JhWG1@$M^4Yi@EFsHGAcIbCPHtmGcN< z?Ts{P(wLZPjJ@Qf-xVoY20aIj0M&1b&R|Sk7m0nlc3{=wh0unF8JRO}&B#S4H#ZNI zDPJD#J9b98^qHV$h8^ep1tuKP$Ro~F0w?Fi^c==qCz3qc_wQrTg84Xd zvkPb z$Il<09v)0Y;VgseE)r96ie3p3cJ17O3+K-vOO`C~Z`g=gY#b#LB92d=K4QbVwakiD zuux&ttW}%A%^5vM_L`IKrV^zH1VM$*p<&p*WfPVzT8MY=-YV~n(Wi)HsnTNLh%rc; zF_ZE;Mo$=wFj7SI+GW3C>$)`-Mg%g8lhRIb`d*ymyAVjabm>HEMV;mBy$;=bqe6|^ z4g;M>&T>l)=@JwjyJFD-?BBk{E)sGFA9rk}OqmjW1`I;UlBJ=JCdab2V2WF6*|Z66 zUcX{clF8b&WyfxKC6dYk+1V-S6CXo9z^_&{rhGGDL==%Tk1tlOS?3giC;^nr%g2u$ z#f)i_@$BhSqj)jfXhO(drA}S6XxAAU7gy#itO1 zZ%!a2R#{WV>2HLXeTR)ip%SHeAv6cY5Q9Hi6!GBRU5pzx1n*wHFhua$LJT^Fl&RBV z@Q`6BQnVPWOvsNldLo0h?;JmV6n(mP(S*$Wge1frBEnwKV$;eZnFlV*Qj&FZ? z^eZW?l>ea?fxuNO5x9C8po?S)QZ%?;!#&MhJmO+j!h~KJG;9PaRIEg9c@ry^qlD=Q z;!T@85$jg3G%>MqMH?7BZX&AHsKv!TJ9~UfMSe3IM=v# z;T*;c=?{_B@RH)TINLNtG<-{O%YoH9f(FXZ*s@kJbL2l6fg>kPM^4|LlqWJgK}xE? z5{knAF&B;;JP^S*|FZf%zkN9OCNZFOyY^_+x*c3yd8yCX98)HY!-n;1O+F#tj+79! zYyV*+N#X-Rh;sg!J`B{aU5gceGrv_E1T;qfK|^A5`E2fphzKlOx)@6r{RVAhgj8qP zx?hE0oh5f(j2t%sZt=V%qH>NS-?(xKBM0*48%QDY~dT)FaJv6JY%I%VQ` zY*@F(=F3FVqehQM_1d+iituKcj{3u2w|b0 zO%l;eIUJM0(6&oYRIFJC8j{KkBAFww6maRn@0d7b2y{{;vbpX`=KuTm@5d?*$Li!c zbLJF3e*9>FhOyU|KHO)-C=@GEo`o+sI_P%N>9trqYbFlt+QO@mNU=u7b1)90*P&#Y z@|ZAb3TspLJ%$p2(3)Ia-LZTB0VZl$=s$6gKxoZjp`jSqzaLJWIAS#qotZkM?7~Vl z>Y{D;9#E6dgnI=Y8iX&upFE5iqemNx`Z6ZZT_x*9Zw(ck6{} zHS6$lo(@{82&&qMFPJcF2(Df_&vX#314fw?xiT&=g>Lxhai~_ky2E0dL#O8CLrMg~ z3pWVJn?FC6E?;G7wvD3%LSP`ZdiU<#7(ILl<0LG8OFBzKC?+)?QUSjC3t{lsNl?2u zuJlyvLg5;0`>DAcRp(DasN z1oC~AbS2olc<~$^+P1;thxZN6K4UM42+GY@Ax-8i7(Zq?~R?@J(Rdl?HZhjdyP;`jc2Wk5m=u|6_yY8Dh|&p{N%S!0Six{98vp z5UbBjo-hu9W@cVnA0%Ug?@tA>bfuA~^@9)yuha!>T)zraCQf821oG+daCbTN7w}G; z2vg=QMIv?)IL1ZT{JKc=?${Pj9^A7E!q~_qiBSUi<(FTcJ%0Q+h572~;NvAogn7$X zBfeK6hw4*t^cL{`%`5ck)CM2ky@Q73BXEEkTdG{$+%aeVLi}8yV2p-d#02G7BM@6* z@baDp4jqpA{sA0$1i-&`EtW<2z5F1~z=1gVrHkj$wR1;=f8ha;=GG@VL&}|*I&U$& zk|g8ic?CL=_fEMjom(_R*yj)kf@MX`G-=X2|KpE8QaZC{d(Xxwfsnt$TJRxxRVU7v zi_E!mJ5@pB)BE@6*{&tthwuP8Yb9zDdU<2znzcxiCau#7e@p?<-aNKAh&3XS5s7*l z?%j6?iCHlP@rNQ1d0&qoJw&r6jq&M2h~YwG5~EBo01|j7#?0TABfgiH!$(1m;lHn4 z$B;fVmj z1XRvDWGfXtlnHzDARV4e;{iGpWK#l$A>^&zdxO3QS+L93CE? zhH|V*H#!N<+t;tf+NBE(LNtr~iiL2L7$K0{xpRw@2W>o9Qj#Db?@vEt_~dDTpkZB^ zDM`bG|YDtTh6m3xEnZ z%$z+3b!yfi4VPyD%6nuoBrs&eDAcd-k9u`#;{M&c+}~p~zE!A6bbum<_fUjGqlW%? z^7yeuQWTH3Q?V-hljh8WM&oW&Qr!Ai+0`qkA?hPAs$U=cb@ehtinTL>4Vj#&`_=X9 z*S{LSzj^}NhSdmN5%BPgkA-VCuz!u&t37~|zA}E;AY3{32k!yMVhf}0SGrw^IM#36 zgn0jiGdNNcHaZ6!Q}0+6u|yH+GG)f*tvj&zw}tp^{#?na#*c&YRx~O#^BL^kcL?p< zv;yU}mjE6-Aaf3_Vf`u`3+$6=|Mq?=x7;v_DI{vlb%%wWi;%A`q&X`~3NHSM!C@o=R4zj2`_5J0$+d=y2@NKFpak7HXFL z#|18#Kju6x3j!(`o$R!e5_!<1C!b$kc`=|dB;xXt9kP9G3wT-4!zE@@Ut8h zKnP$y;xY%Z2AU7=L*Q4ZHr~B`ZT7Qlk{8jX|4@{!P(`Z3qG;NJpx0yO)E-jj}Ufu>jhqOboc2Mt& zH5&q1*&k~QHVJ~HGCUMDD#ZrLRDvIxUOg;?02!D9w7%&tJY4E)-x&v0OIO7^0v-ttUqH z@4=k`Mo7XU6kJ@Q9rYV+0bq~A-UtGI*!7Png z>Y$|7VEWA2C{?;ll(=PfP0?ITWYvqpWt8LAJuBrk&19c ziO9zAlrPj1$B&_R&#vtJjfbpn`umgvF_Qv4a??HvOZ;xQAZ^?x|ePi6}oiofmUs)d97_f z2boBT*oF)kh<&@a8Qn!{3>Z8V{tW{h%KVH;!jR{(7R_soK;-9_Em@4YRJ_1}@jVG7 zG9nxk$B)CdEt^bo_$$|_jR_McL)4Q&*1Ar`{XPPgE}oACbLJTQA5oSs`c;h-??+Al(CG*8>IlPI(I?! zx(#>?lw!{e5ig%VL-$tA488ee#Y%QGmMmQszs{Pgh$^Jdzj^r*RVtJ-iS1zkhYI}$ z3`9Vq#sysCRD(#IpHw4Jp>LxJB#A zmMxppVew8Ah`G;$9jwT!S}a((76Nsgb)e!40k3?b~SJSJ$Ax6EYdv4G1b$+@ML5rVd%v6e-e)2dt&s8Y}7M#LDgW4^gjX z4d_`4vhDG$br6E*{5i9+eCZ;F{IXfv7^%sJvvKndUM5xn+N5MhgomSM^{RON^0}ok zN7Nxj+6th*JboxNzZ@&z?O?YI>+VOk%{O z8OWQzfOMMH<=FFV--3UZ3a>39`0G>a6f-app z;Nd;fNZKeLl_bijR5*0xBocZjQmif@pTWZi4^Y2uO{TP)r%qGll!i@P!mn8?hwBJj zyLcX>2lwL!1EW>Y_fn=z`Rd%cbIBY+EJZt-*(}y;T8YS*H76#|S)k|zWsN8GBHq4w zhAu6evie8ng8)8Y$S^c$*vQd67@k|cbO~n9oWa>EvwlR;KNGh^0e$)pK;y~avPq>re_roauDI%N{{+DJtXOjRj6b?N4?axEVo zcwb-_Y9wPd8>4^J(-!fUROw7)35r;X59LjEf^HL(r zl(`F$K5GsKJli}^c~G~`2)c9A$__==p>^4cRko{U*ntZHVD=BG>i_^C07*naRMBq> znN>wamD??FPvRu~2cmJ)W=<1ebY4*Mq+8R6=+-2uk|JX3*Qo(2U21K_CV>#>-oJZ` zUOl?u>g7x9^Rf1^q*?Q& z3h}lbh)iA(RCsvz4*GU(XQBtmxSJ|fDu=mx5XevQSGQKnGv{Um!dg*<%;E)J$5*^`24}FFN9S2e9*9AEK%rdtW3P8o(_u_En*5KnUBpKN7Cxif6!nw zBI{NxhRN^Svm3((4d4aP4A2%)xoS;}n>a}knMhQmXxO`VZbcCoq3l0xuP02NhDuec zMe8E5`e8xE+C27PWbY6po42OR}qiCO<%$+s{uE)f&W2{!1^h}rt zi`Q;|#?@WXx5bPd_k+;8LtBWX+ZZ7M85?rv&VBIU!Go2Q4!|J-v8H>FV@DTVnY-}D$uJjp)f7j|FQMYDIJfm=LDJt2`-5pzY>_Lj;DHJ&fd8nU0 zeTw>Zs^iO-aD%9mI$c_9*|r-R4OIzop3|CeT`3rps?YcB)e|2-gfLm34#HYh8uQIl zCg*QUSA4bMgOHkH&KmpIM^Mmh6A@Ximt40=O9V70h22J>Jwrz;4wa!jI^pikzfAmc z&gdcz6NrV}2@#WLFGR-dIqee&!TBHM_~Xa-Xy4Eek=h8W$hC3nPGrpFTp4{wu#_Bc zOmVk}@8X&48kGk74;_Vf5~D&1j!f+6GlGDKFJG{5!F*i2a2^>mW)y@l7|^Y=RaiSBW+@T~wJIPW zn%^YJ)1<}hC6w_-715QVE`i^sPsM?~JFOB^^_sOYYTN{F@O7d_mSYDZV(H=~m`j|* z(Abi{$pY*}VB*wYQL&ODw{`4ucAk>6?V`I!Ywf6`I~TiwA>!`echI9-S3H0A)T#Y( zU$_dt&Yp`BFD7k7e|;@p;}FJ?$uYhY7ncbP5!nPc=6<6l!z*2 z9?~W8@Zm$dG@wWzsZ*zBskDZeAxW0&H+&Qdl`IPtIgD)Z5Sf)xPI-rh{?I~e$bDkJ zv8sY@IB@hh5+o$MxpL&MGp8trE)Xg#U$zAEX3J$tjR{1=!X+zMQ<|6_Ji;KV*{CtF zW!OaLn^AWN8A^B*gTq5QOVVv9QES;I~i58m@{oMj_=#WEIG2M!*l@w=+LD*+P3fLDD0yB z9DR@cBy(r|DtUoPX%>+tT}Eu(vB$&%AMKC)058(HJb(Ti-8y$d&_B&Id8Djk!zk-!lJOE{?)I#Upec7jNIen@0kd8v0bm3^_Ul*aD zKAE_s2`7>zOGYiUZPI~V0+G3gF?%Bf_iNMw0j=6d@~`#OFPTa|ef)sd4eLUTBuhdR zDCu`kPfr{?{yW@axeb8~K>FsP{rfO??i{=i2| z7Pu9j2@=@mB1Zl4)-GF&Eo)a;s05@sJbn5!dIVzL(}NT-efWU34eB6L8)?xK)1I>^J4sr zxk}BS7iA8v8RV#ndbhcJB50A9$= ztXMcN&_u*Rq3L?0C|DjJgRkM!yEj(JBVD=-ESH{27JaRG*%6?Ihlis==@JMF3$=J$ zm^Ore^JW7%_TGOv3QLxg-cs$T(&CZP~O*D)cSu6Q&>{{lrD% z0^d$Ukkzj>w2`6MSHCzuetZLsUIUTZcu9k9*?d12#L{Ie7$REp$Hevlj2u1`yS8uT zRs1Ear9G&i#*9TvkR}5ad9X8>qA6g?sG&G_`h>-Uw!|!}1Y)b!)ySzckkj{PCKf4C z?5`8Y;Pl}`yvnx}D8|c?s<3m<0i;ajd{A3#AHt75|KE3Rqig3*c=PIoNuLf=J0mx% z#7~zArKc=I{G{oj6C!bbSWg6>-U)Y|3ZK4XLx=FvAX5i8E zQ>V>9*@_j7%GrH;z!KM}U$+kK2i-9^re)l6b#=vxt$P?pB)hYITGzh&(?R!H7vy735DYMBVt4I+XUYuus#k<0ei) z)oL{r^Oe8-5&U>xWKO?l&se74y&%&*>hcZGBny=Q7p(x5j}J;sT!O^eeVNrt(5n&p z={5G&FOGRkX6a+di2C;xO>ExZ0JgY+Sh>e-}kO@8Z3#f+qQ1T#PMUHjf`ZnIS)G_ zm{N?H3VJms0ur&(R;Z1_{U<=>=D|8Zk(EjV5l1`woq35E&MRvL%ZnGBV0-AcqtA7A%Aj6Q&O-;(01UNvDlO)7n+pp_z{orOM#f*>fB#ysG%6|HlV-iaMyWc+*OppG~s6Wg{B8rk5;V z0pFhre7BNGru!kQn}D8OyWrHxV-|-`meqCJ_d>k&`)Slrv`Ma=1mj_Q6!Lh z_3BMJckWz&V*(+0E_v$on73lJqTQSBuG@cI!SG%^tWxs4g^N(ANKuEwyp=Zk|Mokb z%ZcMhF?7%XRx#anz$5FHf>ecz;g?|xSP{KQx-E@D2EIIdfbC7nvmzJfgXRrr(i{T@ z4Ps~hy~HYO>pFk-3_5pcXZ7gE&6tI(x$`M1iN4w5TZF4pJ6*g`=e;+1Oop-tCbilGHW<8vpE!SMt8je1$|a+D-i9X)v(uGF9K zzu_ z!OkEb(9$`xv47`ANt)pg)Be4@yl~^j4Fg@65D4iXazYOBd0nM>i%~nGcgx1nA+NBR9&7T@H;; z3KnA(sa8WH_bz8Oi$(mka0>3Modd0)fgpxMloWGDU>X-}-nIj2)1{BIKy7%dimaST|LI-hi(5m+D z*^R-2`kUYxySxPfOV)0NmyeI#r4xaC2@ONjI@P5ZH2%1Z`*sP$d}itcICsr@sHxVf z6;p;WLH&O82xd$eX%+>_E4Uv!afa9Ijhk?fW4IvT{=Fdd?AC>q{;?R8;mORIDkjP% zN}d`eCa*;Nw3aE$Iu+o`)C@hs^x@bOP#)p0pP9OPrJFcMUSF(UyN;70`Ap*C5P**# zKA>Ew5}bPf4E@L zlITBrEbmKh2Vk_2Ny7%=;<+;xPbE&`g9C?;^R@*4QHcH94I*pHMmg+c5wym&DaW6B z#Ooj+XZ=>l*I^Je8dsAbJozImMNOrLs8iwWgkE^Ke=EB+S$jKa(3yC|^Tf`*`&r2) z+sc0&pcttm0M%iB^X8?+f(jHaiUDILC`vnYy-XTD2p9i2Z81m6lqo-+KYyMj*&BF* zdHj`}=h3P60FLXoxG181V?Vrm^Tq?A4xYBflOPhl&wBxVjJO4Nwm z?>M_!nE*(he*N+Vx_9Y}yHZg-l5$K8CR71mvg^7h@dW+ zs}?WLAH~T5ZJAFiQc&}7A*mD*-B}Z-Oh<)ERScX%Nt||epE344a|Q_*Idm{~?c8cY zeqMmTS=2+n#ga=3*edLnO+-q-NC% zzn>T(n-$rrB&Q!``bCCYulhpIQ4XM+2&Ac42Sxjhf#~Kb9j{_8Ic>)qAL zV*(BxKMAiyiCIvuH4!U98-WMG_n;P3NS84qGcOuPZ8}E|)}H2RK@Wn15D^i9bm=pI z61KkQkPrXkhxh2)s|U;UlmBK7MvSQAd7$WsMM(WiF{WI*YXyX>xC71BAO-wT?*Kf% zbe5TU%^ywV?Twv#4}!|~IIDNa$bA1^Fetf>acQ%j@8;{`*Ru8X(#@SALq@15yU|94 z8*?yHAnyXd)nrsf^p~yk_2SsGcnhO)> zF0_9TQnc=vfqil9PYM7t9a*ww!}?8IEh`3uZQ>f!E*!zWI{%`NsFmmC4e2cg5BT{|#s+Ei8;n(jin|1ED> z`nwBsLIgA#4;1M)9_cFAXSwAv651DKM5sjp?_WK_p2lS$YIT-@qar}XuLlkp8coBA zfL6_$u)@~1!d;?sxzL4>+c!T}u3l^Nb(y#W{OaKD-M=kxE6>jbF=FC$hbnaT@7xCW z?*>Cp!JBeopHVp=b_tazQR3FUd-t*%1D&MxGUcnI%YZ@39&}25Y}N>GUy}Jyy4|F* zl??+Lq5oj2Nn|;wvPij-zexGb5`jp;%tua~K|(LD=)lBy#6xjCzC4 z>HMYZpbk^^#+316uzvkIGwvtCJ6THnwrai9y3G!s6$H$hFox+fyu?Vc?w@mLbmi7ydn z5i}ZX*t`uHGiFqp(&q1>p`THvcu`3V`g-S<@$K#6>Wb6nF0*uCdt=|;-55HM8s(T; z``kR-v2y!4&DxTSof;=|uwixj)W; zLKCcwQ)e%N6vNmUObh^U_~1bd?Awzy^sy(4Fn9@oXNpWHJ9!B_)1`x`*El7<&|zxH zk#_SM4t1)_^ey^7=1`PO=~ODVOZQ%A*}C;t`8()xD^ubZR@zfJztk}ZbMaAyhK(At zqT)(A&F$Ma(WHT&1%Z&bw0ipicu*T@JA(z+UcHQAeS4bhf$);>A~-mh6A0DcrK;Kn zDwd3gbC#?`inPixIS+$_(WfIJvr$65BtD!teU3$wTHKhD?&4ps7VZTFnfy%aC|RZ) zrvEzYtHd7y@QS5NFvm*Q*hGmE;oz}TY<}Zt0LP9}`6-(7`t@sQ*`y)w*>2Xq+k6I< zMuUuXnxSx~A<&UQgL%pIygZ46gT!JgOt3wmB*NdkW_jgm9d|0SA&AJ4Jr7o`4|Iy~ ztbN`dflyIxSue5gb&B$U5teIQTu{%?AKiNOb*S3@=g%Key7(^^^htWm^o1*sE@LM9 zmJC6_vxoQ4t#u0%ahJuUsnx7*FN40-3W}m0ivxGT%iF#lKxVg#XHR3|&_O0=Ntc0# zM?9Q7bCxN})&_~;qel$GuI*baeomGmId<r57%mzCDe{*<7XQ{rnzs8mMnu_C*T&SIrrx<}) zcCO{7$aTAqz(uhvsDORDwqohb85RWM z@R<4VOpyv2(FHn{7J5A|!=q?dx3T2s=a1uL|JKlnBuC=&`8vPemDL{TWSTM21JAam@;*(vMXA z{n4XGQ3PVX8?{D*^?Qye%9pgUWeewG&&CZV1VW(`>C&YKwdJxo1Oc~h+(6TS`WBB6 zEmL|fIT(!(l(+`944yn`BCcJ%!W7M3efl9=jvN-A0jfV+rbJPEF{;YPl~z5=jjvLz zhEs>==)+X-yLx3xMq=&krPp}Khf2^OPlYh9Mm)=Wt+QPe^Qp|e`MVJoudqMntG zLM(dE5p3($sD?)mA6gJdZU07S-l4NY`wXa7fyKQU6CN3g3V$p-~y}Yn|^B#zL zdeOcdJbm0K{Bitdy+q|)exTr93siK7}^Z)`hS+WWjDQW`e_a8o> zX61?q4g1Vwdlu#Z0qX-dA!~MG>rtmd$zSD56*t5jG1`#oDy`eJ6MtMy<3dFr`dM%&p%W&lCGVMdd~eO$E5Ak@OFTaf^;dr^oRD&G7uZW#Apn zMmpfWLr0jmJf?;%D;AyG>h+tEGk0!9@#bsSLh4zoCXI3J>SeQ-HK16Ta_Bi^gz_g; z0$SCpgHIpcn6yWr7j24I^kdTG$uW1;CQyw%yHULVoX@a6J#g#FC6h*pMBU}eRm7AT zR5H@~46+BbT*;#NU?HoE=-#UzTD0QU7I};sF$_Dlav7V<$ntMFa^}YB^`=F#&z(Js z&h6=a<6>hx+41zbi&BRsWf(g8ge)eME%6JlXvp*X*z2@jZx;>zs96{88X!{bj@kY_ z@FH34yb-C;c~$o}UTiO~M<@7sb4ll$0gC^KilA`~r7;o&hCdlnZ6gxE&EUfprz z&>>R}LOlrc6~L%zzbYObLAgt_#(4FDoYkBWkPbq+kco(8>aGV;rB9Dve_O?3vh1A| zeV}){=6L*o@)n{Fl|lg;1T@9KA><^qf7lKE>mm5>+q@g8!JDZ^+42=x5FH^Gg{8cI z`x=!ilwp1BEQf%VYd0Wgu3UyWRxDeBxxdbkI?Qt9pnWFtPRznzV!AdLE&cknYiQ9V zz+!n+dS^=hJZP9XmlQGyL~2|yyte*{u~Ch06j8>p2kiMlR5HguqW%34R`hP$3Qr!AC6zxc(*os%u)mnMIUsz_ zkS!~w&0DHid7V>W1H5`cvH8XXqDRX%?a`wb)yJ}bbB!221iQA8C5xL}`4HigG&%Mk zI>r!+#3%}J-o10X)g9A&S+nQD+6_@$q-Xc8IDPCWNJfvU=c-4x9J#PQ(9+W57;4yl zN*5V{S~aTS#dC@%Fdc3h0at3*hr8NTfO;%yQGV}X7B6N@upS8#q5SG&@bvPwm`g8N zfT7djVEbBl8+46TCNU>?@|JJkwVQSE`dXdCd=d5tBvqQUQtbgNNN@O)h?FT(pl81U z$ecN=;w5Sg>O>&3XUxEgC5w!t7w$_*mOKUKkT+f7Q0d|t(4ifIgKil`l=Bwnwv}{I zMCP11F>%&!iVG)YymQm~c=L*6xF~?oVRh-&3vJqUP$ZCDJGNpJRoyn@AZ{L>IC1I> zG%gxYF}rFN%0g>}doU4-Zcl3LvL+CD^X6r2vU0`pc>m@#v^r`9Er~j!9)A7}F=Y6t z7)P41qeo%eru8O4f%5rej-L>Rs#juxhm&v{WB>pl07*naR8%@a9<>e~!KF8^p=Ncd zBr_wsPy{5&o*$Kdv+UDHG9x{4YvoK_Uq%f|)rf!=8l#v5QvBC#+J?+ovc`x~?kIs6 zOCm~k0J^uxjK2UALF~SK0>+AgeBnVi#=r_!p^{bgD9Pmk+4D;3m8VaY|nw;W3 zhji(Lzwi8ILLhYE8aRk~u{g4*WXY2o6J{&`<*nHpbfg^{*TdV_R8YV)o~37>{%G2w zm7+X-`}S?gBO299TrPQfw7 ztCu;aM8t$RdO)vsf&cEyPJ&3Ol7J*WNil!zM*CG3rK@Uq&#t(A zilB~i^^v<#Uu zXGYbEWmype`%$bs*I@aIwMdsCJ<63TX6495tfLsL9Jv(5M@R7`Ec7#~Rj$CgI9UKo z1j?jLjg@7Jaj!V3bd*m{q5ffED7tGWTtoqp0#DkZL2U_iRQL@l)L4HKZYk5S7%e@W z1#yY+Fzjev9@=M*p=Bu#QL-~}(QMgsVD0)1P^ozt(U=ymXwH}0#Gyz4GRSuC(g|lypJcm~*TauVL_AkFoUc(Go-W)& zE@w1C&?7=8BKMX}2-6Ws(2c-3FtL4&UWCro0}b|Efl9;U#i&pNS-LJQn})wv&gYGX z=tGjAP5-D}-LZS`ekNi>>0Qn%z)ENulLS(>d<9IIKGS&)A_!Qyd?{v4oB9o>$EzUV%qC_}+{4}druVg$>LJ}h8T|0MT zB!$@+u~~wGt6pR^saBOLg;L3qvM@E7Lm1E7|J+3c-oK4VxEjp7q;6BDV3sNYC^BG5 z)Z=Q9UXDHf#S!}IiN*40KcmJ@09meLgKHRn&@O?EKU z7G~aNE!(1Z-~Nh-NJu_n$RO;Z#_>iwjY#BGQu>EoT?Y9p@Dz;w3t$~Fa**uY+Mpry@(bkPvQ8B zXGSJ&9g3w$ixM*f@hEv3!^yMMf_HDQ*{_I2$s2m8NYN6Qw_pJ?5_~TLA=uZe-@t@} zMWL>#O({*9)T~ehg||B$xJB3|0KZP1iq*@PT3E4?r%Z|2E7mDnu>_!R`?h#+?~Z*d zmbs{sGHqJST(U+{!6vlq)1eg}1>a+er#whuqK8>rzW5xA9ES(Oq&k4tafB~zE3AKjqLm8Nw;~}99Xkn?nl}>-@T-lW zpt}qN_CyPsn=6o z$#NB`iieps+h9q*76^O%6r%VAf-0)f4ehIY?|x|3obnpJ&4?=m(%9b*w{P9Bh{Y;Z zp)$G+9IVWs1Od(K)I`{4vcft@Aab13hP}s4#3pN?r^8vZd5JA@9=jNnAT)x!;kkDq~ud+ZmWke6#OA7st?t;mr(rz5tRC_ZRZFu_q0r^}Fv zemr>+)k#^tBrBOb1PP@N$rYLm8&9+r<+5Z&~z2NxSc>{LYcfs zh2r$saql69UcQV^k(A?MxpI28Z~p;k+>|be=d2aCzZJ`t#=Ey~EW+Kp_8ExM_9}w%8wg**!qKdDWs_Jni&!kngeiV|{kDEPlK3Q1 z*rmN_nzexrT|$oT)sg3HnRzLzanh8T$dtvN7%Lwf z$4mJf6DCZEWXZ^X`t-_ zR!pBhy-3MLhDciJ@tF&kB6UWE{?icg?9pR%Z`0VM8Hrr9;n2~ONR&9S;*ROt+qZ4Q z=;4OuK8APXSgV9y-Z*gdBwSpGdl)HY(tmH{R}VqKe@8j%q`&h%5JWh>!D1@aX3L!6^x z*-}jOHN@I6Kxi?0nN)nx4)h6w9tYn=ua0d@v}`(W!m$Sr9&o`gbLPw<#ViqjAUC09%B<<4Ma1(=fWXb$kunuG>wZh(pzp<` zC*62&L=*)J7RKV`D-~-eU%hgfSulCNkI{t*N*6!Oklg+daQWOH7(aA?NhCB8h=+#< zf`WoLf#l4YQ{-VD##%Pes8xH^YuwZ>fzUfTy$-GF*Fq@8B1wZp5=tp0q^|2%-yxa& z!}}1FD@}>^QJH`I@byTT&>IJioq&tRxJ@zt7%iK%W7O->s6k!)edmrDTsVPj-nJ7N zGAVIH6Y^lBdGNpi4C>#DMS}41JY0z4<#ZyksCZdaOp_ecphi?_Aei;7u%uU#D~O8J zcpITZ=Rc0)_vazd3z1S45_59k{sLwwU^N8NV(H4&@JW(Ps+jK-&Bw^YOi3^P3o8K{ zQ$vo5pnuO?xC)KNcHua>5Xk>Ae#}UVI?iN)U9ovLuLW&yae{!&t5;(E^2Hp-jSK}O z?%lk3lmDf1<;q*HU%y_*Kp-rjPLIOH%AnuKF$$$exp>gEbu+wRHCj25$(yC9*)bC) zI|xG&rHlQ-ZYLi4&R^5(wHP{lEb96DE6#KM>J_wV-i()$l%AG>bn^6B5bzu}EK%XP zw{KCkQaKB6Ia?P*)JmHcvx}C1S}E#TLU6t$GJ|Na|H(u2ICBPJT7ox;xYjCqa8i`t zYTTp+i!)Pe9IyVN2()NCpw&jAM3F+Eo`H^-=AHS3TCKskKd;zDZu;iX0e!J&&u+8t zi%6C#E#@s>W1`jz;nI)Gotjl%E8sDz{GNS!(jw(r^Ra3I>;*|V^85fvARLgOS!lVkUu{j6+_k|;r@ z<~B{6;_8(@P39!2>FA&}P+@H<4q-lM9VuL0aiUfoxVh+{(i3NM%!>3` zt3$8z=dth6W7eCTvL#(?b?~CF6^%Pel`V&U{re+%ij+JkQYp^K(M?A4InwGTjvb9n z8>u~W*UaZJ#`!y^G!Z{MS^eQtg^ymuFCMq+MGq1sr-PMrZ2Mq{FhodvUl z7xkDibt<-O-iYwaRR92Ww zY4C6GJyG@b8Pld%5Vu}MmL)grE>{Y^NhrvU=Cww6QrkL0W)^M|f#2VJM5hzS@s7Gj z5wu9bWa(bS@u<29eVJ=;WT9%`p(B1NT8#IIWN*YAGAhBs{_g_2Y{rfqi~W0c;7eGT zi6zMSxdm28ynLm~7&&^Z)B?kDuS}#T9MWo0n&tb1nte({`=0$!saj1%ND(xf)T)Xv zUp`CXGXEaosC@zp1+X*Eboz3<1}pzXKhau)%N_+L)5BK#p;$P&tHs;q-(NE$@Ycv@qGwd`PbnJ zzEQs>s{~>kZo-~dkqRkb%*?sSo->cl4pU4*CKzdT2&hpJdMhchLq?23oq7!<0#T~0 z!MThabV!xZmDr>m>g5DRG|bBGa1W_{E+#@3sfB;t+IaZzz5zL9_Djc_(JLMfRj$F? zE89_MSiLGyi#s9VXnFDwo_>j7O>Ar;(LXU>>C$JyocRloDpjgDNf zvY4?+n~tc}Fu-mXBbhCAZP^Gfo<6lu+KLt{4k|svtW%1NFS;Q9%J-OIg8yg6q={I+ zcC`s1X`zEgZ~?W-JW{O|(s+6>!N@9iqf1anVV*inzxfyDUBAf$Fl)5Lkbw#WsWoWS zv<15N>diYWeM6tdS3>TazfQ>AwL?3cIb&G|ELqBwn7d-V;+p8f+`fJ-)-I#E3#P-< z(-XIE-)6^UIJ0u)${oLR=Z905+ zT}#7zcg3yilu2khvSrJ5;>eLBrA-Kg{)e@wUeZ4nZ`{hNZrJOSPDFC${5gyn+{fas zq{Zyne+Vg<1tzX2nWjLImHX;dD&a%OTP9MnMmEx8lrCE!SyBX+D+QIvm2!b9lt^i^ z@@zEE`k-J8x^#&ZSD;*TwN8XiB{v~3{cg4DH8FhDXsB5YViEzrLZnngi+{lb0`UCV zGgPl!(Tep<#PIRckhfqVwozNUo802AZT*^73KoJ@Gm#->a{XB&5OVk}*{~g6-rkD# z?(neBXi}%TMRjr#Bl->)fPf|~9QHwc>!YOGr+?o*I7}Q+QWTlqCIT9f;t&O#uUQX? zTqwkza~Gz65oe+Qq89Ke@-teWK96%RD7(^(0FC@0Bt211{N$-qQM7o;IIU}Z>vK^0 z9)Wt@>gAX=d9sE7gU)Tu*1d3zr_i8P5b^#^2-*bHwQ8caNgz}}P8OG<7OKa9k>gRQ zIK{ju-~L@%Hpa^*PfY6+=|xbl!VO!j)Ob>Y$T#}^_|YTi)3ck&*Hn5DdJ0>I77L4& zM76Xjr5GvMaYxyJA~QQ4ef|Pn&YWeYX_i4~Wyp#?)f=8XZ93v5@M5BuTm|tPZS{x$ z422^F-@R+5)QL!*DlO(NTgy@e>qEshhL*+2x#3=Y7}jIu_P5uA3F+XPf}5jC?Sf@ zg1YNoxpKuifzXteq3?Qlc_+oPz%9zjaa4`&eh_-KZDnDyA!HmiW*n;4sHG^XeY105 z_Fp|(w`zeae_pU$mngu~!vz;>)J8nnJTHJdsst6zy?%x6C(htG)$6ipr{V^wT)FdL z#;n=!Nt(i>`pY-l>%ZVbgur`u?ZEJ%mJ#3NTAlUVavpuIAnA}E+^(S?Lf*Z$nul%- zDhm^gd7WMQ;76$$XwI^5?xCtV4SW;kv-{d?6 z6*jD0g((v$tijSpLSgJ%%T_?aWIiV0=U@KOOU7RM6Kn50fL0SJrPW#wtRuGFwMTE3 z4$I894un5^lfD07AEC+!)PR)AL;ZcnGO5lpVL~k5xP$x86bwOcU%y1h#{R6@0;@-6 zG>r8R`m-W|kTgdb4{f{kMWvdx6~M|0hGPEI3C!F}$RU@*q!;ObA3Syv3B3|a%8{~8 z6VvA?TQ=yDdHnbh>esCWy*9$=(&1GPsjnX;KaDQ%6I5iulX{WddH(^ej~~IKP%U&q zBvjOr*6cv!@Ior$JZIiQq|cDSA^k0;_xf*|kG$uf-iI*rt{g!ui?H->*~(SXr7!6e zw);BDXTNX97A%`H)2NCY7l-9ETe}>jaN)vlpFDZuZ62YSK65rqolk}2><#i(3wUxr z7`;2Twzw_Hyha^XTet1-U1N$^Jdaij|9W-s;Ql?6wJ@ucN{_Uj@o}_j6}YI#zRtU( z%)53IQ~&ylaT7Azl24bOHvy;=Zx+y`IePW!%lb^j#ZZwSjdU= zdT8`6I9$CFG9;j~J$k$d)1vM1qqzG1BdaP++=uuEd)KTggSWRgX3U6DeZO?Y@XTGXw^TF0^OX;O-T zar~8C4njX#^M}yidkh+aVr45yMa%5d>ADq5v31o7ZgP`1#MUli>8f?e=RlTWOdha- z9skpZ`{wm4XwkHhY0(7&uBd}b)L>BloM@Mgno#Pn@!lg0yLcAiRy^jiKA~2tpx240 zSfwgPjU5kHsucL&h*1b!xpEn8nl~{mu0R64Ucmp_yAt@At}gzc$tsqJ5L;2zs!EAc zMO*v66+vT5L=-J*t)=!QvG05P*=pAiTeVsxr7gwR)|N_bAJq~fBuHeNnXl*GcPDRV z-prdxCK1*5`}qlZ@2=@CM+g@4m&N8PgSCGrxb(ONvxp zty(q1{rgnT!1SqDxiaQ1TPMIwZy&n+r|`QGA?9jGT^3nM?mpH*wLgVQZh3!@WoA`cLh9VZDQK?#!Bt%EE(6R|!v75B$)&HO}0N!_Z^D;t^7z&D0=M%c~8VKXfY9t@jpY z&s%_k1q(BsmRa)hX*kW(#vLO6=XP8i+O=*bRSlNq#Qa_U;N|Ky5V2s1nq`;mNm_C; z!n$-orb5!3lJu}DL4=xR9(GJb*6+h%Xw^PY)s#lWa^dV5Od9#2B3@1NR&6kI_B@tE zMUI=ic#cFzW8|>GmR+%eH9E|$^A?WYio@ zIyF|ZbXhFlK;gtH?tIeVb$ayg)s>Y&vp9XV8hmr;2#OW+&4cq+gGUdN(7I()JWess zK;!2`v4ZYEh7O5oRD>i0=^7Wf%93clmWceyqq=iWhhbtpJ3*_ybITEx_ z%chMKlif(I`gGIh%u=YT)l!*`kRZgzWufSc5f&>}3}?@tRpc<6Y)4K*xgUe9HZB@q z@#;+|S+1Of8eu!%@7n|Wv1-9=RyBzvMV9&4fR2G(F=5JdMXQ5zmX}YDF5e&Y>x*Ab zowTx$1{m}%fNLhSgxkYB>^hb-Mjsf_F*L3iJY*bWjc6Y}gdT$4!v36m5&P%#3vO?%EL< z$;oEOi)oeoZKC&E98kBvmES9Y#r(eZq!s$nM zvU8`;V8oC?NK8s%HR+qTXo=}FX0eEXyoezqL>ZDE9FkPFI|c(h3Kqut$XzVP&)P$u zu(WNh1`8vmoKd`D)?bu_pFg;;32 zHH2oSk{Arw8@UZz{y_>-PB}+cIn7!T$(@fTqd)g>P&gBa_1g_T`yX?9MuDAb=-Dp*UP$~j6 z*uQ-%HmqKy_?j*CN>#<87b;h-Y#8KI42H3wN66;niQxrD- z)vDvOjaxzGG@Ru_poLzsYzbC>vWoMDy$m8DCN?AZnd#Hu=}E~qc7AMSr^MQks*uwI zfaXQ2X>LD9zKh040yH4RB3T?JJ9zZKTlE`Y$XRJ#Ma^z4nMhzdt%Rrbt zgm3W4_8suldO%0Tc$>o#Mh9Yc z4w_sCbLTHY(PBPUVUE=1Y5x19q(ro7-UJi_)hoHmo{(bS+ z^{bX+F#w&qzKih_d3geJmCh03nlf=5zSz4*^%G&9b#-;am~rD-byhl&OwG}%&DfNl zn>TF0f;qETz^5}$dt&O3Mvp^};M^&k!wJ@an3x!Zg@hm@Bh}(OXZx+uqTZWtW684R zAWW*9NLpIRi0MBkTtEG0It5^vOSFK@IG`aghX#>dm`rqR}V8+C8EOt;XXw)oM z^7iaz5cALzZiUjor(|iYT)!DEu2i$bwnv=Kft!?Z_o z(`vZq`{eN-G3fn%w(kuxz_%AGzl>$eR-#17GF+}nkqkLuQ27GSoi!7mZ{2JWc_2=) zY!j40hp>JFG4R77IpAO$ye%Dg6qz0STMYX4>5YuE6zL$$I>5Lo6?miGn=JQ*Iqm-T zaH|3InGm7WS4_-#g!b&Fa7Y=TadE-oRclfD`4`xo*XI6EdpaEkgmlOKJG@k$dHROh zXj>C$1vl0FESBiIE@=kwo;2S9- z*6!gk=Mmu~wQ11=WK^&w(ZslEBL{pi2x0H_XCcqImElO)ircqtwxC2ciK|4qM!$^o z%g=Z2Mx_@kJ5@4SW0?!T#h_2`5M-vOD%|O4V_ot38+EX3#Y%>&>*)MQ1#RD^CGIJx z#dP{pLKKx%V~3^+Sr9E8KN5`v5tEG)O)v;ak2-bg%#4bPvdr66n7sWIXf+Jpz;Zm+ zY>9+Mp>~IPpCn{5(o@jC=etOL_|Re@>GWySz5~K%%yvv%8w~hhKtG)P>6l8+y9TAo zJcp$#R-$6XifniS<}^2sVrvYN6A4A*{b2A=g!Su}4N05*S;k(tfY6X$j7iKN#tbVi`IM8q3T?-yU7@Ffl=qo9vYy zj5ZuuuC86XPC9=4_*Bzxc&J6lfDh3wP^#ZSp#WCZEw00yK6xAw6URz+c3F!gJ~bN5 znlm3{YPCmH)5gT#jzgOk%}ipWOio-$%y#Y8gB86bsL!m&j$_G@g_6Ci#2|DClr38x zGw04TI|PKLoV6|T4Pv7t{N8fqIG}hQ$GDJ+9D5-KVZB3`x@D|eVpL)HIvzL}jpBYK zRVAB{G6%mtfH9*+NJuYPGv;cj2IIo#;I-Oy*{a&YH5NMg7dy9O!>211LgO{4SFhgW z$jHcP_HL&`3_^{TFJImuuSUx64}hz?7uIas!3xySrHCvXwjNr_dDAE1z2%( zjvo(w3!S9F$it*W%?}5*&oLECH2nPY&$Fty=6W=p9t%JH3>7L?RCMyl2gWPA>Tx;t zA|{M@pZ%yl}w0r-^P-apKv?46;m?vzEaJ)s3^>wKA8o#(L+g0(`L*3CWj=AM4Dv!{?A-Hos#x+l zran$V^x?yxD#`Wh*GGwxboU^#A`L=$&FQy`7A?ev^{W{!m7Ipyp{vz0l*FLkeV7wk zUa!(BVf`JLI=F8L?%cQrgA5&0NDzCx=1Og6n?d~j{S8TG;BY*!SC5eXAEG@u-E65? zGHF0JmWjiM;8M&vD@&?n2^y({|%AY#E%mQ6?xv-zkv#rE#l zj*Xu#gTY{FviJ7(CWmLXXgymW2}+W~1Iek2E<-CeY(>!$rC5TM+T@6^(~=+IgWlbd zOf?^v&Xm<6Q?InHSiF1%tJg=Hkyn@OoN|p4gOEfk=Ih7IwCZ@SrX!$v*Q)(GD*X*= zN2jaW+U9ip&iI~3Q4m~`0Vj?h!;ry)SlK?gTE$gpz^iZ(toUpzL%&yhSm2}qxE~*f zfx+Drvks}x?A$#>-#KECz`(#6Cr_UISTrW?B+L}~!i$){{8Q-ZbWxcpNdP_cxFG?~ zxft}&vEr8e5kx@AF>BVXN44rTR5eny#m%eVE2~&p`4wu>2l8=dAysP8vQ1mem^J6A zU=St+09GxSJo38C472+y9eBKO>HJUDqTKV9xaws^pD@xJwX4@-)Sv;l#(>}~pEurk zV@_05)R^qa`p2 zTCG~Dd-SAU5R!vL!==~@=pEbxndxMHg0mV_}#3P;SghPeuXU@RqTQ>68Wv8rmRvc7tNGOJm9F^ zZ^+se(yJ#j%sudoIke0I`EKtpbnVsK?)W>pbKbmu4a53}u}WU@njnHfvU@R?&(|qi z*1UQ1FE3oU&|2QG$iXO9+!rg>Z-I+P0ah%=7IG?=BqTNTSg~*}4uAC(G_3rn<$2=f z?tyjdH=v6Dt5%ctv%9fQ`%VKyMee}N@F~(1)J&C*^&Hf_Cq|96E2PM=hMn>@H>SkK zoJXI~U}U5dK)7kpAg?~GMU&QT@WHT=1PUuj59{m1LsDI!OHW~U*82&(T%O!nNyD#N zwd%fazWL^zoKD3l3_^pbSh1pk@-UQ6^f%sWfR84J8>NWtQ=cy^Ez#U5ALG=I$Cwr( zB#E$=FhioN8lX zF#6;1`DeNkfE11v6X=-`2@*o`20c0@|mv&j}y=gkKAXo{c>^{J40MNYtZ(%km=S>mVaD6X72Z$N4j7 z;KHn3S>o3iG6D^H_vy}P3G_T#EBvOwTF?eWL;s}`DOeq<2Z zljTd7VEx+FOjWj!e9|?vT0j8C&s+dkH#csrQbYcbw&e1K7<@c@Ff$W#XQb)VxN+k? z8#iuT@0=a9SI#@o8zC|#BqS&js=KEbmapFkAK#J|b;xaXk)hLJ?xeB!_0%br(NCxs zRJUAQ%=q6LG^pPQGv_aWr@N;V!fsmY+<|H2pI0?t@k;-@dc|T*gSz#=sC=MeiBR{{ zv~-LbF&M{x_<^ZW;wU3Brh1PIJn(NvVa?a;V9d0c4uMaQ6*Mv74hDt>BR$m;9g`T* z*Vor7LSl9|)Tt6g45C7X3M}H+{9%*uD~09jHp122!+|jq@PY=5A|~MY(I0GFI1C;;XWjJu@v8gTg{^FFsB&OM~v8jz;R*v#GhlAk?w78Y5%`fAQ6743XO&um@_3 zt11QtAhvq>LVUA(51=6i7K6cm^yt1TD57z)_}fdq0i2;v)Z$=R^G7; zlu>ES27}}m73-@n_hZV$@zCoilA6a>WvNy)V;u<7sa3~7gbf-3EnJ}2>Y%YwbH#i` z2+Lsn@DFk2w-~FMm^1Gk^8Ry;L8ucYG!$`(fBmic7&URK;|P6p0gT*+O>0-P4G}o4 zVp3U7N2#FA^azG+m0v6?SnIf0p32oSh#p8Lq~Lk zFtagT#ZQDc*=U}Yf;8m|Z_Vm24BlNkcEHvDT{eM9qRyTqgHXk%;U5l0l9bk}w6>Jz zuW@n1=!w%%v)-FLY~6vVYBGy|x@;j1?%BhvS4xMRzrX+PufP7fle6~Af>v{@VbShu zuf1ltd-twsSiDpdguLy6-MXP$-+??H*NzlYVcnw3-d{JaW8%mmNKU%X^jn6Xq>u|h zrqZ_UI$+$yN$~I>xJu_b>};(e^>q8@O-vj=7U#~MwJFK6LTa*Fq%|Qwf+({f?<=nu zL~gn#aD^G+)3ATv9(KrCthZD|h?t|Gw-3VSEkT*`6+r%8Yea5#dP;fr8f;#(0(-V^ zlbp+zNG8RL7su(-r*rj?&(+4o#9w;pB?ILPSb9=<_g#7hqiaY%Xqkn|K9*(o3KH|l zj}Sh73}R!>Nk(9$Oh(Q)lt49US_B$5X(E9N6rFqaaXem0x|nbMY#nxQ+YG%zc`e=- zS6GNqs#Nu7)vd~vFYk;swBCjW*tTUeDDBoB@Cio;lUEFK{rWYGA3GWsV$QQ63W=hm zR*~VQbB|yIg@iIA3SWA&N<=m$O7HL3v=-Ype#WE@xkq@$Xx2y0^pvZud7d!{4TJz6 zMBcG^{xL0f=b&x~3L7ASILHj?DzhL-LZ{S~3$d6nX)KbH5};%0yEbT7qd~J4tuT7* zI283QE>-U3?>p^6PsN57&c{Y_n#r=<)HcPQ-xKgdXxIP@89u^!a6S@<$P)OXkkTPe-B^^>+C}Q1+VE#T^|w zcEX?`!{F`X!%fC^D+KDKOn_^_-Gg}coCDS?oA(+iNw9CL@r6hfJ~w>4M5!njWB%tB)k120w0Qpa1i(daPHUtVnO&6BtLp&m3riAo~x@1 znzw9&4~GteU&&HVp*XHj8?b2JJZ#yt-tPOATIbYm_ux>B7&%HI0a@*9>%YHqI}VE% zF2Lb~2au`L8v{RWS^uak+wcifQSG%_&}!)-?ZRT_xOd$?2||931>sY1@_Q-xP|Gcq za^lQu$stzhi7*ItE_@{Pv-t+lzhA8CkEyfg!`;J+nFg%|LE0jRwA9Dg@X1OX{Bj>> z9Azk`_bG2G#q&4^{=sy5QOlj1!q1#Ji=~Se;OuFtL#`H{Bh6okgOPAM&KQX;UmL+lx#cCdXRPY0VNWYLct>2^#pxjyhyoJn z)ORmX023qTph~p>Sym#wtzJ$*X1ij+k*7}_!-@s-k$9hSGbn+C=VB=>0(WK-GjaIK zm#Vyq&^~?8uwf$!fR2q=$`m9g7RQogXEKA#G-YOi#$uzSqv|S(Z{>=M=t~$qjO;7< zl(=t*;{rT>oPwiAzQg*p>v8$_80eTp71+e)>X48WQrfe@fFi|wF=+Tm)T;9q_jX9B zvFb#`_tFSe3Ly6ESxlcW8X2i6+|MDi?+Y4uQfifaBh=@nx@|ds!5R>qXqiHGg8RIW zjzJWvz^hwvL=XG@7{a5iGw5(={}5`{T4|(+E+$OP8*w->?x%mM+Bz zkZkvyN%9uCQAi|V%4TFO2?A27;^J=N=#gmb+OZRtFI{3Pl#-D|Nm8+kwm}Eag5K!e zw;!5P$z>N8hfUTZ$%(h8$KKDkW8)eE1GL;fF*ZVo&koRA&Z*I~g?zyvG$c_AQE7r? zp`u)n>b2`&%%rJsE8r!CQ`>h5G(8cxN|2hKibMPMV%z3TNPYCcZf4_z$m5IXC-0&@ zs9vo)TD5J5fPfk(UAi<&a&nrf*nu6VKI3lP!kIH?@YVjkIDhUuk`nJr0iArU?Dog} zoljq&kT<&b3PFpu?Vxq{;P*EN4ka;kCe<_=WIj&8%xRNwmes4bekj>fJJ5k;$A8*vD%RroqmneavMT;77td#Qdl~gZ_ z$ODO}ND3q--pAd$3Al1G7AH@f!1#&bWzapeFB&v% z!eXhob(*KjI(j5=GXQWc_5#8ujKaeQ52agR7U{{YiGF^5_~n;h@(o?)n}LW;ZQHi( z>N97~^tHCh{N!;#=k7fa6dVS)P_Y)K_~+=PFlh9+67w52tzV0nGrveDCAVIwmHV5Qj8m1e3ius~gv0@;kKOzLb5+zW$s1J%1E{p13tLS{*F;vfhJ2>5LO{{7^F&o@uLV-T@2N|kl#cSNi%q|0i+tSIBd zXTq;^*=$>exW$YgjVzs4qe05U2ROKYKfcYqurt;n#{^`p0m@7y6nP>7Yw7{n$h-CmMW=2(rTTb8Kn57JuG!L; zM~{Y5putNH(ETej6T7xoc3@)EwQAK$yM6n%dzSfENtH6d$FCI2J0QeGehB8QwmBz#4z$3~#i0kCh~FJL zh@*$U#l84D&`}1R1lyI}lRUS!mNN*KMp_p)_!jp=!)DFUpm8%Mc}cM%WJCM?-PT8H zR70V~rHe6`A3hCt<8CQN*2wp~7J-xxo+%eM-f_06DmdBCP)hXxySD zb8zLxBZ!doDMi_m@tM3NnHd=j*DK*pJZ|2&&MvF*cj9sPP8<@G5|Nmgh_pwKnB_Gcz~UJ~A~^ zDen!RuEOKwBvwk>nzc{tRjpdJmh9QHXJ~HhLvFSC)G>%yh)R_zF=>(&_{OKg6Lf^( zlVfOk7xP8vfDiC?qo$;gGrgO%4Su)Ku*}4qhPZR02Qb5geAneh0CB&NJ{Hz4Kn#tePvxKm-Sq0!>%Hjc=>OPrQqqPM@hOg;p?#P{ z(Ykwb&|`<=b93vg%Af7B%RHSX85ubA^;g)uehu@@*lVVkJVcZNs7LJY*r2{(H6)qbkpAeB*vwB(1_ zyK@Kj?b?adWt%xyr0yjaeE1cQi$5#z9?IlcKO{S zzXTaNYq|bW3?dfAnipPa(P#mPDSUkW&@Qkunzn3>!bOX5%O~MFSt&j3tPOZl?U*t> zu?6tp!F_!F#XfwqZx0gU<9O_todcwGaz?=#JG;xX*Zz?VB4-pKO_WwtT#8xo6+J0m zJj|j@x$b-#?Kt#FGA_8N^(oII1d1vG5Z9CaHsK$+*tN#J$@cwfQ=enR2x*7!m` zPYF(sTcAiH>h%n0`q+^pIC|(H;{N=@q`sfEgT(xPNB{mOGsZK;AY!tF%|-gOy}249 zBazIPIvC(pxF}wJ#UBluG)2{F0Su|0p#_pPOhAg}1C@+P2N5b`FfhMM?71_DKJ+av z#{Pzshe^CVw^U!=k!aw&#H7lGV08K?&x8aqU$uaM00V`Mt1}AELg6u4@qv+9|6XRMBftWsyroO$Nx<(ZqX zoC3_f@{X!t^0xS8UjtWnca$vUhf<}V!wWA}W`8KluwaoQDBx9)VFMGCrzko}YLDDO z$l5d}pG)EHWZH|(+nbCk=nIuKe&1GCW9`q$MI;snhYM3(Kfl(C|_P!z@*qD z78uNz+tVLU)wKWs0d+}4K~$?C3VIa;rIr$`k(;|4TwPp^6_<2+9W$*`{dt1LAzb6+ zlw_o)B=hKDVSr|tj2wA_^{r)YJuaQH%OL>DlqqxnMv#R znTLJ|JS#>2id6T56R(^)vzAycp^V|^c2G`MO%Wc#Z}eizIDx;zgCUg?lZ4eu8~}1j zLBGpysjhY`OP;*)FN3HobHj!WPu#e1;|+rS7mu0)GEa8Sflh?vl4l^gySo!s^2sAd zj?{6Wsb}uhe;LHVEr0#>*HaP_5(=;g+^6Uv4|j3&nCN|gn>ckdpV z!zn(~pa07sxwQR(fq}CxUc5M(Lh^}mNWGyyO6r+4qkx+vIqP5lrw=drJz2R_*8{J* xo3moY3d5ZyQ`2|dte1Q;^WMLD_4V7B{PyA07*naRCr$PU3XlR$J5{Ex#v0@$I*KSsiKG|ih>1ejK&yaVxlJYu2``58heX1 zmLwWuVl;M9#DXA*3eu(b-mjPE{XE3;$Eycig}dXPfB1u)+1c6o-t6qo&O$)C2>?I=0FWU7AQS*D0H7WMP$Psx>&pe4 z;>gnK93mc<5C<9(pA=LVr8G3u0sv5T7@Q`{0BN%D8Z0~RYfDSZ0R~26dei7bY{(>E z0t}e}6vBX@=$xPcKuwiAZf+Hh^XPGP#ogZuE8|ic8fr9HU!5kpqLa~Sf@@mG&dz{1 z7P9vpNjLkhhaP<>0Ki2FU}#qjForiA+EBz{$46GzT-sWge51SxRJ2Q|YtXC#9Y9oU z2veMGoF*Are&a*bpGwBz96D3y*klVJ&`^o;A`Ta4?K+lO_zD0ZRIv&)n!OGnDw@8H zPELa+np-cjB9cQ<07xQKeZm23(oq0}Y?0{g`J()tPax5?J6Ty+T%-Fw(tr*i$~6hx zc2BoalNeTO=m@?S1OTi)uxNFlC<_+8{k^Po<%*P)C;$LQxhm0YZaRP{Vg5F51hC#6 zufgBbEPlcZVJ`>;I1K>C{~I|1Adrp-A1Cp6tA2d^I7SI-(FztifG9SN&?;AZw^_FK z`wc{-P=KPaV(dDX8ipVs0w^n!-E?&6yNm}{4E*(H*u~h?L_}V9Exjmk|7zbdoP>pt`WEqCEZJT$y zARyua!87;Bhm&gnD3q2~TiWRWBIQhmJ39JLx3t@BO(F-gg9slN=Wr#viY-MNyQ%9D~t zqnqgf($4h7jS5+3&%rMuqp*=c%a>HrSTY2F@SdplyT5oP*RI4Av8AEW{ZwtO%|?R)ZVJ2c+kP*eYxOp;4j>;{(+W2?eJ?z5{%0l@+f{97n#>;u zAizV=>Kc?c)AhlltC~EPwq(}G1aEqZk9L`j8o6zo4C6o{pB-Dk z;jS4R_dHT=Mrur`1IYg-2f5m#*O!CnMwk9uLFhhdG@ysWfh0Eb&-wY;YhsFuvZSHW zq&7N$yq~SC!NJ2BFyR6sfK7xtct~x-Sn`RN|@%CJ)+41;PBKR;8z+BNv;^PiQ$Q(buI00MyV%RWKtef0DfFuT|Z zxiXEAg2RDI9yikZ$-@Z%0Ij+I-sJ)Eu3yL!eFWJ|#o_eS@*og`cmPG4-iJkG2!cqc z8m84F5CoV)VM8g8bEZek)1}G;UuW{_(g4}{!|3i4N(8%Y`EY1cCoZk9b-WOQ*erqI zT@H`^Bmp!%Y7(F+cN7`89ATq;$kg4$?DuVNe1c27r z6$Y@SR=q%wHmf#e+`maB8J%g^%IdpI1EeHm&@nQE%!W}I5_Ljgj-4gcK%5sZc}*uT z7FOQ5nx9`L&j#8MM?-_r#+H3TNQ@b_FnJ7sAiS~+L>4~HXCn~GX~c>Cb%=)afGSqCQBm8jotCyLy!k?u3W7w-?8Owda4rdT0n!X^cibix-a*Es55W? zFgkj>8V|vMEfSW`%zYd3Pkw2dx*EQ;a&=jNR1OTiLBzqMv1%h7VMOmMiSQdCVce>e zx663tl5`C>@JkCeJ3KsE(sR zxNmjW)0>E0_ENRS#Hu+80m{A2DxS*}5PzMOkihSZaVw+GE(efd40F#5-rhI$aELum z#ralL91i5M+4oNs7p?p$@7)_^Oqjepob~ifE_rlcwY9uxpn1cTJPI$^(F z2@mS!mAkX)x*S06_6*qHL!UOCqT+K8Jp=(sO$|4HFDY2RFF*fvXHPWNe-Ax9lihBf z3xnuJYgN55AvUaI`S09T=j3Fn!}#Tqp{oG|E*}`0N`N7^_d&BfI%}7P1i_#J$BVBe z2v+*udiC~c;c$S686(8(B2IBS6;1*u98n7Ej6 zmGRm4xFW&#-Jd_#$cpk^1|To}{P49n{JtJk+6+7d6IE4A8p}z#U0XAETte)50D!vw zYC{?H<0lFaQ))ZFAyb82nRR8v5OvHxE1VtV3S=SmEYoxys7!4L~6i zd3m%)hvjEQ)m5yI5pKamvp!vO}_GVR%|6__Q=5iiE zT+z242~SJqVA0_u*s`;R0#^AUMyn72lT?w9hNYfFGi$DwaJh@8y?K+@;e%AL!!7}k zpL~3%))Zp+FfwB`tfDtNu*&FvR2F=7jh*`Moh*$hg#(SSVp#p|>i-sjAi637_U4N5 zBJ`aI0^^Jx-Ahr>UOSw;O8_LLZ(oKXp86{phZ~`aC(xSnjbOC$VM(>`__PF#@E$EJ z4Jscz`~g9P4uAkald zA^=41n5-y1UNp9UbabPFbJF4DmwbFz4`P_Cpuji-Ra_dQAqYriHV*TC{^Wj#4^F`j zm2EEyilZ9x$Gv+ybf+1nqe4JXb`Q;YN2rK@i&eUS*d#prl?K+Vo#f@3HWfmw zRFQsz0pxyF4d3#T^w0w=C%HQ+H*4P3R<;+S?3jM3J z^tq-PzxJEqS9YL|Wi>zL z)$ID0TfYC`i`;T)jZ+=kX+1zjjBqAS;Tw+{Ei(LD73D-RgW{p0CSwyi9lCNwV~c8K z_Z#$(h9DsX91JNt4F@YxnkQ;FQHf)34|NQbM>E6Z`}w=Tx!2 z76T+1@C`$vqBJg*LTo5G{X-tn5d1(qtQvsivsvc~8<|^%yp7M1);QInoz?^7sE3E1 z2gP8jKh5Bp)}RUR3#`wx0SN08oFHnOspSj`@U%|2V+#N0A4 zDK1wY`8tb6>j84Y(a*ru5I?(@0ez<`gY@v6bmx=#`I>e}_`&XK1`mCWpg5HT=@>wC z9;CA%-cG-m2l3L^pW;U~JfbQOf^GB8O-&BDTwWZnq1(K)>foWV1Q_S8iXRc32T@aS zU}7zqU`-xXZ}CMRfzfX&0my$R>AZXVy4mS9jlXZyz=Oh`Mtbgdy#w!2aX1@Q?DRFi zBLX)aErHm(2Hua?sHdd0eu)JDAZ+(`AGyfH{vTDG5{pw!vN)^=9xR%miK!+Ry?j>< zHa1zQs#FsSj04F~?SW)b^`0q_nZ;`Sd-{oJT>&8Y#deONTbNt9>V0vviic%Y zJv1;`5tI)de3u}=4T1rTS4GM@c2IbxxNzgF)TE;T0FA1X)fE7O+w9;PxX8iriL&?i zTmIZ&2Zn!FmwvW~pLjnt6==*G;(<tU~AuV}(e%a#+@B>?iUN1$B~hJGd_QhqjXOJ!muMz5Ba4w{mZ7^Q-Y4W$Ea zk*R~(T6gDH2n<`O;+~9HU<@EH>l;P~#67*G^g*bYVV3|%Y0wB09>j~I;Sf7j8Uj|d zAy>e;R|RwDheXAw^@En!ElUd&nK#PVWTz^NHi(y4RRUgrvqurpDz;~(v+oiBDeOCp z0TAI;Jp?yEl>otJ2P^v%RagDvu$M1>2S9zV-d+pn;So$V#jXG-{f^AXOB4}6t05E} zVi*3!Y7STRbc)>9{>V{`h=HEBAD!d&9ig7tg5OeEq|Rb zFOtS=x}R8IzivuuVw^m`T&kkk=wWZ(k7^bcWMH%mtMsnwj{w6UqPp@zMpNCIm04Lu zs_swO{JR7|uw~UoZx@F}#*XO_0C;65<4KwnJs1bFQPy8~;HvdY9we*9OH01Nbow#W zdrl8TZ@Vh%^0w3m35LOw`kGnSSvePvrB-&{7B7v)urMW!>_hgRX9*eD69@#TfLmVp z7N7G^L2CK$91aJ&?DommRR983IyxGJ+qonG0BlqphhSR96ide8KzdWt-vMvpmUB3q z&dy?`KJ68_*jrzJD<)q@{77K^V1?sOmF5pwl$wea@mBS7EFT*<#n{twa3I-nbNSvK zzHNCIWaXTz(Xq+nKW@`y0P-j}m}G|_59mT%A6P}j<0MT05rRRbh!^uat|Dyp!?!WY zUIIwNuU`*i%M<#Z8*Ff-nHVrS3LMQp6fnelTwCk^`KuS$k5fl{1ao)mAucr2CBu+` z18!{pLh4Njuf-fZ1Cp*GQ#C>e{bm}*eVY}0kK+tKjic*&-;^Hzrc0IaqpndLCZg?YE4r z>^0(_Q$LdB)v-m`s+ZSZJ_`_gyDkF|?8j`Dr-RQnGuKE0fEcRYz4>^8u$zYh7~++Z zaFO$)qvu4`)v3C`gI?mG;0+dlFpUggBUPlaw`YWyalPW#?yh1Bw||w8p!8R!h$b`_ zPjic~p?vSJqE0L_Y)>9(|DQ4Svw?NFbTw2uHrvg8+GIqV%|!5r;uz-wz4rcoic>$za-)KB4Z_r{DBp1@$!dn3>wB(Mx{1s8bAof-MS6y zM?lstBkT8G#^+uuw6glPj zRTdq~(>7?%{>qEp^F2x~B~+<{65AUCOt*39?`_Ih{TT#>^u^&n1YMB=KJ$d7g?-75 z8&aoG&vkK}wARXLzX6Pxsp5V}dxMem|K>?RL2WUg{W^(Le?OU3|M);!UY-Sm!4lNe z@bdn9S3~3h00K-*2nCgue<|3 z-Q+9BO0n=T{lcH>zN~!D*6#A?RbCp8XIa8qxyP+4PQNHuTYymMR=z##8LNjw_>lt% zL~3h;9nNg*&P-00WrswsEF?5m4}v{aal=sFN%@ElvvnQ-5K9E1JT3_*zzGE?UI=hl z!X=7AWaxiOOeJ-xJT~u(>G2{etJair>JKl>%Hm4P-Nc$P+~3S)t>IU5`)2iV#Fo&V z^5y=z%v{|70uxP59bC<<77c^>-+xZhH;_g%)7Z>4SyfdBfDDgBzUbHgv>k!+H5G!X zoe{iBGcHYT^S&Hw!-$v9{o@)N-^mhVeY4}7Fgwtx(|{q?yV?MR%&@evF*p7(1cHB@ ztVgz%)iCxmSy^eeMnlqWM@l>imBeqpr`@1%Q|DtwFwu=8QhmCGtYgyT_V2GCqij@^ zzBnqv`w9TkDi}7gGWo*s;#KUldpd!P@&gRvq7>aup1T6i^Xh}G5NqN2uF(6Dv%qu8I`Uq^0N;^2+Ji12RhUEZ!E zgWGM~0S!6>AS5#z`*D4Z4Z^|*6hCE-@kcguQ)W`qZdodaCk-F8#Y}*oLlr?rY8;Cw zQ%xPh3wv^ng9=MV6}}93kyRsgav~$ZnYNTPrsBrtal{T0w4F{m3LuEFxp`18Gs{hD zNW>sj4g46hDfOxw0 zz`-%n(x|$f(g8vw5?#Eyd954)3BMdhBuljerK~}oWixYbn3{emD?q~iJ^iQYneA}I zkq0*kHMZ`wvec+BpJspvg)c3l?qjP3jH?%ya2)Yl?@!aorWN*biuk}vxg;QGKIpL3NGikckI(OvT!pA zaQM=3_XV}sy%Ni6ZKvvpg( z(yIKEcGx31r2<~eXSwm;Cgs;lefPzBPBU z<75r>AlNwG4G0dm#*=q&bz8o2nHsjW%LgJ}BP=*${~&U%EKC^VyoS0g{#otr?n?s@ zX!Z!YZ!L@D9G}e3%c-u$DwntVnR>gA4S@(d=aVUJvM5LyZ58n#SpSis{R37d|{c{=fPfj z^NkGPJ~#xzHL^g>i*(`NvyN0BE3T|-vvdpH-F-kGB5CIuBB`%xhwwk{io_sdE-695 zQymH9vpO##+pJ+}B`Au5T7mF@!_x;#WT9q!<`p_C+w)T7&+{3w&~-#xi2$)>5IkQT zwK;1*!TC48cG}{I8G-;1h)`B~F?g`ye8skVFB>qRv|5AKz{|_Y13~ueCX&ahdO(VL zkmgtVc?2#kjKDypmwPj^OB6jaw>d$#(w~aHz zpaUCz0+1Es9eb_*2KNU60ZzhpuGT{Uh;k};j}Bbp?)~LX8CGVK-Ky*~8pGScY5OD- z%O8RoYw)TVqcS`QCRc30fKWFIr9Us1g{C9hegH^tFhIOW^pRU<82>>)AaZ-&<6|MQ>`fSkKv69&E$S8L zmzjrhu@d*yZ4JiK*K_V@9=zr|eLYKM3|d(L!DNZ{3v{xikRQn_5EiAsD-iBflgF z4uB>$T9WWi@Y}d;<%ij9cALp+3}5%5{Q-X0aw1hBMJW>?9sm%&ckVW9wvFAt3aCPU zC29yI36BSntcD90inI4!&Mi)qMQpDTUdO26KhKecrlZ?_07&8mGpe4DeBZx-% z)XS^31a1!Kg=lSd3GYhSN#_2jx7FCCnO5%w(4C!o_))2QHj*fVF}xvw%ulo#D_Bs;Lq_mKvQ;%ig}gDRO7hwH>-aC`}w;I0D!tSl%(h5 z({t-2oNz{e0;SyqHf4K|_j199y}ieOZDetR1jB~9Y~TO10>Lmy74Z^Ym6uMLpPHH? zi=pf5>v#LsaX`fDL*X*t9F?UXWwdSepwPjBWOrei=~6c!>R~do9zcL7f@kAIVcXY8i zWlXfPdGKKvJzks}vOYcisjRhjLk9Xpur?nZqHLK|!LQv^6Dk#$T@JUJ?&n0BP1nO& z3zP^HY(O{|fTl+9E+Y*+9<(j%GytHXt$|u}?>Tt{%H1-JOtb&Eor*k2b2o;5h6Wk5 z-nU5*6xe)ts*2gr0G`cv%^^ticSF^o*s`)MXNrnyWD(nx!3dpeGUCdele1~E&~;YZ zb^+2tF#74mW?u~RqpmcfA+AD^a!mwVdhz&BX=aAt{P)Ll_GXr}{Y)^!%WG%|g!^eR zh4P_Rk}5q2riT@yLk7=d;SZWYI8U~+*Pfka0W$O}eha=rBuw5{S}^!zW~S72$i&@D z^&66%wr@joMN3WiJ^;kS!-G&Q5PZ+Cs`|60rl!?jaeDwrUr%5J|1jKOqd5cVh3)Pr zeV`)1%FY)3dG-!_=fOuM*v?n0r*$MdH-|t&>d{R^;)q|^%t&Ke=Xz*rz9K4JOB?;$q@8nlhgclVRwOO%n)(CTt72^%Uj19p!JR&lb^%TCn(c^FcRFr{d z@u|H16At9xmgO4f$Gq)>r;sZm}}SC>tDkEspc0gi?FnK zkzv*FNxAF(Q8MfxoG!hY5Dl?$RViX(%Kq-rm^q`1xWajdCEayw=V%6fYGy|k2 zEwcPvP~Tol?iT&YvYS{Fe@p7S3t~Q#t*xEsTH3Airx|?Tq~+C6uoR0rkuciw(fx4% zfJzw+7B;dn2ywo6^MQRLS! zpXmqi4&7`*rjOR!Od<*WAP7a29nixN;6}V;Z+INvaBBa(hF16HKlUKlSO$gPY`oH7 zy(TyI!C`p}{83<_S2v3OHHr{8X$wS}@0UY>T_@siaeDM{ha7r=%?CJrXSe0tIXP(6 zhD0HNv>Zh^i~VO(Ug3tu#_IGoMhDFuZqqAlB59ow4GbY5K<2di)_CJ_5QxuY{aRE3 zg-_aDSkhWMNdUpxcAx*S;IA^~5A`{j*eggfe;4}nbRSJMj%+JdivR!~Ye_^wRD?uO zr>FgK3@B%jM(?-l(R+p&^)JDf4bcW79S_tk%o&eT(uT%Ey3X#f!mzlq0u z7?YX3jLBrSd1rlUuw}QEU+XQkG6fS61f?k&=VAbf%VhmpEGIxP`E92?PrjP?9DBG? z=HMT@4l~!v-e;Su8y1Wjb;e{Wt559uY-Dxymf^2o8~^~wbW)DVpx@2WHBis^RNdaa z1C%9GXYo-wK*R(2ncb8gQChsDva;rGTZ0?Z!`O1kR|eC3oRE1GlE^|JZIS$sm?{Gx z7)#TsFJInHit?9dLN)>bvwFJsUTI)+4S^stB_|umBa+NC6c`8Ii0Tf%Kr41HjLMR_ z=yZ#Y2t5Y4o+NBJGezdk)sR4C01=P%3Y%5_cXh?`;^IBF` zkoP@7_fhWqXe&<2RMkXYJ$D41$^jxC#62#z{vxYkQ%)1>L|d;w1qa)c`upk+U+TwS zkEaTKAQ<4J3FwvOLB#UbjsJH|R&M5Yc_(4Xu}Snv0!mWY<=CY zB@Mi=AF^J{de+g*+9}X}#dOw?b=M9~(df9NrJgzrAmUDAd3^SHoajh$#;aYRvCTW{ z$jb3HgJ+MWt~R2I0&p-&Y-`EV>_M_vzZNw>;gdGD9i)p73fnXOywMlORF#z6Xs=ys z|6dmD?zIrII&1>tgVnW!5C#ks0AV#5z4@@bdde(0-!V6KF&{OF5O6x~*fNdZr07(D zwCsF^fcNL?l$4bm4hMT9ywz{TAoJeyCeXr+O`uT_UPP62@ThDLA{ORd=(%3?RaMw? z3Uck>{y50pZVhU=%K{=y!2t+_{x$~t84}!@$ zcGY#}&NM>rrh=4I1t!t#sej<=p85t0`Dy|Z9Do21Mw#_Q(H~B?Bc>}bHZZZ*pm%wD zH_03~D$-|1(W(VN#DhJ~=cnDRZJ1h}n;F-Z0w%i~TTC0P|81WhgfNsKGRI;RRe2Ch zjuFfLZ8*+x9$5MOcAHz*?M#|}gkQkCK)T@$zNm9ATw$5H97t4GO%k2%cI$ycHL1o1 z1iUQWy+^4}>2;LxOVf{90YohD-^}`ot2}gBK_>rC09t=rdyJd0{%1j?&&LfSt)oyO z*H@YB-wG?CmGT@Ewx_H{tzv&I<~~EpjWY z69_d5AY%Re6*`0&hmfzPoI1Uwp(qEtvo7_gs0R^uG-ln+#`Br_o3r1=?~%ImRvP|b zW!HC`i}NWx81E_T$$MK~quHOppz&U1#ni9=t9&SP(qRjcXqh^3+J2D@%cy{QKCP!T|+3E;&wt5_mI{9b0(>;v(6ik#Ur53w(q zY(?wd{qMS6KVeq@y5y%-0FkC}oGLts*m7K_KgzzFSKqUyCacZrkWyM2bkpB|um@3p zj|q|3o!>^rCYFU;$LCkRW7eMxc^S8@&6mEU?3_#{TaEVizhCgvHV-LDw6d8FAWELr zL5pwK94{r0Y**xDKf(HYZ6m{@E8UK`5HXX}Yp9J9YEDvKpC+ zNVg}SV}Hk$-&FTm6>)ryMiyLXZ+z1HUk4Crv?NEH;%PCnOB189q86$7aps?3Iy^$=F4C(Xl8cw)wBcWS1Q`4 zrqF8@K-A5Hh>c*(kx1U}FZB;*Bt2fCZieyh7iSX$7Tq#3!1-fa#4uL;B9(nE>NR`E z#>-WilH?`38d;5|`rIoxu-Q}H#-nU`vvq<`MV43?*gqp~Mn%#U@$ z)k-KpvGkJjNn){Z`;ajHF8~y=CG~IPZ27}{{IEk;kN-ebm3*DeSE~S` zt{$Z2B#&Noz41&oA-pgv1xvi_^iL8%FrhHC;PDVBsz4*IMxYy(U&@MUMOeMSz@FEH zI}T#N*Sb%u03xjiX<3`Mvu2br^=a(=q!**)sXL`inOg&hxDV_}Zw?o#udD+XTxVpi z0RS{g>fhega%>36`(WzfwYJKXud{h+6+kq>gS4FRQ3oIJPCeyJE6GX!OTk1!x$USu zfQY>b=2F-^^xgS;4J($Nsc7xV2$J^z*Pkd`&S1sBwc^tOL=C3VZpMS;$2{WHs<$r^ zI$9iQ03wzNyKaIC0l%gc+*xw6dfwHfnqo;?!c-R%-w7@%YCJwqxH~Qe_90) zO}aYx_2d9u2_hfJk39>m$+GmMObtqKYHd+XAk=S!lK1t9;AUI^7)de9Vh z@d^LWsMN|P3A_=Dp4MM4V}_pm^W-$LlrpPbGpz!MrY*4~Swm3Rt*XB&_16_9#~qi` zS`dc4+mLS@31?#W470SF9T6MFLMGQICuA!XAOHXY0)g~0^vSa{-l}=~@+Ce(gk7@o zG_X6v+jr#dKmkJ4XXqoSRXJ1fJlULk_&|U)}Ce_dYCM^9stdV7VwQl@U_=Rmuu8X zyjB538$5_4K)EfrpPpDtyj59U5HI9HWo$gIo+#u~5CoVr80LMuQQ(n9r-w+)x@`|2 zsYe{_Y^{(C6oCJi2q`PDiFgQ9H=xPc#b8JO0})yK=#2JS z1rTlWAj&d^_5gw%cT5^=)oc5Yv;#(DfgcP>R|{E^e3&;uQ6Q{tKyUV1khQF`0c1&P zMoG_R-lS{GY{CWvkd~5&5JM=G=9-pd z$*`0729~Je@hjHe3A;+LJNpusi1tB_iziq2T_5rDY(quHs7hKLKvbz+HQ+6U2l;VOBh9kOg4DC@0Z3oxevn~I))y%`oZU?;k z#56Tf*C!(A0HThwqC}Zw1&FxoTbr!D^Kl}Do6&I|T)zC?o0% zcMD<>a^Oww+aEzCkaXY&uC8(H@9RX^Vr41{<|*J!njZ(nK_IP=cPT27y<_Rg^0$)4 zK%r19M!5UlD%!WJhcshUWgD#mh%QL~)KuE@Am&p8{q3ghiu-EO1=df(8&k{PmSH7T zKPfQxZGbavT!`SCp}movFa#-dM;)ug&*q6Lv&(qsx1BbezT4Fr zKMsZgUYh6eB#i|N;_)yHN-BjhKmXIXZuiad)^1!91_b$Dwegi`s5v!-u{UPZJPEbOGJWL*3W9kPuBfq~g#V}SqI~Se zrNn2aO*~D8kHq&poPKa^hd$J!RREFJgXm(A|H0v757H7SzVm)6x(+2QEqhyhrInv< za>i-rJ*IT%TM`}^2&74fk-+HvJBffV1cfJ_qG7K}vaVja^68c^0040q;1oWqdeEAG zj(tz+2zR1Y0MU66<#~{noQP}Y-g@%vE%q$7#DaL+zqji9?GVy7GebB8d*@zWZ_->H z5(T2FT5$9CsD{Psel5?H1OnsUj;_8UqZ5sL_XVpOBszeoTEm^pzr7g5VtRi++wXR? z`(DUrvraLO;U26gNhxXVsxbb^Zu{x3*2qdj8f>S?ZWpF9?0(kcB>tMKcLnFq#1u(d zb|J=|mLul!`~80J)Ixn#saXdQRcTgD__hy_06*LB_OuHS@o=1%Y)ZeD5fj{6t*^P? zxwwDn2Zi@@1mE)%NLRu7!xA7fi@4E=nSyoS?kmPh5lQ+;0lmD=fxSOZRE5fQ08xcj z)re<%+q@RjWdn$KXpM8H?MyAL*#T;5BrUs;-xpX059>`mM5Y0EMGqK7C@Rb=7M-AX#N+FqIJ*D8QW8>H*DdH-iZ@&W|o5IFB(*?C&Rl$z(c z65njxIM$vX;Esm%_a`g^5Kt(nCL#coF!5PWO2~V^Uz*VBWipTMjytBJgBJdMcnU=s zK+!6I=z?@fL3-O4rW65)xHo+Gyc_R|%NLgyl;=n=N_bKT!~O6q(=$Y($Pa>0MBX6& zzsp%b*uVmhzS>eSKRSae@nIlmBGJjdr{|f*od*Xh3KFdXh|Ys_4nVM8LuVdq_|-CT zXWpaK9jzE8E@G40%%0sut0-h(gY9<7`@t{(JONr8pUyop^HkyXyga~>#LFVcZStgA zk3G>xmzv4jmsSBp=RqU^r0x44%J3j9d$n1*HuY{s!o;SgresM*Y2L@z{>g0Sb_*K# z0*3=cp<-tzBCY^U`|XZs_O)MfVxuHi(l_qj%XK4Z@0oe>fJCbRBCQ8ehr)64OqM(} z%HGA2RtDk9S!Z9yrvA`c`->|q$NIaH*4kU({Wv^1QSzQSmbUWz4d=+?*Ua79uGdI> z#n;fs#w!%(awYG;rf$-X0c)pK0MU7n&i5cKB?z8#p!!tVqtP`rHL;S6l1c?;*TQY* z^z$ICKoP~#>9LfRx&~qX%M9-7&$bl(4FKSN(q}M0oGa{?_y20`mb4HzoHRhu0Yn-t zX>c1=ZS%^a66)fxhcEbf-!faNcye{AYxf=w&<;~ubc9Hx*ySt|0Rm~I;P$_XqAhC= zXGsJm0Dv^gFEFBN->&}cjYF#dqFZ8hUXb3Np^aYm7xy&l-?03bx!9{)p9Ix2KeMFt zbA!Ja+n0P0h5@5Jer^5#eLdmYvRZK5eP-rnN&R8;mX2nVCK3Y=JwLwGxHW!S1rXg5 zOTCs@5`*-ms}r80IrD2?6-)eeAJ<#oXv1W)-Huk!H#h{4uM-*@M zq7(VITLXb_u=_dUrjt`7{a>p9BCQ9}9Tfi06qLP-CG9UiXWfry5}#yZMXn`o(a!na zWyt1{&_N0bc%T54I{^|01Hj>TN zj-;%&wZe_Y&Q0V!iO2fGwuI}-K-BeE){=SWiW4POr>FSYS@hadF?jkfxqc+CGN?ew~w*Lk~IX-|B<*YR5qTfzn+QE02k zNjwG;0l@R0B{Mf}xKML4A+xbcLZ7g{k-lqVbtCppqeur3X=YIa+jQ1Jp}eYcS-d{? zT2<1K;7xiyZ}W%^MgO!VvkoBg&ZwHx zX&OMp>TFsxZGTE`>hx+PX^>cfh&12q1Tduf7P zO+5#ZW^66n_+NW1xEr&7Lso3=K8fk{egN<3W;t^5Sju4n93anXj%hq!_T6$x1qOA*llpuaxTZ6AX!`U;PrR7pWRmWm=00a0axAeF;x z>gzeLQWvC4I{}*3PLmG`711@ke&)59$7$aIAo)?CUEkY!+L0PL)x2nk_=AUJV?ti4xDwk=6BK4 z#l@MWzqSH4V!|PZFdH*uK8*rdh|}q@^b>&qaBn?eFVC!^o!EFKm8TT|(FN(Bnn70v zh*%r8%T^~|iGMm203h+Q)Y6Hj-M<;2zsAcJnII5~u)^8ciF)Oi8ymdtCzV%Ve6#`} zIuD`)NL$OY#yQh=rjoPPg9N~r&<^=G-1_srp5%Qv5@6hlV6Xb_WYH0EJFNhS&V%Rx z(q7!+Ie$mqzxdZ*R=p}skkoeLI5Sgg1Gwk(bES7%{$DEqBCQAMN`>QeODxHd5iFl^ zJ}swe6|1VMv^`L1%@RxJL3AEO8e&+bf5LvVyeg~iaeH)H0TA61O9znl3?2Kw!Tc2uq8rneGxO>sJ#{USg;oGW=RtG; zX$>HK&&LU)63_jSotd||u~A}?hBmRK6#$XegXj(l|7Q!@d{Bsua@5>kSicgVoqYG| z9d^-5Vv#y&ypC1?MCUwjg0RWQfsU3wiC8!j&}#2Pa*GWy&mV%jyOX;4iWX#+s| z%-vshUH{dNyx2S}yS77tA)Z#X2FP`8W7Sn0Wlv5*+|kxb3abCPRJp!M;sv@rN-_8PtS_9;46Ep7?z^yJ%d5u-xmiv&WEkmZ!XaW80 zx*e`KKfqrT3|>|lH4z{-OV+-=&q<%tkXnuvhwBuN@v)xO%7YwdGSluh)GeqkD8P24 z1hN{1Jncy&0|yI7yYcRL+&DjLzUQ4S!wux&qi!^s1Q6P?u!m1F66bImxDu}iccyOD z0w7!Xf+sQW(q}a`Hl}x`G5`QD_EfNmwY3!ur}w$F0d+*6c~g&wou3(YvRpEEukHZ( zYW8{dAGGIx#J@;g3;<}5d4L^6(5V0k7YMJsN_`u~X0r=BXuR@vLo+Zia4|G@2y!OE zV|tizxrtj#{#|bBz;LQ;Dzc?_$Tp1H3 zD-U%6h}XPr+1L0Syx$yy(p^|J5utD6{3$FOCOwH^ms7=?;{U`)@Y8g8dLA)<T(5xK?I1+JRdQw2IH^)P5l{q&5wNV9^^6+EpQe$3A;%hmxZ?{Dc zTobOIovzfgAnKAucVg1gW@$iNDV4ghT3G}edzT+Bni>Br6-7IZwsKif(OKE^nwpwY zmBFto@gR}tcKYTP_o#xqze@apG7EGv zb!Riqu@CGUAv5!j2qr8z%f5tt^4qbNUhqQ?M&eWW!Wp5?>!X*79-TgK&I zN=#2)!{Kmp<>jHH`$Pi-0QBf44psvkNyvC-3dt|yREUeTdMa5~KBgq{*Bh<1r~6v@ zx*hyOqKA>DkKqeX)?Y@V52h#N{G_dMr5GT9!C>^c?&20Bjbg-Yq9A_bAC25&DJgHU z((@gXT`7xvTG|#Skw`Xr1`Hotn%O8%L*WopU#iXH+5HF~kGElJ{Ep0|#~WLX{X77{ zmFc|9@WR<`vgoScg2Uy137@VjNGbgt0Kk%kR@2&k00<^HgTV;=&dluEmo&ObTLZa+ za%wKrFjp33Wn#Ctv|2$LGNCY_(dfo>%YLSKFw~7~7(|_K?ae)voBohsHM1t>`t!EN z;b3Vu@ADCJ55#;Qh_uztZT_~*+cmMXtBUKMYSLD%B6Ilv0P>y@;^^ITK_HGlZx&I{ zNz6QvW+I5F$Vq-)Qog3LvJxxLsIfZ}iYQEzXvt(FD>j>03qXl9KVqgmdwJ{#X1R=s z{&|_Pc$~wh|J?9MKkacsM&^1ZlPPt%98JPyA07*naRCr$PU3Xkl*B3wcy_ZP{Tc#jG6jT%uskm^@TCJtpT36lGTKA~c;$C%B zt5xf0wY84cx~sTwPu!p=s0`Vgkp15KeO?5AR0AX-fdumYggehY-<*5qMPUAnfdYh& zlfY(#(4~Uql38f`6NK876dI z%_6a;hz8sQfbpN56M*0;L0)_)m(Lw_@#1wokfl}(VFpOmjuFlEZO~|(dxLdW6t_Fj zG*-3YYPL6q5KusipGicUrag~ae>FQhwPsgT^Rt@)qT?AX4QcAw&cb>^3uG~eqJX30 zFh<#pjWEas*}WLLaB0sAcm6iYMOM?I%mC4W(8q&=yEf!nEUVAv1%8knllaO`Gb)fQ}(2)G2IC%(uEB0yxKO zC7elYKo+%K-_VAMF+k*jth6Cx9zQq?Zz79p?Pmsvnxj3?rVY>2h8y7{fV{&^vl=zBY|7$H`q067nV}iYl*h*bft;Xoa%uTk z-?JAEnetvLTDut_WvDYWz|mr$mD^`sS@nKV&~?U8qcd;wFdUKXXjac7MLPL6|>HC?LRbcql18Fex!%-u0A}cuo44QX4ZsN=_@j zOIS}^jJ=ysVh*Gk9vbs76MUw`5aiR;dhhd*Q#I~rA`Q&|0f1BOgIc;{7E8PdK`${? zx150b;8PBUK@O!ze5Mqy>~i_q&-xhCSjI2|1i=2z2kjRG2rR!Yu45i@eKH{*$018D zJMVVsTsQ#Kn*8@V50K|=J59ADh(#7SE-*?*AVPAWX;Fz<#6t*iX(J4)ClG>Sk}OP< zOLqibzdTK!;H$~8>NG%BeA%~oc&c)hy8`Pjs==!jbB`wxl8BYc=P%@vOVJ>@AX3sX zr;1Wjuf}DUX81971LnN2A42_{o~9N1w&&RfcdlpO*@D1>krIS8U15N^Y85DzPsZiN z3^@8QHLfNZTVHe6X@I15>b#kUi18SWA*u!xbH^- zg)SU!BT4m359KQ)J$NW9WBKAoPh<6%Yk|7BTH1csu=&gYWHAN@u&r)$H49-Np~#G} zFCTUKCo%1bu`<4<>#DN=$?Dks7zfAtGSx<^gNaHii71K?d24RWqZ#syjN1ndTXM%K zu1n|Db~I~1b;gq62!R49aSAQk9&q8>6lCHqr8)_ag&|EkjpjOUu_ai()e*kVD6kTs zvQk*Z8(W^fx)iE0pW@HZfKNVP+0U;>a02Aj6spfK6^S6T`|WE@qKk^MHT|_V#&&fQ zAQ|1dpJb7!J5B>`=ztO)hQVu@^x>|wq}fXn5>C|w6NCvJeAfH>E%6fw1~JKAhL)I2 zHAfO?*-7#1uZoqNJ>#OI6*aMMeauZ1IVeC zA?pGyg`@cf_In5j0v?JAkN=UHv|w#w;=P(aXlDLEfxux^qb8HvTibnSXv0J)V{J}+ z-ssyGFP<60{&iVHT>}V~(Xo3h8$*pslxAIMR%stD!XOiu-_BzzXSX$%y&B6o0fK^{-OK+LfC;?tE7M@5fS8#nre?y-ih@0D^V5w-4Ogq~#elj@cM0 zLy)oJ6kw2>sgREdx_JJW-cCx716>Ub^|oiRcRAzS_J*PZvs{L`(iM`^GDLpUacW zzG{E#mQmlk4}^sYxS*WV9$9=_@@kbqc#MnTaJVR=?`OB7PcxD62ARZwgVq^m7S^%|7QsKS|+`KWYiG<=*ww33{mY} zm}_S)ALa%Tc5?~9xQ2*FV?s}^7Uv#GmC2@zynp{ywQsVD6V?fUtPTjUaN~34^x)Zk ziy7$64yH2tzO1BA4@+W?{u^&HrEpL$S6kOVe1jhm2w59KVOLd*r|9Ptf&q3HPRCS{ zU#q=)od8Hon>MyKEQ>un9Ped_C(u;!Mi`xSE;Xmcz$eirAw9})8p`O>^DLX9J0Ji; z-_liOPNzYE01vf}I(etsIlPJ!)(L>bhjy>WBFJb>#n!Do zn+t88(j>6;J%cLlh^7=L#A#o1MC7hdO|WL|XH7i=esu6TX@Lk=LxkTj0Ex`b`n=b@ zTL<(Q+OO}UR6Aas0La$H&6*8zak)p+`nUNrcWfn1OUFHZ*!{1xv}>lu03}>um({V` zH6D)pDhyD6m;uONnW+;7J-D~qh8}!X z$q1~LpTbHe$?eqr9)YkHhRBbxBna4(nlfi(%!91}kZ%n8)pQMYL4vTqsqfiynQ!AG zhRBCtBnSzVXvOP9msN3bCZ~UtGD>2{uD2~QycLrluWv=2iE6$q&s}*`ma*=~n=dmo zWlm#gSL*@NtCtUFxWc|qU&`ihLzEL`7)nl4B6|mGYjp6S$rja0XxsUmkRUp-aSZ7@ z42LOE`id&tnuSZ2wLcr3WDJb2=^AQ1Kq5YL;dhcbhX*Mgel^7Xx)dOfL1EJ=D$3+S zDM}gTMY~QHgaoT23o{Hr5+&kalZ%QMcYSpGg{I7D4DD(?KsNdN3;g+3BZ7ri8w~=G zk}KA->~yCe9zHBKhC|h54GHZ!-?S!()=V_AzOPs+2_li~$`BPV?)dQ5OI_AiQ+;YZ zK(=}XS^3(q#<#MvUSUWjea?%gKHCx#P0dO8mhW=Abh}5;xIvV31|Vh$Qd1DGWL`#s zG_|xG9%Nzjlc6LCW9I!T_Hn2E8Lw}fsF_!o6V~kp8^iq!@griEAVvxfW?56i-FSTp zoIn0RY4^K90P@C@EfZ?=ag?p(cC` zhsP*y{bMTUhvDa+y*A?C)AyEE7XZSV;^EbOiBF^B3Io-JDk+YZc`iLW&;)}OA){ND zQ*0&P83T-Eh>&;2QMluEa>TgUhnoPvr0QhV1%Tj-JbgpI@$|Z+Z~K1PpBu(u_;*g) zhd(GDM8?K~$-E&R>Mf{u+&wgwK$x8&!rd7fF`*`Zrln5);_q0h$aSinY!T{uKPR`&ScW)dt zg-pw`2UjNmaxO5`Bhc3J8KU$b&0Ch3Oo`D$8EKt|$2^EKK*WaL0ryQkPsi{4eC`q$ z=4^;OnbHL_0J&38*f-?X|^h+!jMH9O0QViN_jiS3umqxn4k4Ne4PMDa+@BuK#+$71m3}r z0AWstDf<(ZpEIh*otu9E7;ozB;gCT8FpGLj3V_~oWZu1^P(bG(n(k^7b*4tkuj+!H zIsuRi;b8(nF}cu3%AaDWl0T)yXRcXdno2Dsl_MKoI&YL2ba#7)dRUR{pB)t3UWQW4 z-_ipRBwMoY6m#H<;Dbj^?$xO}0g!M2c5|ExFg~hdpE~s)trgf&5dW@%En(1>=thC?b28wQ{cq~qBz;>!ZXP$&)zIA zk*lr%1ZI1AS5J}6pGB|cQ^oPM^15WD_KnR&gIxX1J>U#nqzM-6A>NtQg{}w9A^dl>(#8LK#Uy$;o0_Le7@QgMXV@`a4o*0K)v3O={-VbfLXZ6boUthPK|k z3j!wdP>CUV8W%r5>FU)9QTh3XF7P0D&^hdTXJC)y0cK~2F!suT&M?9Ml4?{qcR6+dQT47CgfcpIi|1gNS9RXQ-#ah_vS*m&mxGxZYkzq7<{)%F)OR}i00_r7@*DYuOZ}M~ zgj^Bx7Mi{W*J7Mf##)~g-zy>^fhp##pLYsFY=!mPHE+|XSCgp)Hr#eJ0t+pcRdG`W zAWBMJcqL&mE1$J0HsYY%)Bu7zg@-q?Y1?XqZPTEB)V=5}a-sF6=yBsTCjP5#-S${2 z(Le$)o*^PvwK*&dK$;HHe~x6P@17MC$9S1_t%|~$=IiPInWOXPK7?>20Bv6#h6oMz zDnFW=?lt_ugQ{Mycuo_#wYKZvHB`{Zsy~S+JAkHrNS*FSmn%GuUdtjx`ozk2ho2IbF4O$P^dQdSo4Q%@J$ zAZweDW*qC@&3J;=2*9%@B!#;DGRH$Bprn}MW`RW3qp@z~SXuVNTLyT4)M6N3-{uoJ z3v&U=z&m^c5L%Hdex@K$tJqGPMMet{zDJO=^O&9kZJUP-C4@H3DTFxh-!JL>7rS=) zW&QfAHqo=Z?67XPxES8l5E+KL07Ok6h7;rfNK7GGF5}`#oK(^*5^yHqlBN+40dN6; zQEiOwD=DTYKa-U`FGE_eadLdTOw&c$xAkn~FtYPdeiN&3jEi}zUcfRiF5a%K1R%;> z@e4VDS{NHZkg!P;8{1wVb@vWy|CK~w(VE0CHJ~Wj4kJcrn$9`&SFiXhSI&bLZ*e~e zZMV&d&HJ2>;TAQaylToUrrNHg9BW~(JJ*756%{?#M#pM(cX}LPn6JU5MR}r+qG_W7 zh>cf}m9T4QJDbj}hVz`gdQt$ES}+D}m;CrQwn94f@W`nem$aPStbT{dcD~zeaZV#C zW%zIlE%sybO-oXcX$eg|of37X*+BqJ35@Es_iX0A_T%%2gWJ){>h>v?Xl+IU5N?AB zUx!!*w;9H*@7xdPvF&M6H-bqeHJ|)*}ae!K&{y z5NGo|fYuEZWvPu31f=fU+c$CJMkaDrV{2AI@2(M+GHeo`#_Wt(7pau7jI9ClR^E57u?BmLFJLmPmE@uv}YjHJ1wHg~E8)7|-#ehPf zByswJ{h7$_TFil*d;7V&F6nb05VF=Me`v9WHvx3jaK*yx+O%e;O~FSN}BoGl0d z1HQ$r0?3n?-xWDIv*TvZPSu7W&)RibX^9ix2ymQZR@Swlocgv*XnUbTnR+TGYv#z9 z`+GGVu|Y%_m(3M^CG6(A5->#6Qh-?6HJm^5L(8D%BUyGf!9ZiY>ey%rK=vqda^`3Y zkoh6af`;rQLRhX!xQ0HlZ{?>I$8wg&*IrW3ZfFvXg~BhHa*I`G|S zTTRE%OFIKd5MjeHV>(#%{%AB|XVV1`2xtSi9vea9#QYNMIv-7tZ>QCObpQpFYupR@H*Ezt0SS`g zt3SSaKI7-tuWx8Nf)45 zW+|c_Uol}NG);q8Nv(Ccd}g{fy&LZv=F7I?Z{oNLyVi*3{N2S?1`u~!c9WZ%>hH4R zQ-41KxPf}YW3~s_&zoP-jzjtAOPxBru@R67sZQ~?#*KItjm{=Zf?B5p~FAn zw(mBZ<`a!+jETy-HonH@kS#F`FirUv!vHdJl{c}ZM8;XmID@u@8VYMKkH+c%@?f{4g@EKmI#>}OU>MT)dR5UmsuX&X&Pzy@A6&4zVC9M11&rgU z+&fGCe*IjmJ9Zq!3u-k5AQL0p%{Tf4C#&RX0 zl%gED{G%q84Wh5@H45~LhL zDH2lU$9_8_TDk6U&b`f&`^H+ufjOFS?VaDVi3KD<48kenAp^1Rg<0lZ7+z8x~Q;$|?noCtpID?$+T( zCq2rOAn#(+)0ykBdq%x!ja=EE3;CG6g4EwloB=4p0J3xF>vtYWe;T+h~d$y93bgDf9-CAa9ox7aF$5mdzf>!7yHR7n`f5-%YKiVajYB%t>oA z?%_kGYQ0v>5pepec`PA&8fL+^H44YzyF0JUQCPOUg#|e7pW`|@$eTOfT7bJL^#~MZ z9vlM@6~XhkC-j!~OXIf#Kw(8E~ z8=+8@^HeK#sy&TWrF;OW$h?dnvgPu{6Jukws?^T$^78fD(Psz8NzjfohOiQKN23iu z%JjG1KE?6l9zhnf?SzCcspr~6%+*V-Kxxn7l)nwz{9?_s)QX=8b{H|Dp<{5{Nfu2S zj{*c1hL#`<9cK0I)McWW^`j%gn@IG1_cQ?vl@wy2@|B85yNhzeBJSVUxIjkf_5!26 z-i`5DgE2PFqV>!}dLyAafP{yK<4G76oD&^=FTR5MMGXMBgTrmZzU*lg;bcp+VyruQ zTUhBp6o?ZNr~|wHm8|&jLMl@(rd-RHRl7bmPJOzKwfdmt6mfi9q#`$WZY2p*DDb0j zKOZ*49XxnYYwhq{>(=9aID$D2Y>r1=b0RYS4mnL_PEUB)`M0F>SZ%LXExzg9jri1e z3$o^V(J~`WOj8FC{{sgETo&h!C5MC1)r-rDPDLJ$uDH-JKDd*x)w(a8z86@~VF&}B zzAaw}km7X;ef-RA@#;ytU*E1M<_qlYEnVC9^r9X3&r`Q;E4RX8(Acdkg@bKG?!%Lj zDNion04<7{mE!kB`!0(tacl+$!|RMb!aOM_rKr67irnShuHIR#!;d1-^47GA7>sFD&8$ku6PMWZ~}jnI!YYhYuq@W`ZrX{Vblg z3tQ^R;eW$~#T)TaYr8MC?cY>Lq!e&r`8KG%F1P-J~QcxWC;ulJ<0}kt|2)vyWA>MVG44)iB$op&`c&u{9R)`K zX=3)nvnl+a#?HLWydJA@E#6W>sQ@C+ULUe?TT7-x!ZvQ+z8eQ!M+9bXw0cL`O_dK2?j6F$ z+831oq_hvhaiFBKB3W|k(WI3tR^~l@^r#{tg$Fu0O#DRfMe~OER1Sx#uXl-p0Tjux zqzkWjKMmgaayujyCz zwY4%MAHyL=tEgnfGlbLR6nw67sb@hfO7|5}=G z$A-wFa^pgyz#u1ARbMpA{K|M6zAtCL|G&6&gpSX7cM6lHUI!107*naRGOX- zY~9?6&;QAT%WKCJe5g$yBRTSvio{Gxu{7}V#b31DTbrrjJi-|pA?aFw6Cgk)a~IY0 zid6|gC`DG@{j9@B7AIdhcM4v;D%T@EHV9arjq7(B-B+-P%O!&lq6vNLXcz+7nLCp2 z?>`o8jX0cNuKBU71VI?V1%dRG?l^g-PDc0%aNCP3@3`trtR;RtR&6YojML~}k{m0WJ&I2Aw?nG1#P z0n-?O&@=@&R{)Zu`1$x90#SSAi4PIqiu;xB6f(g#c36h zbVQg-DDfZTc}-t$@$fRyMUr2t`G1jaEO*xRUklp0Eij5A z4BBo$pAzKVxtRn(N+!;iroB2Cw`$26*~6D{6|JvdpuNk~Ppw7 z`u7UQT(@u$?ruuazf8%{y9I?iUMNK0>-wHxi*pmgaZB!IjKk9TDGOccuD5>G0g$(g zWgHk3rBoDTUCY?NKjQWM$5$aXw%j{ySXcvYyB3x`rv)h%uq?;`gaNLJLSI!0QWhji zQBHjN*)@}19oc(YSE5ht(Y*Bxx#Ko#gf};qHxMh@3&RsP3i9`)7Rsl88Go-LJ+j3xc=4W;LNw>Ih)R@n#!<+ac}tY^_pgaKPzveuxLp(a z=ia=`#2o4L%?R^rIvKfcbii0)##%zCBs!PO;nBy>W#kMWr{g>34F|OJ6O3)Wob74* z5os*dNpw23axx0v07Si`P}h0N>&G}Al|}jS#j#J;KbgL0FF?hPzFD1}T2BlYPPcbJ zeGrSXQ0I8mmjo#*`Dj|1n7pZf+mr{-7^|305hbHK`+3ZxT~<0H_8=K;T(4L$<$9C| zJtD-R;e-Je6B>E5MzT3zUDooYPYI%0@|(v&NrfagI_bpb4>Kb7 zWLAOXj+cW&zinx0HCbUKAi)8F9HWa1IMhDxlV?U(VQZGmh2%)0%I9X>7J@= z$XEdKb`KZ^v|N!VOG!WcV&le@MR%@NX!-iW&)#`tf6E~s1hS{oY|4qzQS>fB{(m2( zNoC%(!sO@SPnHd>&~#nNnOL3)3Yi#cZL?fK)oj9*Dl!*Q`8ix_d*c&lXI6!mnjf3I zhN#CHh4FAJqs$J}WphRckpJr_fJjoN$P=B-*!{<%l*4iN0HVvS*4VF+z2%4PxgQPa z%$?7-K)!dMNq$exLbJ7X6jn+@3H;>4&Ud(#% z%eT)o=~GS{^joXu!QHJLck@Wps5(KyXX@ulBoCtt3KkA|eD{#fdx!wcqo~Vg_Jf;m z#Mp$5(TA^VyHQgB$XlVJQs~8xM2{Y=eL8FEKLAjm?S9L)*A*bl7&w~;1qpZm%8$Pu zk@V-vn5wK}bF*eP{oS3`@x<8Y2!Lg1pfC==XX2t0w~F#2Mm>IfN7uFchljHzjnSue z{XKpG?Ef_ZbREwq`aPh z(lsz3DNakcmVG~RLDIfu=c>vlU%GmcM&_2A$0s74?z4m^l43M{{f)F5zEW)!>j-~zl^THSBt}s!!l`0hf$pC_m(0eN9oQqG-!mnQtaU|VH$*W~~IgoJ$2j3fBgjm7rUrL+)p?~wx(pTy*A z|9TR);#g{`)@jM_tdr-rbMSbJ<}Pah!&Rx+wf?Jx00B)4$5mAad5<}80&qofzC%v* z#zXT)F3<<_bakV8$%@-=`k`pB4h z3U}*Ssx{OFa4`F<#Q@o($jO;gQC%#3N|66w_b>pOl+yCl{aNc*kBg80S0Cld(edEm zE=@R=KRB@2%@q|eCliI6uTW$?6Blmlbm!Jm0FYD_9JT;LpUAIPK|PCcsu;Ms0uD1k z{$I4AykJG$n2SldrYWT&_rmMQJtI?(Y}VOYRCfLe?(R12t!+p5ws%@g(=?~-{}}5O zLNuKyD?T_gA$HN}^xP_$p5NVf)Aa+ov70)7hdWw!FuDrtx*d{cz3RLKc_-yoGpcFY z3IRw(iFADA=hrvPVV**+)Nr7|$k+%M7vB~Be!Dm%5zJITVll5eA@WwNOtiSqmFtX! z%21zGv)x0#pUi)GVRFp2vAZD)v?}mi z!F1>=Z`ZJoY~B3s6y(Jn&Cb4kGdH)wV&7UU9r^eGJ3d~OfBv4@({$w^KW<~270A=7 zpDJRvHh;AkAipbea%NYmgH)0aqMx&8{NpUJ$b7Kf`q^JA5AOI~r(zKLSxd#EeBH}d zIH9YMX_xr`X$&)u{GViv$7X#jX?F5MY1)&8kJpURJ^rbx1S+Th^PN5p1V){(v%-TJ6ES1Sy%j6@ zHR_&ZdBpx~wJ~S5y?lfo!#l3Py{(1=3+LCmYnNGqRGd+$Z(e*;5JD!V#5p^!Pw6mQ zmnx3>UQ_9K;$yz9q>^R~3U9sRECNAMF_rq@HMy+obj4196tz|{yMX~A?vp$Igj=$M zRP~=~i>0d;1Eglmyqa+Dn*=OcDK5N`e)X4GNe8!G(WIN9+o%HwGbh6%EEa;4O!~~B zGxYZ}|BPpo2g)Tf$kpw9_9)SZ$WvC!4A9nkWc{UF~eI0@BgF>4Y4iB8Ca1nfpXsXgj zSF5sSgZHh)05M((Qub7t8pjkxQR2MEIp;Qwj6eSS0|2NI(@=R5v<~u$v|TDIGv{OC&*&n+E?*-Ig=)(8FA2z3-WX?LYQ&w(r_xlCX`} zEQ+QGL#*4NW7cAT82uGX?ZF}(Fc76llJ3liUOf750MJLM3g+H#bQg?mZ3jxhsaJ<1 z)E4yC1|a`gJBjlDJS&>M_mbj3RBV$prGwIkJZAU46X0MYcW7fBlVnAA@f&L$=DQH#n2kwTx@s{%G?O6J;waSZXN^(f zDN-(#<|H0ZI=SVCoGV8(eTSUzA3VO9P0MayQMOHoAqv<`o#dS8$Q7cjXR8eX5N3zW zbAuE`OCKkZYrorGyy4tq_4NR3o7&d*TQ_*4!iLv_A~3E|`%cV`LL<^a%EJ+p5C%Y! z3Q2O&t4C+VqL-0*XO70x0EG&Il2B5LZG|L4`!2nG9lV0NVgk>u60Uf`g zP+MlJpgm*hqDtOsV#0@+C%DVa4c7!9Of8D92f74I8)&t~j;9R5kY=@z)vdAQ@meyc zkUrk(>-ZU2g`hUP#uC8J_t@lbacACGnnej{Qj?4qJyNlHQ_5$e<&}a*=JqO4Sd&{3!@lUJp^h7#mIQ+RN>o>w=yt+%$udLn{m>-y|sN#FokEP+RB zcNR@}c{bzrLG`{KfzO5kp9+ILR$@Gsy;0~UbsiuF4|lH$TkEf{E&wT={lr4op#k39 zIgZx2zf4i(E0E%JqVVU-k7dh#-2PZ?Q9tL2ojruX&PyztyL?gOmM$|uOh8H315UKO zV!bz`izV0}U+y)np$jnu!)czb(xlYdrBq<4XYUtJ8n`s>`($VG&_OE(p7_dNtmJ zSd?dty^LFn0iv0iw+e;h^uV{(HiRY;q~DHymP!eC*vZWYet}r^k98iJ=hd!-H+!*b zJt|CI1!=VahWv~3xW${~iO-`;VH_&$OW7zeYib|tzYprq?6wlwaN4Vi)m2wGeJuuvW)j4R9feh4ym$L*B0)lK%yJ{y zl&Zi40#J$`zFsT7pRzu2{bhAeJ)nubt=&iVv|i=w#u|WOz|xgx86}>DVUU_dUjO-@ zqWPnXT^<5Df@zO@)2W(lIL1Sta$6-WpSNU{8dI3{D((@}pi zQM>O&88ff>X7{u1yZEzuKeD)V0|r^T0)=3tiHs^rN>vTvfZG9pQ2g}5X~frSqFK`9c3lR+dm>E^%e*Di`;_+N1rTxx$_DWj<4?H z#clWbp`JAw!exP#Qpf0ah94PzSCEjr^)fyEZfg9Yg9p`y|6?sTg?HyQaat+2;RP7% z*@apR5VHiSq68_60>s9FE|jMzlZrRoAG~ctc~Qp8hPk#G(v`c^(FW_JdZJLrU-vhB z2~>6-9RDM#aLW8W8EPLZ@|_-L&-ZtmNVIgG4>Zk06X^Fd1EemQc`FW9Y;%8}tAdjK zCnx8T0YZX#nbLeSI~wq2DJ#Xii%%7$&bmxC*mCCIIqTitOrxXUh3Q*1l#+5vlu# zfVH2DWwmqv4zY1(y?Em5JV5G{C%xiWEKLDYmNRLfGc$j=HZJ*0*1r%RuP(WZ_~UDr zupX@~Ht>YtS5*fiMbqS~*VOj!*W@lg^fF)VdoTikdS1uIu4B~`48XVquS)t!Ee42Y zO1jz1tF;7q@249BTJk*Sm?S1?=8K4PYU^i3^lxAt;zv(!7tEdk2)I>I6gUJxYB4MR zQYvrFuxZg-${VZxtS{b+rnjWJ=wpH0Ohub&yid(M5v1h0igs33%BLRTgJO>!2Hg<`ny>EfhGSL0-A9|r0HV4HJr z{A9X;)l`g43AOgbnQgCuu1?aSq)?DLfO(QWJ1y6=utZ(%UL9TW?`eT zEgMsF`8=p!mGZtA1|V0`dAFa+ej58r^0HU2K&oy|@8#h^yvFtsJRi%xT2)x7#Q-sz zd9^h2>H{FEDRBgBIW48ri+;WS;noNMP#d-D^g)XTm&O+_a~8tKI1U`LYQ7Unrlg1z5a=&Zf%nFjqbwo+21VaGV?n{5Vc&~WX(j&QQ-(~R}-Nmt9 z51++KZ{cu+Xm*7r;aUt3vjnLr2~rjbDrJF|lVbVBjIn=>PK`}XQU??Z3vfI<$9+Ou zf9@=rs9HEZ6S9(DNG9HilYRT);uQ6zlOh0fNe%7lG%92R#=?Ee4W~B)q%JAx`Zx1x zbWKrt`&2-Y$gUh>7*UV9b^q*wOK zj|dzvvFw%XS?0p~!}k8JPMR=dOc>U%>!RN1M?M$)Y36xU9dBMZ1|U_?;uo(KhX>Eg znG!D&WvN?Z!!O%;@w@m;WBJ=p06-X14NsXXejz7Ni`Y(^Macia0RflAy+hbo`y$Nr z9z!wK7p5v>7(@QQy8Iwo?*RcI90y5a&aw0@R~BZUcp6>K82q>&edaqjp)q_uvQsE^ zL`liu6b0G;oRCc~Ok?k#{IB{DnFbL(+gXKru4Fj~S}U_f3GV=ebBD08F=c?5C5W-= zAZiexO3o^hWyuorcEo(XyWF=@7JcXxJg7H+fx9c-m+_nEDosl52Kng_b>xP4>cn4P zGkGrWq0S~e$jZvk?n|o<-oq6*YF9>rm^?ttI*2jrAnL?KX`xh^GDdpv#ni`Zj-Dw; znsBbI@fzaq&iFIuiD!&!N?<9jJT8v-es}J+=x0UQ>hLS@ak3Z1r!)VfORiXT z1R!QdVU6h^m6c=hWBF`eBr(N4qA(w5DD#xhj%`SqBqN|2kWF z|7oIpQLpb){;BAyShoODr^4Nh#LTOOqQX{^S!qWePki~?ljD%0_5o4ir*ZW|Kk3Nc zV!^}wv>5umrc7KHP0_OS9CF{Py~@Sg&L=0R6?AWu^g1Fz>J&g~&{0?g&P6x|veyO2 z3-4vje>Ly$eRV@cjA+31tEZURBZNPbz`?RA5`~c_+4)q;^?S1Q1J@+4d$*o&ZIK|F zDe2~-4aRrHQip&yAuGkQrx{Chqb8p#&}t60f=T2#Ox?E4GHoeup*G`e8*<4N0@QbcOM{d3&A2KulTc)Q^3#WL+2jHLj{Dj?&f zwG1GdNf5IRVsaAXKPY(f_=pn86nCCX`Dc{qX3|r2kMuOKwzl3l-FA4}0PZq2t>9~_ z#Mfqkn01g^sDo4}oG_5){rhBD_LZcc3Qj!AQ`dIWXb->rol#FsWQqBTRj16n zrsoII_O`~<>{8^2pGa?~Ono}%2vY?6{c!pqU^Qvv())}4{EciDa$~~5$^(m8f|xz& zrhUay>&avCvk=e;q8*B>>8l>B{YPynB5MHadpjDhwY8=`Wf8Q6IwfA^)Te;TDpbZw zi$!gkOw2MX>DvBknL_*8sH9g$cf`T~m(Z!o*W%T825j1_?jU z5Jr-e)lc34geJ2J$$#@?vO%rJrHajF-pVL%g8yd#l708|YEf#;%GdvF%2BrkQ?p`e zbPUY>3B!QQ5uMH7c75)PefRE^(~lFq!E<_jM`DtYkDN=x>6!2or4nQ(&PrxJ%NA~p zI2bE8=YwcOdDCiCX8}@jOiZ{6B~|9j(hB!H?!SF502He`!D)l)HUGSW<-DfuM7WY< zo}pKH)U!T`C~O+}Fe8)cW0oMM#rqnqwypz2MWi?3^fV~Qm#N!tOi#_behk#VJwhCv z?Qk<1;sE#;?DLf006n9aQJn0f2u6{|cYo&u6e$lqTO`Dk|h_M0i{ znnet7tmkHhwH*Cx+NrYtuPp%4&{3GVQ0N{o4PgYAW~y1#($6eG)Eq3MpD5{q!ppCx z=WM-qAUiIuNR#8QEdVh~5VHi)gx^(fF)EU!XQ>47{D+gbmsdaQ?O0}jXhc@iYGjrm z<(wB&lSD=qh;AkRl78yp&jlx*zo_hFYYRZkS1hvxshHPEIw$8;?7ECAu{*`56V*L+ zt+sz{0f_mEWd_K98T`!?x)dQ-CRkp6xh7`a?LDcfsp85c#=HNoEdbF>g4CgWkUDF{ zLI@Dh6uVG%PjEJ2)#=4250zJ>P7|=09fg`u$;8^!NgafV6G5P0XBMBahyumGQxH%w9h<#fd?{tqldWf-8)7Px1_%bU2qnes%RGB_Mf`938IxVPc`*?Y z5!m&N{9Z*Z#{P+Lwk1uGhM=cWj9JSi2#r%HjlP? zXT!_bT`$&bUYivg%e;xFQNoy|XMk_h+54_y1l!IG5EGIhrDaN`#^j%%oOk4^kw6S6h{mm33-fIKTKIGf4VB@IIuTa(ei+8G1)64;rm9ur znzWwEr~$%I7y(Td$sZKmd%ZD!^52X_N~ZNDtEoec0EBs0P?=ZwMD!$X^V6A=_W(da zHSJf|X*fJLJA2o#p$&NsJ{>rY-tF1-yc>g(cGGo0L-sRDfT#*8AdqJiMHSvmUiWhT z2_|z-Zb&-lW9;e$2tuI9&A3q%`)Kv^ZzC9!ozWAh0wQD94O$2VetykeJA|}SQvN+y z^<4ZAP1qtpyti4=`Zy&m$1pa4AcQHUi8%-IZzruwUV4TJ1*w+nvzBC2+q`-Jf)Fi9 zPdt}5^=k$|R>3?2j>gbwW_yP(#c8pIesbee~n~s2Cc$JVhK^R5=09KX#GXTkrKDA$zn>a0b z->Ot~V+G!w>wrH6yEg~djuVe+)P;Vx&w{~P(v^pKp{&?zm)yYU*Ekxv_HDG6R0w7HIn}$ zaC752HEQGOW@8;dw+;V*jR|^6MOo*iV$sScONS&@v`(9jp6xwX_S=GS*#0Vjy^9k@ z@g}K4@uT#EslVJ>UT`h`r74+et8zx_!YMG1ucmdZ*EM)J;o#JjqF63Wt}iW DT_MDL + frogpilotPlan.unconfirmedSlcSpeedLimit = self.frogpilot_vcruise.slc.unconfirmed_speed_limit + frogpilotPlan.themeUpdated = theme_updated frogpilotPlan.vCruise = float(self.v_cruise) diff --git a/frogpilot/controls/lib/frogpilot_events.py b/frogpilot/controls/lib/frogpilot_events.py index 1d5a7596..19470287 100644 --- a/frogpilot/controls/lib/frogpilot_events.py +++ b/frogpilot/controls/lib/frogpilot_events.py @@ -178,6 +178,9 @@ class FrogPilotEvents: else: self.events.add(FrogPilotEventName.openpilotCrashed) + if self.frogpilot_planner.frogpilot_vcruise.slc.speed_limit_changed_timer == DT_MDL and frogpilot_toggles.speed_limit_changed_alert: + self.events.add(FrogPilotEventName.speedLimitChanged) + self.startup_seen |= sm["frogpilotSelfdriveState"].alertText1 == frogpilot_toggles.startup_alert_top and sm["frogpilotSelfdriveState"].alertText2 == frogpilot_toggles.startup_alert_bottom self.played_events.update(FROGPILOT_EVENT_NAME[event] for event in self.events.names) diff --git a/frogpilot/controls/lib/frogpilot_vcruise.py b/frogpilot/controls/lib/frogpilot_vcruise.py index e876eeec..b7effb84 100644 --- a/frogpilot/controls/lib/frogpilot_vcruise.py +++ b/frogpilot/controls/lib/frogpilot_vcruise.py @@ -4,6 +4,7 @@ from openpilot.common.realtime import DT_MDL from openpilot.frogpilot.common.frogpilot_variables import CRUISING_SPEED, PLANNER_TIME from openpilot.frogpilot.controls.lib.curve_speed_controller import CurveSpeedController +from openpilot.frogpilot.controls.lib.speed_limit_controller import SpeedLimitController OVERRIDE_FORCE_STOP_TIMER = 10 @@ -12,6 +13,7 @@ class FrogPilotVCruise: self.frogpilot_planner = FrogPilotPlanner self.csc = CurveSpeedController(self) + self.slc = SpeedLimitController(self) self.forcing_stop = False self.override_force_stop = False @@ -57,6 +59,24 @@ class FrogPilotVCruise: self.csc_target = v_cruise + # Pfeiferj's Speed Limit Controller + self.slc.frogpilot_toggles = frogpilot_toggles + + if frogpilot_toggles.speed_limit_controller: + self.slc.update_limits(sm["frogpilotCarState"].dashboardSpeedLimit, now, time_validated, v_cruise, v_ego, sm) + self.slc.update_override(v_cruise, v_cruise_diff, v_ego, v_ego_diff, sm) + + self.slc_offset = self.slc.offset + self.slc_target = self.slc.target + elif frogpilot_toggles.show_speed_limits: + self.slc.update_limits(sm["frogpilotCarState"].dashboardSpeedLimit, now, time_validated, v_cruise, v_ego, sm) + + self.slc_offset = 0 + self.slc_target = self.slc.target + else: + self.slc_offset = 0 + self.slc_target = 0 + if force_stop_enabled and not self.override_force_stop: self.forcing_stop |= not sm["carState"].standstill @@ -69,6 +89,8 @@ class FrogPilotVCruise: self.tracked_model_length = self.frogpilot_planner.model_length targets = [self.csc_target, v_cruise] + if frogpilot_toggles.speed_limit_controller: + targets.append(max(self.slc.overridden_speed, self.slc_target + self.slc_offset) - v_ego_diff) v_cruise = min([target if target >= CRUISING_SPEED else v_cruise for target in targets]) return v_cruise diff --git a/frogpilot/controls/lib/speed_limit_controller.py b/frogpilot/controls/lib/speed_limit_controller.py new file mode 100644 index 00000000..ff800ce7 --- /dev/null +++ b/frogpilot/controls/lib/speed_limit_controller.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +# PFEIFER - SLC - Modified by FrogAi for FrogPilot +import calendar +import numpy as np +import requests + +from concurrent.futures import ThreadPoolExecutor + +from openpilot.common.constants import CV +from openpilot.common.realtime import DT_MDL + +from openpilot.frogpilot.common.frogpilot_utilities import calculate_bearing_offset, calculate_distance_to_point, is_url_pingable + +FREE_MAPBOX_REQUESTS = 100_000 + +OFFSET_MAP_IMPERIAL = [ + (0, 11.2, "speed_limit_offset1"), # 0–24 mph + (11.2, 15.2, "speed_limit_offset2"), # 25–34 + (15.2, 19.6, "speed_limit_offset3"), # 35–44 + (19.6, 24.1, "speed_limit_offset4"), # 45–54 + (24.1, 28.6, "speed_limit_offset5"), # 55–64 + (28.6, 33.1, "speed_limit_offset6"), # 65–74 + (33.1, 44.2, "speed_limit_offset7"), # 75–99 +] + +OFFSET_MAP_METRIC = [ + (0, 8.1, "speed_limit_offset1"), # 0–29 km/h + (8.1, 13.6, "speed_limit_offset2"), # 30–49 + (13.6, 16.4, "speed_limit_offset3"), # 50–59 + (16.4, 21.9, "speed_limit_offset4"), # 60–79 + (21.9, 27.5, "speed_limit_offset5"), # 80–99 + (27.5, 33.1, "speed_limit_offset6"), # 100–119 + (33.1, 38.9, "speed_limit_offset7"), # 120–140 +] + +class SpeedLimitController: + def __init__(self, FrogPilotVCruise): + self.frogpilot_planner = FrogPilotVCruise.frogpilot_planner + + self.calling_mapbox = False + self.override_slc = False + + self.denied_target = 0 + self.map_speed_limit = 0 + self.mapbox_limit = 0 + self.next_speed_limit = 0 + self.overridden_speed = 0 + self.segment_distance = 0 + self.speed_limit_changed_timer = 0 + self.target = 0 + self.unconfirmed_speed_limit = 0 + + self.previous_source = "None" + self.source = "None" + + self.mapbox_requests = self.frogpilot_planner.params.get("MapBoxRequests") + self.mapbox_requests.setdefault("total_requests", 0) + self.mapbox_requests.setdefault("max_requests", FREE_MAPBOX_REQUESTS - (28 * 100)) + + self.mapbox_host = "https://api.mapbox.com" + self.mapbox_token = self.frogpilot_planner.params.get("MapboxSecretKey") + + self.previous_target = self.frogpilot_planner.params.get("PreviousSpeedLimit") + + self.executor = ThreadPoolExecutor(max_workers=1) + + self.session = requests.Session() + self.session.headers.update({"Accept-Language": "en"}) + self.session.headers.update({"User-Agent": "frogpilot-mapbox-speed-limit-retriever/1.0 (https://github.com/FrogAi/FrogPilot)"}) + + @property + def experimental_mode(self): + return self.target == 0 and self.frogpilot_toggles.slc_fallback_experimental_mode + + @property + def offset(self): + offset_map = OFFSET_MAP_METRIC if self.frogpilot_toggles.is_metric else OFFSET_MAP_IMPERIAL + return next((getattr(self.frogpilot_toggles, offset) for low, high, offset in offset_map if low < self.target < high), 0) + + def get_mapbox_speed_limit(self, now, time_validated, v_ego, sm): + if not self.frogpilot_planner.gps_position or not self.mapbox_token or (sm["carState"].steeringAngleDeg - sm["liveParameters"].angleOffsetDeg) >= 45: + self.mapbox_limit = 0 + self.segment_distance = 0 + return + + if v_ego < 1: + return + + if self.segment_distance > 0: + self.segment_distance -= v_ego * DT_MDL + return + + if self.calling_mapbox: + self.segment_distance = v_ego + return + + def make_request(): + try: + self.calling_mapbox = True + + successful = False + + if not is_url_pingable(self.mapbox_host): + self.segment_distance = 1000 + return None + + if time_validated: + current_month = now.month + if current_month != self.mapbox_requests.get("month"): + self.mapbox_requests.update({ + "month": current_month, + "total_requests": 0, + "max_requests": FREE_MAPBOX_REQUESTS - calendar.monthrange(now.year, current_month)[1] * 100, + }) + + self.mapbox_requests["total_requests"] += 1 + self.frogpilot_planner.params.put_nonblocking("MapBoxRequests", self.mapbox_requests) + + current_bearing = self.frogpilot_planner.gps_position.get("bearing") + current_latitude = self.frogpilot_planner.gps_position.get("latitude") + current_longitude = self.frogpilot_planner.gps_position.get("longitude") + + future_latitude, future_longitude = calculate_bearing_offset(current_latitude, current_longitude, current_bearing, v_ego) + + url = ( + f"{self.mapbox_host}/matching/v5/mapbox/driving/" + f"{current_longitude},{current_latitude};" + f"{future_longitude},{future_latitude}.json" + ) + + mapbox_params = { + "access_token": self.mapbox_token, + "annotations": "maxspeed,distance", + "geometries": "polyline6", + "overview": "full", + "steps": "false", + "radiuses": "10;10", + "tidy": "true", + } + + response = self.session.get(url, params=mapbox_params, timeout=10) + response.raise_for_status() + + successful = True + + return response.json() + except Exception as exception: + print(f"Unexpected error in Mapbox request: {exception}") + finally: + self.calling_mapbox = False + + if not successful: + self.mapbox_limit = 0 + self.segment_distance = v_ego + + return None + + def complete_request(future): + try: + data = future.result() + if data: + matchings = data.get("matchings") or [] + if not matchings: + self.mapbox_limit = 0 + self.segment_distance = v_ego + return + + legs = (matchings[0] or {}).get("legs") or [] + if not legs: + self.mapbox_limit = 0 + self.segment_distance = v_ego + return + + annotation = legs[0].get("annotation") or {} + + distances = annotation.get("distance") or [v_ego] + segment_distance = distances[0] + + speed_data = annotation.get("maxspeed", []) + speed_limit_kph = 0 + if speed_data: + first_segment_speed = speed_data[0] + speed_limit_kph = (first_segment_speed.get("speed") if first_segment_speed.get("speed") != "none" else 0) or 0 + + if speed_limit_kph > 0: + self.mapbox_limit = speed_limit_kph * CV.KPH_TO_MS + self.segment_distance = segment_distance + return + + self.mapbox_limit = 0 + self.segment_distance = v_ego + + except Exception as exception: + print(f"Mapbox Callback Error: {exception}") + self.mapbox_limit = 0 + self.segment_distance = v_ego + + future = self.executor.submit(make_request) + future.add_done_callback(complete_request) + + def handle_limit_change(self, desired_source, desired_target, sm): + self.speed_limit_changed_timer += DT_MDL + + speed_limit_accepted = (sm["frogpilotCarState"].accelPressed and sm["carControl"].longActive) or self.frogpilot_planner.params_memory.get_bool("SpeedLimitAccepted") + speed_limit_denied = sm["frogpilotCarState"].decelPressed or (self.speed_limit_changed_timer >= 30) + + if speed_limit_accepted: + self.overridden_speed = 0 + + self.source = desired_source + self.target = desired_target + + self.frogpilot_planner.params_memory.remove("SpeedLimitAccepted") + + elif speed_limit_denied: + self.denied_target = desired_target + + self.previous_source = desired_source + self.previous_target = desired_target + + elif desired_target < self.target and not self.frogpilot_toggles.speed_limit_confirmation_lower: + self.source = desired_source + self.target = desired_target + + elif desired_target > self.target and not self.frogpilot_toggles.speed_limit_confirmation_higher: + self.source = desired_source + self.target = desired_target + + else: + self.source = "None" + self.unconfirmed_speed_limit = desired_target + + if self.target != self.previous_target and self.target > 0 and not speed_limit_denied: + self.denied_target = 0 + + self.previous_source = self.source + self.previous_target = self.target + + self.frogpilot_planner.params.put_nonblocking("PreviousSpeedLimit", self.target) + + def update_limits(self, dashboard_speed_limit, now, time_validated, v_cruise, v_ego, sm): + self.update_map_speed_limit(v_ego) + + limits = { + "Dashboard": dashboard_speed_limit, + "Map Data": self.map_speed_limit + } + filtered_limits = {source: limit for source, limit in limits.items() if limit >= 1} + + if self.frogpilot_toggles.speed_limit_priority_highest: + desired_source = max(filtered_limits, key=filtered_limits.get, default="None") + desired_target = filtered_limits.get(desired_source, 0) + + elif self.frogpilot_toggles.speed_limit_priority_lowest: + desired_source = min(filtered_limits, key=filtered_limits.get, default="None") + desired_target = filtered_limits.get(desired_source, 0) + + elif filtered_limits: + for priority in [ + self.frogpilot_toggles.speed_limit_priority1, + self.frogpilot_toggles.speed_limit_priority2 + ]: + if priority in filtered_limits: + desired_source = priority + desired_target = filtered_limits[desired_source] + break + else: + desired_source = "None" + desired_target = 0 + + else: + desired_source = "None" + desired_target = 0 + + if desired_target == 0 or self.target == 0: + if self.mapbox_requests["total_requests"] < self.mapbox_requests["max_requests"] and self.frogpilot_toggles.slc_mapbox_filler: + self.get_mapbox_speed_limit(now, time_validated, v_ego, sm) + + if self.mapbox_limit >= 1: + desired_source = "Mapbox" + desired_target = self.mapbox_limit + + if desired_target == 0 or self.target == 0: + if self.denied_target != self.previous_target > 0 and self.frogpilot_toggles.slc_fallback_previous_speed_limit: + desired_source = self.previous_source + desired_target = self.previous_target + + self.target = desired_target + + elif sm["selfdriveState"].enabled and self.frogpilot_toggles.slc_fallback_set_speed: + desired_source = "None" + desired_target = v_cruise + else: + self.mapbox_limit = 0 + self.segment_distance = 0 + + if abs(desired_target - self.previous_target) >= 1: + self.handle_limit_change(desired_source, desired_target, sm) + elif desired_source != self.source and abs(desired_target - self.target) < 1: + self.source = desired_source + else: + self.speed_limit_changed_timer = 0 + self.unconfirmed_speed_limit = 0 + + def update_map_speed_limit(self, v_ego): + if not self.frogpilot_planner.gps_position: + return + + self.map_speed_limit = self.frogpilot_planner.params_memory.get("MapSpeedLimit") + + next_map_speed_limit = self.frogpilot_planner.params_memory.get("NextMapSpeedLimit") + self.next_speed_limit = next_map_speed_limit.get("speedlimit", 0) + + if self.next_speed_limit: + current_latitude = self.frogpilot_planner.gps_position.get("latitude") + current_longitude = self.frogpilot_planner.gps_position.get("longitude") + + next_latitude = next_map_speed_limit.get("latitude") + next_longitude = next_map_speed_limit.get("longitude") + + distance_to_upcoming = calculate_distance_to_point(current_latitude, current_longitude, next_latitude, next_longitude) + + if self.map_speed_limit < self.next_speed_limit: + max_lookahead = self.frogpilot_toggles.map_speed_lookahead_higher * v_ego + elif self.map_speed_limit > self.next_speed_limit: + max_lookahead = self.frogpilot_toggles.map_speed_lookahead_lower * v_ego + else: + max_lookahead = 0 + + if distance_to_upcoming < max_lookahead: + self.map_speed_limit = self.next_speed_limit + + def update_override(self, v_cruise, v_cruise_diff, v_ego, v_ego_diff, sm): + self.override_slc = self.overridden_speed > self.target + self.offset > 0 + self.override_slc |= sm["carState"].gasPressed and v_ego > self.target + self.offset > 0 + self.override_slc &= sm["selfdriveState"].enabled + + if self.override_slc: + if self.frogpilot_toggles.speed_limit_controller_override_manual: + if sm["carState"].gasPressed: + self.overridden_speed = max(v_ego + v_ego_diff, self.overridden_speed) + self.overridden_speed = float(np.clip(self.overridden_speed, self.target + self.offset, v_cruise + v_cruise_diff)) + elif self.frogpilot_toggles.speed_limit_controller_override_set_speed: + self.overridden_speed = v_cruise + v_cruise_diff + + self.source = "None" + else: + self.overridden_speed = 0 diff --git a/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.cc b/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.cc index 9d177d33..79b902b1 100644 --- a/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.cc +++ b/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.cc @@ -8,7 +8,11 @@ FrogPilotAnnotatedCameraWidget::FrogPilotAnnotatedCameraWidget(QWidget *parent) brakePedalImg = loadPixmap("../../frogpilot/assets/other_images/brake_pedal.png", {btn_size, btn_size}); curveSpeedIcon = loadPixmap("../../frogpilot/assets/other_images/curve_speed.png", {btn_size, btn_size}); curveSpeedIconFlipped = curveSpeedIcon.transformed(QTransform().scale(-1, 1)); + dashboardIcon = loadPixmap("../../frogpilot/assets/other_images/dashboard_icon.png", {btn_size / 2, btn_size / 2}).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); gasPedalImg = loadPixmap("../../frogpilot/assets/other_images/gas_pedal.png", {btn_size, btn_size}); + mapboxIcon = loadPixmap("../../frogpilot/assets/other_images/mapbox_icon.png", {btn_size / 2, btn_size / 2}).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + mapDataIcon = loadPixmap("../../frogpilot/assets/other_images/offline_maps_icon.png", {btn_size / 2, btn_size / 2}).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + nextMapsIcon = loadPixmap("../../frogpilot/assets/other_images/next_maps_icon.png", {btn_size / 2, btn_size / 2}).scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); pausedIcon = loadPixmap("../../frogpilot/assets/other_images/paused_icon.png", {widget_size, widget_size}); speedIcon = loadPixmap("../../frogpilot/assets/other_images/speed_icon.png", {widget_size, widget_size}); stopSignImg = loadPixmap("../../frogpilot/assets/other_images/stop_sign.png", {btn_size, btn_size}); @@ -155,6 +159,7 @@ void FrogPilotAnnotatedCameraWidget::updateState(const UIState &s, const FrogPil cscControllingSpeed = frogpilotPlan.getCscControllingSpeed(); cscSpeed = frogpilotPlan.getCscSpeed(); cscTraining = frogpilotPlan.getCscTraining(); + dashboardSpeedLimit = frogpilotCarState.getDashboardSpeedLimit(); desiredFollowDistance = frogpilotPlan.getDesiredFollowDistance(); experimentalMode = selfdriveState.getExperimentalMode(); forceCoast = frogpilotCarState.getForceCoast(); @@ -162,15 +167,30 @@ void FrogPilotAnnotatedCameraWidget::updateState(const UIState &s, const FrogPil laneWidthRight = frogpilotPlan.getLaneWidthRight(); lateralPaused = frogpilotCarState.getPauseLateral(); longitudinalPaused = frogpilotCarState.getPauseLongitudinal(); + mapSpeedLimit = frogpilotPlan.getSlcMapSpeedLimit(); + mapboxSpeedLimit = frogpilotPlan.getSlcMapboxSpeedLimit(); + nextSpeedLimit = frogpilotPlan.getSlcNextSpeedLimit(); redLight = frogpilotPlan.getRedLight(); roadCurvature = frogpilotPlan.getRoadCurvature(); roadName = QString::fromStdString(params_memory.get("RoadName")); + slcOverriddenSpeed = frogpilotPlan.getSlcOverriddenSpeed(); + speedLimit = slcOverriddenSpeed != 0 ? slcOverriddenSpeed : frogpilotPlan.getSlcSpeedLimit(); + speedLimitChanged = frogpilotPlan.getSpeedLimitChanged(); + speedLimitSource = frogpilotPlan.getSlcSpeedLimitSource(); stoppingDistance = modelV2.getPosition().getX().size() > 33 - 1 ? modelV2.getPosition().getX()[33 - 1] : 0.0; + unconfirmedSpeedLimit = frogpilotPlan.getUnconfirmedSlcSpeedLimit(); hideBottomIcons = selfdriveState.getAlertSize() != cereal::SelfdriveState::AlertSize::NONE; hideBottomIcons |= frogpilotSelfdriveState.getAlertSize() != cereal::FrogPilotSelfdriveState::AlertSize::NONE; hideBottomIcons |= signalStyle.startsWith("traditional") && (blinkerLeft || blinkerRight); + if (slcOverriddenSpeed == 0 && !frogpilot_toggles.value("show_speed_limit_offset").toBool()) { + speedLimit += frogpilotPlan.getSlcSpeedLimitOffset(); + } + speedLimit *= (scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + float speedLimitOffset = frogpilotPlan.getSlcSpeedLimitOffset() * speedConversion; + speedLimitOffsetStr = (speedLimitOffset != 0) ? QString::number(speedLimitOffset, 'f', 0).prepend((speedLimitOffset > 0) ? "+" : "-") : "–"; + static int lastFrameIndex; if (lastFrameIndex > animationFrameIndex && frogpilot_toggles.value("signal_icons").toString() == "frog") { frogHopCount++; @@ -194,6 +214,14 @@ void FrogPilotAnnotatedCameraWidget::updateState(const UIState &s, const FrogPil glowTimer.invalidate(); } + if (speedLimitChanged) { + if (!pendingLimitTimer.isValid()) { + pendingLimitTimer.start(); + } + } else { + pendingLimitTimer.invalidate(); + } + if (frogpilot_scene.standstill && frogpilot_toggles.value("stopped_timer").toBool()) { if (!standstillTimer.isValid()) { standstillTimer.start(); @@ -207,6 +235,12 @@ void FrogPilotAnnotatedCameraWidget::updateState(const UIState &s, const FrogPil } void FrogPilotAnnotatedCameraWidget::mousePressEvent(QMouseEvent *mouseEvent) { + if (speedLimitChanged && newSpeedLimitRect.contains(mouseEvent->pos())) { + params_memory.putBool("SpeedLimitAccepted", true); + mouseEvent->accept(); + return; + } + mouseEvent->ignore(); } @@ -225,7 +259,7 @@ void FrogPilotAnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p, UIState compassPosition.setY(0); } - if (!(signalStyle == "static" && blinkerLeft) && frogpilot_toggles.value("csc_status").toBool()) { + if (!speedLimitChanged && !(signalStyle == "static" && blinkerLeft) && frogpilot_toggles.value("csc_status").toBool()) { if (cscTraining) { paintCurveSpeedControlTraining(p); } else if (isCruiseSet && cscControllingSpeed) { @@ -248,6 +282,10 @@ void FrogPilotAnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p, UIState paintPedalIcons(p); } + if (speedLimitChanged) { + paintPendingSpeedLimit(p); + } + if (frogpilot_toggles.value("radar_tracks").toBool()) { paintRadarTracks(p); } @@ -256,6 +294,17 @@ void FrogPilotAnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p, UIState paintRoadName(p); } + bool hideSpeedLimit = !speedLimitChanged && frogpilot_toggles.value("hide_speed_limit").toBool(); + if (!hideSpeedLimit && (frogpilot_toggles.value("show_speed_limits").toBool() || frogpilot_toggles.value("speed_limit_controller").toBool())) { + paintSpeedLimit(p); + } else { + speedLimitHeight = 0; + } + + if (frogpilot_toggles.value("speed_limit_sources").toBool()) { + paintSpeedLimitSources(p); + } + if (standstillDuration != 0) { paintStandstillTimer(p); } @@ -725,6 +774,41 @@ void FrogPilotAnnotatedCameraWidget::paintPedalIcons(QPainter &p) { p.restore(); } +void FrogPilotAnnotatedCameraWidget::paintPendingSpeedLimit(QPainter &p) { + p.save(); + + QString newSpeedLimitStr = (unconfirmedSpeedLimit > 1) ? QString::number(std::nearbyint(unconfirmedSpeedLimit * speedConversion)) : "–"; + newSpeedLimitRect = speedLimitRect.translated(speedLimitRect.width() + UI_BORDER_SIZE, 0); + + if (!frogpilot_toggles.value("speed_limit_vienna").toBool()) { + newSpeedLimitRect.setWidth(newSpeedLimitStr.size() >= 3 ? 200 : 175); + + p.setBrush(whiteColor()); + p.setPen(Qt::NoPen); + p.drawRoundedRect(newSpeedLimitRect, 24, 24); + p.setPen(pendingLimitTimer.elapsed() % 1000 < 500 ? QPen(blackColor(), 6) : QPen(redColor(), 6)); + p.drawRoundedRect(newSpeedLimitRect.adjusted(9, 9, -9, -9), 16, 16); + + p.setFont(InterFont(28, QFont::DemiBold)); + p.drawText(newSpeedLimitRect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("PENDING")); + p.drawText(newSpeedLimitRect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); + p.setFont(InterFont(70, QFont::Bold)); + p.drawText(newSpeedLimitRect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, newSpeedLimitStr); + } else { + p.setBrush(whiteColor()); + p.setPen(Qt::NoPen); + p.drawEllipse(newSpeedLimitRect); + p.setPen(QPen(Qt::red, 20)); + p.drawEllipse(newSpeedLimitRect.adjusted(16, 16, -16, -16)); + + p.setPen(pendingLimitTimer.elapsed() % 1000 < 500 ? QPen(blackColor(), 6) : QPen(redColor(), 6)); + p.setFont(InterFont((newSpeedLimitStr.size() >= 3) ? 60 : 70, QFont::Bold)); + p.drawText(newSpeedLimitRect, Qt::AlignCenter, newSpeedLimitStr); + } + + p.restore(); +} + void FrogPilotAnnotatedCameraWidget::paintRainbowPath(QPainter &p, QLinearGradient &bg, float lin_grad_point) { p.save(); @@ -797,6 +881,146 @@ void FrogPilotAnnotatedCameraWidget::paintRoadName(QPainter &p) { p.restore(); } +void FrogPilotAnnotatedCameraWidget::paintSpeedLimit(QPainter &p) { + if (setSpeedRect.isEmpty()) { + return; + } + + p.save(); + + QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "–"; + + bool hasUsSpeedLimit = !frogpilot_toggles.value("speed_limit_vienna").toBool(); + bool hasEuSpeedLimit = !hasUsSpeedLimit; + + int euSignSize = 176; + int usSignHeight = 186; + int signMargin = 12; + + if (hasUsSpeedLimit) { + speedLimitHeight = usSignHeight + signMargin; + } else if (hasEuSpeedLimit) { + speedLimitHeight = euSignSize + signMargin; + } + + QRect signRect; + if (hasUsSpeedLimit) { + signRect = QRect(setSpeedRect.x() + signMargin, setSpeedRect.bottom() - speedLimitHeight, setSpeedRect.width() - 2 * signMargin, usSignHeight); + } else if (hasEuSpeedLimit) { + signRect = QRect(setSpeedRect.x() + signMargin, setSpeedRect.bottom() - speedLimitHeight, setSpeedRect.width() - 2 * signMargin, euSignSize); + } + speedLimitRect = signRect; + + if (hasUsSpeedLimit) { + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawRoundedRect(signRect, 24, 24); + p.setPen(QPen(blackColor(), 6)); + p.drawRoundedRect(signRect.adjusted(9, 9, -9, -9), 16, 16); + + p.setOpacity(slcOverriddenSpeed == 0 ? 1.0 : 0.25); + if (slcOverriddenSpeed == 0 && frogpilot_toggles.value("show_speed_limit_offset").toBool()) { + p.setFont(InterFont(28, QFont::DemiBold)); + p.drawText(signRect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); + p.setFont(InterFont(70, QFont::Bold)); + p.drawText(signRect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStr); + p.setFont(InterFont(50, QFont::DemiBold)); + p.drawText(signRect.adjusted(0, 120, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitOffsetStr); + } else { + p.setFont(InterFont(28, QFont::DemiBold)); + p.drawText(signRect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("SPEED")); + p.drawText(signRect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); + p.setFont(InterFont(70, QFont::Bold)); + p.drawText(signRect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStr); + } + } + + if (hasEuSpeedLimit) { + p.setPen(Qt::NoPen); + p.setBrush(whiteColor()); + p.drawEllipse(signRect); + p.setPen(QPen(Qt::red, 20)); + p.drawEllipse(signRect.adjusted(16, 16, -16, -16)); + + p.setOpacity(slcOverriddenSpeed == 0 ? 1.0 : 0.25); + p.setPen(blackColor()); + if (frogpilot_toggles.value("show_speed_limit_offset").toBool()) { + p.setFont(InterFont((speedLimitStr.size() >= 3) ? 60 : 70, QFont::Bold)); + p.drawText(signRect.adjusted(0, -25, 0, 0), Qt::AlignCenter, speedLimitStr); + p.setFont(InterFont(40, QFont::DemiBold)); + p.drawText(signRect.adjusted(0, 100, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitOffsetStr); + } else { + p.setFont(InterFont((speedLimitStr.size() >= 3) ? 60 : 70, QFont::Bold)); + p.drawText(signRect, Qt::AlignCenter, speedLimitStr); + } + } + + p.restore(); +} + +void FrogPilotAnnotatedCameraWidget::paintSpeedLimitSources(QPainter &p) { + p.save(); + + std::function drawSource = [&](QRect &rect, QPixmap &icon, const QString &title, double speedLimitValue) { + bool isActive = QString::fromUtf8(speedLimitSource.c_str()) == title && speedLimitValue != 0; + + if (isActive) { + p.setBrush(redColor(166)); + p.setFont(InterFont(35, QFont::Bold)); + p.setPen(QPen(redColor(), 10)); + } else { + p.setBrush(blackColor(166)); + p.setFont(InterFont(35, QFont::DemiBold)); + p.setPen(QPen(blackColor(), 10)); + } + + QSize size(img_size / 4, img_size / 4); + QRect iconRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignLeft | Qt::AlignVCenter, size, rect.adjusted(20, 0, 0, 0)); + + QString speedText; + if (speedLimitValue != 0) { + speedText = QString::number(std::nearbyint(speedLimitValue)) + speedUnit; + } else { + speedText = "N/A"; + } + + QString fullText = tr(title.toUtf8().constData()) + " - " + speedText; + + p.setOpacity(1.0); + p.drawRoundedRect(rect, 24, 24); + p.drawPixmap(iconRect, icon); + + p.setPen(QPen(whiteColor(), 6)); + QRect textRect(iconRect.right() + 10, rect.y(), rect.width() - iconRect.width() - 30, rect.height()); + + if (isActive) { + QFontMetrics fm(p.font()); + int textYPosition = textRect.y() + (textRect.height() - fm.height()) / 2 + fm.ascent(); + + QPainterPath path; + path.addText(textRect.x(), textYPosition, p.font(), fullText); + p.strokePath(path, QPen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + p.drawText(textRect.x(), textYPosition, fullText); + } else { + p.drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, fullText); + } + }; + + int signMargin = 12; + + QRect dashboardRect(speedLimitRect.x() - signMargin, speedLimitRect.y() + speedLimitRect.height() + UI_BORDER_SIZE, 450, 60); + QRect mapDataRect(dashboardRect.x(), dashboardRect.y() + dashboardRect.height() + UI_BORDER_SIZE / 2, 450, 60); + QRect mapboxRect(mapDataRect.x(), mapDataRect.y() + mapDataRect.height() + UI_BORDER_SIZE / 2, 450, 60); + QRect nextLimitRect(mapboxRect.x(), mapboxRect.y() + mapboxRect.height() + UI_BORDER_SIZE / 2, 450, 60); + + drawSource(dashboardRect, dashboardIcon, "Dashboard", dashboardSpeedLimit * speedConversion); + drawSource(mapDataRect, mapDataIcon, "Map Data", mapSpeedLimit * speedConversion); + drawSource(mapboxRect, mapboxIcon, "Mapbox", mapboxSpeedLimit * speedConversion); + drawSource(nextLimitRect, nextMapsIcon, "Upcoming", nextSpeedLimit * speedConversion); + + p.restore(); +} + void FrogPilotAnnotatedCameraWidget::paintStandstillTimer(QPainter &p) { p.save(); diff --git a/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.h b/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.h index d5fe444d..638c48dd 100644 --- a/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.h +++ b/frogpilot/ui/qt/onroad/frogpilot_annotated_camera.h @@ -25,6 +25,7 @@ public: bool rightHandDM; int alertHeight; + int speedLimitHeight; int standstillDuration; float speed; @@ -65,8 +66,11 @@ private: void paintLateralPaused(QPainter &p); void paintLongitudinalPaused(QPainter &p); void paintPedalIcons(QPainter &p); + void paintPendingSpeedLimit(QPainter &p); void paintRadarTracks(QPainter &p); void paintRoadName(QPainter &p); + void paintSpeedLimit(QPainter &p); + void paintSpeedLimitSources(QPainter &p); void paintStandstillTimer(QPainter &p); void paintStoppingPoint(QPainter &p); void paintTurnSignals(QPainter &p); @@ -84,6 +88,7 @@ private: bool lateralPaused; bool longitudinalPaused; bool redLight; + bool speedLimitChanged; int animationFrameIndex; int desiredFollowDistance; @@ -96,14 +101,23 @@ private: float accelerationEgo; float cscSpeed; + float dashboardSpeedLimit; float distanceConversion; float laneWidthLeft; float laneWidthRight; + float mapSpeedLimit; + float mapboxSpeedLimit; + float nextSpeedLimit; float roadCurvature; float setSpeed; + float slcOverriddenSpeed; float speedConversion; float speedConversionMetrics; + float speedLimit; float stoppingDistance; + float unconfirmedSpeedLimit; + + std::string speedLimitSource; Params params; Params params_memory{"", true}; @@ -112,12 +126,17 @@ private: QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } QElapsedTimer glowTimer; + QElapsedTimer pendingLimitTimer; QElapsedTimer standstillTimer; QPixmap brakePedalImg; QPixmap curveSpeedIcon; QPixmap curveSpeedIconFlipped; + QPixmap dashboardIcon; QPixmap gasPedalImg; + QPixmap mapboxIcon; + QPixmap mapDataIcon; + QPixmap nextMapsIcon; QPixmap pausedIcon; QPixmap speedIcon; QPixmap stopSignImg; @@ -127,6 +146,9 @@ private: QPoint compassPosition; QPoint lateralPausedPosition; + QRect newSpeedLimitRect; + QRect speedLimitRect; + QSharedPointer cemCurveIcon; QSharedPointer cemLeadIcon; QSharedPointer cemSpeedIcon; @@ -138,6 +160,7 @@ private: QString leadDistanceUnit; QString leadSpeedUnit; QString roadName; + QString speedLimitOffsetStr; QString speedUnit; QTimer *animationTimer; diff --git a/opendbc_repo/opendbc/car/toyota/carstate.py b/opendbc_repo/opendbc/car/toyota/carstate.py index 8f07935b..bc309adc 100644 --- a/opendbc_repo/opendbc/car/toyota/carstate.py +++ b/opendbc_repo/opendbc/car/toyota/carstate.py @@ -25,6 +25,19 @@ TEMP_STEER_FAULTS = (0, 9, 11, 21, 25) PERM_STEER_FAULTS = (3, 17) +# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team! +def calculate_speed_limit(cp_cam): + speed_limit_unit = cp_cam.vl["RSA1"]["TSGN1"] + speed_limit_value = cp_cam.vl["RSA1"]["SPDVAL1"] + + if speed_limit_unit == 1: + return speed_limit_value * CV.KPH_TO_MS + elif speed_limit_unit == 36: + return speed_limit_value * CV.MPH_TO_MS + else: + return 0 + + class CarState(CarStateBase): def __init__(self, CP, FPCP): super().__init__(CP, FPCP) @@ -217,6 +230,8 @@ class CarState(CarStateBase): *create_button_events(self.pcm_acc_status == 10, False, {1: ButtonType.decelCruise}), ] + fp_ret.dashboardSpeedLimit = calculate_speed_limit(cp_cam) + if not self.CP.flags & ToyotaFlags.SECOC.value: fp_ret.ecoGear = cp.vl["GEAR_PACKET"]["ECON_ON"] == 1 fp_ret.sportGear = cp.vl["GEAR_PACKET"]["SPORT_ON_2" if self.CP.flags & ToyotaFlags.NO_DSU else "SPORT_ON"] == 1 diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index 3ee82532..a91b526e 100644 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -1180,6 +1180,14 @@ FROGPILOT_EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { Priority.HIGHEST, VisualAlert.none, AudibleAlert.prompt, .1), }, + FrogPilotEventName.speedLimitChanged: { + ET.PERMANENT: Alert( + "Speed limit changed", + "", + FrogPilotAlertStatus.frogpilot, AlertSize.small, + Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 3.), + }, + # Random Events FrogPilotEventName.accel30: { ET.WARNING: Alert( diff --git a/selfdrive/ui/qt/onroad/hud.cc b/selfdrive/ui/qt/onroad/hud.cc index 17a1db75..30729192 100644 --- a/selfdrive/ui/qt/onroad/hud.cc +++ b/selfdrive/ui/qt/onroad/hud.cc @@ -64,6 +64,12 @@ void HudRenderer::drawSetSpeed(QPainter &p, const QRect &surface_rect) { QSize set_speed_size = is_metric ? QSize(200, 204) : default_size; // FrogPilot variables + if (frogpilot_nvg->speedLimitHeight != 0) { + set_speed_size.rheight() += frogpilot_nvg->speedLimitHeight; + if (frogpilot_toggles.value("speed_limit_vienna").toBool()) { + set_speed_size.rwidth() = 200; + } + } QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size);