From ce1494895e862598a9951243e054fde1b7942d6c Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Dec 2013 12:57:49 -0500 Subject: [PATCH] Commit of common limelight core with bindings based on HEAD of RenderScript-Renderer --- moonlight-common/.classpath | 7 + moonlight-common/.project | 17 + .../.settings/org.eclipse.jdt.core.prefs | 11 + moonlight-common/libs/xpp3-1.1.4c.jar | Bin 0 -> 120069 bytes .../com/limelight/nvstream/ControlStream.java | 505 ++++++++++++++++++ .../src/com/limelight/nvstream/Handshake.java | 133 +++++ .../com/limelight/nvstream/NvConnection.java | 321 +++++++++++ .../nvstream/NvConnectionListener.java | 32 ++ .../nvstream/av/ByteBufferDescriptor.java | 46 ++ .../nvstream/av/ConnectionStatusListener.java | 7 + .../com/limelight/nvstream/av/DecodeUnit.java | 42 ++ .../com/limelight/nvstream/av/RtpPacket.java | 46 ++ .../nvstream/av/ShortBufferDescriptor.java | 28 + .../nvstream/av/audio/AudioDepacketizer.java | 65 +++ .../nvstream/av/audio/AudioRenderer.java | 9 + .../nvstream/av/audio/AudioStream.java | 223 ++++++++ .../nvstream/av/audio/OpusDecoder.java | 14 + .../av/video/VideoDecoderRenderer.java | 17 + .../nvstream/av/video/VideoDepacketizer.java | 213 ++++++++ .../nvstream/av/video/VideoPacket.java | 56 ++ .../nvstream/av/video/VideoStream.java | 290 ++++++++++ .../nvstream/av/video/cpu/AvcDecoder.java | 44 ++ .../com/limelight/nvstream/http/NvApp.java | 31 ++ .../com/limelight/nvstream/http/NvHTTP.java | 145 +++++ .../nvstream/input/ControllerPacket.java | 89 +++ .../limelight/nvstream/input/InputPacket.java | 26 + .../nvstream/input/MouseButtonPacket.java | 36 ++ .../nvstream/input/MouseMovePacket.java | 42 ++ .../nvstream/input/NvController.java | 65 +++ 29 files changed, 2560 insertions(+) create mode 100644 moonlight-common/.classpath create mode 100644 moonlight-common/.project create mode 100644 moonlight-common/.settings/org.eclipse.jdt.core.prefs create mode 100644 moonlight-common/libs/xpp3-1.1.4c.jar create mode 100644 moonlight-common/src/com/limelight/nvstream/ControlStream.java create mode 100644 moonlight-common/src/com/limelight/nvstream/Handshake.java create mode 100644 moonlight-common/src/com/limelight/nvstream/NvConnection.java create mode 100644 moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/ByteBufferDescriptor.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/DecodeUnit.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/RtpPacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/ShortBufferDescriptor.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/video/VideoDecoderRenderer.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/video/VideoPacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java create mode 100644 moonlight-common/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java create mode 100644 moonlight-common/src/com/limelight/nvstream/http/NvApp.java create mode 100644 moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java create mode 100644 moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/input/InputPacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/input/MouseButtonPacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/input/MouseMovePacket.java create mode 100644 moonlight-common/src/com/limelight/nvstream/input/NvController.java diff --git a/moonlight-common/.classpath b/moonlight-common/.classpath new file mode 100644 index 00000000..62423bd8 --- /dev/null +++ b/moonlight-common/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/moonlight-common/.project b/moonlight-common/.project new file mode 100644 index 00000000..aff885f2 --- /dev/null +++ b/moonlight-common/.project @@ -0,0 +1,17 @@ + + + limelight-common + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/moonlight-common/.settings/org.eclipse.jdt.core.prefs b/moonlight-common/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..8000cd6c --- /dev/null +++ b/moonlight-common/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/moonlight-common/libs/xpp3-1.1.4c.jar b/moonlight-common/libs/xpp3-1.1.4c.jar new file mode 100644 index 0000000000000000000000000000000000000000..451ac82af4a30c845cac77137cd9f64f5b5d60a3 GIT binary patch literal 120069 zcmWIWW@h1H00Hr(S*9QwhB+A+7<^qr9CbbY-1K3}I2gp1W|=;1vagO~U|`5%U||_XlJ6iR=R=UxsE z9W6iIB5xnPllrTkFu`5P!N3o7B@;Asg&7zaA_4-8;|=u;^-PlE!(4-cJ^lTd;aV6N zzz&5f2T^RG08cDStblt4Qw=C4v%qR9a&z#g14SA_U0z~tYH>kgaw=R02iW#y?{o{? zSQ!|0a4|5*!)*tdjL;G2mzbNXmzeYvLeC+Wtk-rcf9 zsg8lQ)x$LC)+MH>kc8QH%%aU_Pss5SlyJXa^VPTZp=-qk{s-<>%EnBKliiQkJEfPD zxE+`L&+zrm?%O83-H)Bm?frhIxbFF#`>eNwM@xE{Z4%MgVK%r6pvIY>TwhX zayYxp5_>eoA}#dE0nsxWGg332woWQey`(wYY_65tvz|)jRLO5Sr>@E-UC3E{KwDM$ z^m(5YiA71^R)k5x+*brR)^H=d5XE`HpQpMKh;k<>hmk`Q;(DTCpXpi*OX_xP+l7K%&Mb-zoDlACP}w>1^lL#bFN0Sp8>7yBN(>f>nOgZy zbcR&bhS1l0BUQ&9E4%!xL4myT*R8CCV-=-)NRuFlZsZBEJ>{*AK>o3@!%q-IR ztXp1iZT$-4WZs%DYkn=)T^uB-YjybOs=3()ou>O|L|xsY8M=#Gda2O%;*c*czaCyI zU%7D6cl%A6wPz3C+!JW$<dsaR5xpG_n_DkcNyluw?4~1RqWMEqK^~tWw(d8#Ym91Z? zPpW?Lg{hLICG5Y5j^}<=mUkYnSv*7U&2`t-ThL!Ncb#sD3gb@U8$L?ksymchBt-UA zZMrY7->>1*dHr$JwT~e)jVD_-%1k)HEcZ!0MRt+tT7AV8>eB^P|3n8&GHu!C;Qim| z);lGes|$4$i~QeDzxCokSJ>2Bj4N|veOFGO#CVD&X5sU*!nM;oS7?8klPz(iROZNS zi?;HVUAv_B&CA<0FYlIj_#2k9j+frKp7OR))ntnLa{gk`ZaFKRsqZ~A&YFH$Aldq@ zxX;Xb|NGZ}TGkh5Z~pAQ@7(WK=bo$nKTtQ(EU~Y+=6Ldw^BqqOk#Vd39=xpouT}hqQ_L=B%^%bXp(DEGadndpo8ZA?dPnwTt_ zo_lc3v0U%4*Ce<9rQVDgR(DxN7OQPrd-n5HPgcfmg(D$bHizY;eu+PLOX}3cXnlA6 zdCSd>W|^N*TJrL<$iYIM>ioiV-j1$SEq2Vim95LZ3aWeV({SGZ1zrR*FbF~G3{c?@ z!Ym974EaUrNGi~)2p*^kXw8MBQW(uFK{S=Q6$OTHQ*A zVFrVh`$hzS>qAKCzh^Id%~aQK_BCnGUY=KNIl$^9w4;Q>lOxnih()N&p=qJ8pi@y} zf=3e9(XJ+5od`n(0fF9JQSJpzHxf5~zxO-p+Sc7-pXM3+@BH5O;MKqH-(R)Ae0pZ4 z@%`duHs<~^JO49qc!640Q9yR}PTC;@JGJI08sAc%%;@)%D zChVU$C;Q||^>ks&*(Z1Q&Cz|5qna*l$@?TnJ)Pb1`sB@%=SZK}IdP6#QJ&KCw4yx4 z=V3*8%Fpwk-0?dn`{a$%bHAc@O3(9(-YKS=TUMXA>3weRlRNI`{yur5oNjN)KcTo` z4*SWBnXf+mw&{KOcT#vykcU^{O);r~Ff~^q=_I#Pa-$ zKatJ$=?5+T?Kv2)aBkm<&(nGSZDO7LLp$V2X!HKG57#yOT<;nDn9Ww-ZFKM2PiwI= zPuO|?>HT~^@$`VV` zJ@`NML;i|a+e2Pi7wuEGs#AFG-u2Jr+@fO^KWb zc@=pU&d8Y)z`HPTQXa=)0X0@#=NnJhPh_TcSS)efV&-D=GnZ%Lg6?f?8R-X)TKEW> zxKulrEZ8K=J29ifug$~AWs_o%##&>`*b|(Ri&iV{l3cV~u}Wgma>XjiMdnYxC@x>G z7XF5#_KC)G<_JFNF>w+5dGpB= zi6oJq$0r*{F__329+CLcuMmDLC~2MICmzFd0*`qO4+;qL7B(rWNhWR5TPC4&Uw}0v zYn7s(iOWL8Ws*s|6w@S(n(tt&n-Mt%)7De&(*)+_9&zXT$e~%thml~rqI{68z~)U+jg82 zi00XNRp2(y#RqGo8D#F1dA||3E}gWTwcXPu&%ol-S2t%9sYhzgcXB$SOA1#j=53D9 zs-KtfQ`(o^@9^6rebzjV0ty^VM`EYcny1$1c2x6hye&}8v+=sX_ozp9h4vRW_iMd1 zKf>H*kuEhoLZr;C5r*IyTLr#>pT zH1g=9i9AugYuBt;sk?5)+{l%{pJOrA zv0C4@{>zj-nV;34`JdN5bN+Pdg~F`FgID$(dRe@}E$`lqlb_R{iTcfsm~dX<<%%_# zm))N~@0%AhVgB`?qZ?UoWc@2)%l)(FBx|y}|MXr}k?GT|l~>G+2$Wv8j$^t}La`*v zGVwmS+2-}Pj_efve4KlhXwHIFyEduB1W%6NqZ1Px+T)nBVArN~i$tPV%nAz+4ULTT z=9PFlRrToB)TyF8Yd&r2DyX}=sr6x2jzpxO#!8*_(P0l&B~2d|xF)8i>WZ!96Q8cb zIsI8^f^b?z<&H&VzgB5INqPNY)2dscl}{h0P6^pFqcKWq-KtoprKh;N!v$=4Tmmmf zGm!E&s7{hG&e2wSiMe!RclY((~79yf$JN$ zoei4Pr*t)CvzpO5o=6Yh2)`SOn?hBLmgNZwy*yv z>N6J9i)&kbY`uZuB%iHf*K}vaM8;UH+p%PukhBW-ILp`AWqud@yXcM43(o~h1yZIOmpfl}yE)2(q!M4$G=g^8|Pr#bDi^GD62 zSC?N2RkYWb6df5PrKjm`l%?&@89rrh+vjCh+QP5>x*RSM_rxdt&Z*R=J0r@}wyCw> zJm%}*rRCLqTHEu_Z8y%pGftiQR8V;Igh!~$#wg3Txn9#m!jn(4iay*ZF{yanZ;d6- z`*lyaW;opJa*{Vx&zIVywx8j+;m!pKmwHxyUfwgWw`suwg)Mzc7EI~UbFp!7+p4>M z&5?~le<#IW5%*O~3W!lT?b*AY?|Mu2+*Ye;8=6`Nd^ zZ7aU1lxpGRE}dh!UUJ)(E>CCAj!y5JInULPE(mX*HYt@=oI^G#R5NPFzRjn!PGuE8 zv05W5uD8yWX`*Ru-@M;DCmk1^BqJ(Xulp|1j*P?se-W8wRlorkiQ zhlM}-w0z7JgvxO=hm+p9s>bwizFW3(rVg{RJc zmf$=0laa`?D|WhvinwbNcmv%#E}vd+$ee!1RM_Uq_sBMpBl=~U9V=SOYNKMxJ(p^< zi9TGMR<`TN(fb4}zpEhw}X-VQv2d0af|10DE zOpB0wDZAa8S^8w@8u=r3Mk^-pFMYhKTip6n-j|s#)|$3SopPy|6%=MT^@>FF3lFWA z+Jb7Sm*=r8-L$Od#(e2@nvTZj0{f3C?LQb6kybQC(EYKLbn;==Q(0-cnybGaYfV|0 z@H#^0Pym-;+u~oYS6dFh3^v)Qula`0TkCY-o@1e5-e2Tn1FbA$HqLNczWdH0Lyy9; zw)@N0sIA{=>LJ^cm)6U=`0}C|>y$Ph(w6mY`rx_vn=3GZ$MnN!hEK?^&^9 z-JPiucAoVp^xctI?iq zEVhpnJM{Bwjylh&Jr+6pHq}@rI;?H^2r}o zFIra2n4h7qsi(?$T6dl9w<|BF6nsrOzHW`u*1ic5ah}eRF_G52B5n6iC-F?mHOQV7 zZnl1v&$Op1I&W+LoYIyM?+wq3ygtSF>8&D@roPvwIZs-aSYGg&)<2`w>SbCd@3bqI z?rm#UVvea-(7Us=Wl6}H19yX$Zk|<`uOum>e)~(S-@5%9O^UbNU&yqKS=Q*x2Bqnz zR%&@2d_CzyjSt_1gEm*UmqnlMnH8D3)jLgJdwJC{uTQFsRhc{P7;b(XW42?P##*+W zJ4&y&xbS%euVekX=38OGfx8Pmj>=4USkwQnAxE~iT>QZj-#2fH_AC&->iuffT-kY@ zitLL|xJ@s%pSXd)d%nl!0JFV2sthA16;0VDKK;=*F7>K-QOqojUyC+#KOYN58PlX^UuFr0RCrs@#uB*S8!}StGAD zbJ{mGsb2SmotqaP5>;50`BC$<7H22V@dbDsRz zT`6y-)zd6F5mQKt*JDX#J_|bz67Dw#3 zHyA{VBz(Rp6Z^+jfv;PgDaUZBj^gs3sVo78i-Wz+F@?z_EDtzlRHSucrOI=cFo}ex zAx}cO!Zunm#+;E@wQS4OkhRk;z0%0FbPW&45Pf7}(V^#ow|-73W3&{Xw0Yi~(kC%4=Tx7>c%4fPsbIF8KDij)zX+^<&Tx%0c63Y9S;WjVXAjvZ~_iD$#d&l&f z%^FX$f6-s7^YXn}z~qff$|5Vvzi-jLd;Op(og9@R(?r5sw6sr!3s!ba7VSQ~R3fH@ThrzA4bg?-%sFh%Q!T!n57PbZqyL%z zb6;`OmCa|kriQj;{Fl~pKC0^&G4D#NL2E^*u;o$fck7K-U1?tWF7WVKT?y~w6Lh!O zKRLgxpY_t;+*Kx}6iGR1UZ_WO=?P{;~Jm3Ff*AYt=t_|Wh z*78hJHEPd#;^u!U_slA**=y4#+&DDv!cEIdPX9RyYmR*>e0f;n%Masd$CzYQJAUsC zYi|{tuyo^j$i!KxW!%yrC#9JF{VGHC{91tof29>2*DLBA_MEHZ*JE}qYBw@ATG41e!R4mxO7?Q^mWTTm8VHH%E$u4WExam5BreEHpm+qC>@aGo3sAubE6Y0R`Bc$ zmUJ!FRny~~K1V5XXOmf;qWHZVZf1;IYh#aUicZxQPzzvg|5IG?#CZ3#PwigXi>1#yIG#|=j5jsCwuNOa^Ask=V!?AXMOYRtPb>D_|(5jQ#{+aFwLOu zsL+$#D@WWKC7U8LBY6uqnuy%)Qx25l^z%NwD9df@b5X8v3tq*SdhWj!&WoLNej8fb zIl1VD#lBga(l)tuZ`pBcl0goBWcxUPF(bip=_=HDo`Luq{4=+1RYBb$H@9>Uoy&u$Be!{V; zY^Kez<)x?2^MA06VfkVC=MKaCL!SdPT?EeED zd%CcenlXEJ)q=LUl{3z=3v^1FaBmiy8mjD_lu@*y#Z}_<`ahZQ+ z>f>X*r(e&0Csxv1Xe1rLyhdZ)s!6{m*cDA&RK>Z|%YK$ni?!FZZ;=i9ilUeFx?J_b zH|3ZYn$SpJ>NJ)WwrgbS&wx# zrXJm@>OJdxgqV^aU*d&DuTG^Z?)vKbY1gwK-J8ldzbP-@rnXA!+N)QWCpmYQ2)^5v zHLYdQ^45b#><^^yyFESWVKPVjdesdHQ!h5*nv11b3i*rGf~UC%e#|m^KSONZ?c|2H zsu%rM@`X=q43c`KpPQ|6t1XT=Od!?Pm2migXq%b+u7caL=_jVUk{& z=o9ZYr*FXrr=aDV*|k)pMKvB9kMz&pX|CgB|6x;SBxdin{;Z|&gY-|EnR2wFg&(Z`k;(JR-RDK{>#oy* z(-)@ke?9r`HQ$Z9Tii3h$Lx2z!u9s@ZvC}4PcSXpD)s8}+Z$(l&Plm4e=?d=`r7>M z8atVc`HLQXx$L(5{8=A)JNx7#doD;Wy!Z0P^u_67pM=;lg+$m>QiQ5p`AiG;C08%d z+RL0HJ$HZVzxKf2(w{i?R{lS`Avz;{hV#_<%IPLmcLKXNaj^Fto-cmdX#cB?mCvp? z&r3bipDLNX;35AX{=@xqwz>3ZY@3_4&GOYs--f*>{5dzsZp?YRS1ac7?1;;|7j62! zF?wmW(0VS}s6D;Q?p$0tx6tZo_0Om7PcO(!7k~U|g;L+biH|=V*PJ2HYQK{;dHMO7 zv)Zk{c_dA|<08J5#m9Ehs(+3f&lzcl2Ah2sxv&?lrwssK0JNr?3-l_*)Hw#eKn5zuTp5g7d$t_+b_T9RmsOXhnfTX zmQCKt&iu&vqo7vY|1E*v^=F@1;q0D-Dl+k*|u70=xknsUkB%IN!j z_OmGqrtZvNwn#rD?CO=a>58^TuUER1A)fAn0;o$@VTcpmiM5UOpr zuk|c_$|a<}T>nnn>F?5+^{+TP>m$>zgjM<9x)inO(+$(Nx4d>aNc##m{GGl#-(*r$ z)$d2)L7Xnj61RW3bGmZj^edNgXU8k*eLE}IWv|P>%UW0Jc8=W3%)3ciO;db1)_-YR zIx*GoR&Bqb*se`6)7X1@n=>bzIkuToVjc&BOjy#NmK8rTCFYBW&hPtpGVmi)kAG*^ z`QsIJAvKK0xEDP-DluP5RG+DJ-3Nm^oxaZZebX-+*0%YCzesZawRwZw&&@YwUY;-g zt7QIP<@)^R%jUm1_I&q8)-@5oxFb8ic*okG5%a9y_*%d4uaSBE%IN#y>lz z9kc(^*VT7EE#LJqYkN(v^!n1zukZXQn!ZjnuV$%qyy|P!&6hXqIX?Ze;a{GzWoPV9 zKRtdkXX}^qg|}^|ubucW%Disg>Gscef3U8rU-&xy$*Y-TsS9e@9(kU;fuOXY-f!#gC&?{+~=q^{Su!+I+{q zvg`LB{<`k|F;o3meuBB%(WOiWHe6wQ@jRJ-`-bR^M{*@ztH*zOyx%(YUMtLF}sp4XRlN{SaO(EvP(G% zMoI)tW%a!MFkYnV`gUiBr57f-^+>*Idb4Eh0__l!_TGR=@(d-xuNInaRS*)LSfZb? zvh!44$JxutpT5knQC2(eaWB6&z)+iUv(Lm8r`EU`7^rXs317XNQ02N}+f&hrkzpm% zWGAO4FK-l_HT$}kN><*yZPHP$OCHp`=*qL)(wI7dWvYQdmI8NHqKHnrO$4K{fKhjX zDAVoC09`eBp>`?}`hhiVbb+YeQ4;MQxf-N5(lU{!^HUYq*E zpb9y&1KI_adlsW+(mt7U@F#BL!-5|#4_mA=00_&gR)|2_qD@Z?@ zbFzQ(RjZSy`@ zq#SaTV5x6^SRmoopq`**)5`reGlJ**0~`DH;y*TPTEz?I#U0vNDY@l$L-nJj z;!D3?^)uSK_wW_DNh>AZ6$Gr?&UYkize&+H=a#j}3UYVPPyC`kVQu`Q1^-($e(~}+ zT$EgcElu3e~L8(Efa{m=rpPYI{%H$fG&NZ>;yOft~aygNv8F<0E zOLp3+4yW%PpER~aPf9;1@U4Y=um7(LyX81dH(XInDRxiqv;K8qcE^F;-8Hi^Irns) zOGy__dgjei+z?uNfycYVio^0rXUVLcF6p->$7mKTKDkb0$Hl&PCZ{ug<-Jf*Rrd69 z+cf8U-?|!sIjQZaI#N|n{o)_zex(jVf9hJI@*rXYnZ#YPtV^j2I zbC#24ntU^0#_7IhBQu_`O%Dqgr8&hn>+EPsRsQnyKv(_t#NU}0Bs=Z7L(0rfz0Arud##_E$4eu$?`bjqge1x>Nsra8iXXVWYuFBD6To-i@Guxs1F zr#aR!hszG6mD!qXkWNtFZFnkZ*TGcX#n*G*P1b(vN|Ee z+T!1;wF%oe6^~`*O1yF3DLuKXXln2OV=@s!@lpGvk8KNx=(V4B>e(`_Cr@2>T!@@y zY<}?>(@q1~s*N)(F0lq=Oi`S1N5=JK%=}B!Z=8;py(~^)>6hH+s(<+$SJxWk`5pSP z*Wls7md?N54sd?e;Q8)Qe#QEC!lgMaCs*d^`&1g3R+>LCKeVTXX}QnrCsi}FxP9(& ztJ}>8knoh#o4owwlb3#*e6OaPIHjMu#curkO=~3Q=I;k&&dl4){w(O`i3ddsOob}- zql&&O^4#q=*dqUHb9UnK8>?4ru&CypbN;fLVs>15{*K8zIA2c`wOp2ZP2$9?Keuhv6h2fj zFMsgGW2WTxPL3_hluVeag^zz0>wBIXbNRia|2MlYY4Q$gzXgtd5tg}~USRV(=F+`; z3lA)>?eVrw&QIbl;<|Vv=m|%=iiqXjV@sFWvTT_oy|CJ3x_{BmIR@)h!?vq0J-m-` za&5Q6H`#Sd-X|LTHtcG3t}$x+p(AtXUfO$;Ev^|;l4=-5PVDyVC*JzS=$ z({ZHjy_ZJc5w|y=+FUlC+kInQTF)O*$)D0jpUP972&SY6FG(tIwrEbAb2vjUK_+~$ zgyli`gDmqWzE^%N>(+PJDCCsWjK~?c_ziUr&9!*Mu-)+?qe12B6=u5oeYY6;U<*1)dZHtg?Z>mLg zrb(~&MbD6L2Q)3FuQ>MPmd}0Wrz4qDZ1maW5#LW zh0O0+Vk?EdJkY!_zr49|4>!*}NkLY23Z&>DF_qDv3LtV=pMbI>J6%cm2+U4@wzlpSuPcU0I&8 z!e(#h(kJot9-hDd&zj`waWZMik~jXrpLh=~Q@;8>k=x|v(!YF%K3?e#nl*QiWY90c z{7LLyHj}TY^55Ay&D_+icFu-Fj+VNKPuI1C`t2w`l(}1Js_@)h?Md4Il(_`l6`mfL z$i0W{<+|3CIQF}Scc!mD61tbG?Zf2&1Aq4b8*a7Fn-kgMdsqL_i8yiob8nUO*=6sS zq*^js?NpR$w|^J(Z7=sM`wjBZ5r?x}_RKNev#-@O=VEo(b|ab($xHPF`kpk7o;H_VaD} zDK_KO_CqH7&bIH%xs(2X_FavS)oQ<8<@kbLgnvJgsrLJ9>NTHlyYGB2alY%xvroab_K>_*nh$OU|K{J+c?(^&QmkKQ`I>JJa&4+U*VV z_J~?l1l777shIU<>D|qd=eu@1 zWo?3XyXTd(HXTm9_T+@v-lROoZM#gSuVs(A#Q){f&el^4pG}p|+-T(9+_<~xrG(zr zBaXGR@2oB}ms%k3mvM*1#nefuBNit{Yvu@ujm!`s@mw$+!yjyj1=MncuNNnUMYJbuHp(`tEPWYB-`%gqJxvPBYZu)ea zm5=T({`QM8@|*IO=k|&_{`M?8sXyUheXHOfbBUv~6V@6e>@`SSyy1aW;^DPQeDB28 zCT`CVUivZa%f$b#>oyB@{<;5S(k7wP0-K9f+@+TEohm=S)l-sZ%8QMsMLUz9xMdpb zWE501a4~B=@MoLws@K1_|b17d?@#!oV$l-!n~X zqr`W}ycTs`T_>_O?^NKNnLT@-C%)hcIo;26@PgS{a zJ^z=EhDENIb-NbKogscTBZ=D~@%N8eZ~2yOy}joY=h-T={1U~8MJlT#6#Uo43>2F%SJJ0>`&5)S8`Sj+>>b!Q7Q{Tf5r=H7tv2x0!qFVyF zUr)XIn8D)Ib5(nqg7|c&O%|+vBBvKUsokdkesf{BxqH@Pi}|NY^``!G5B_=ol9;&f z>@7B~rkBFFA0PdsvFlSU2u1xa$t`&~t5oNmV%h)gEZgj6RW4UYzpKPJ@_PQB2z$%*|VRKHvOpz`x7-fBREOkwqG{OP)5B zZMk}D@64-O|LV3}m0~_8Tj9Q3@}5}sgPM)Cd`w^btvUWKDnArHIk4V0<(`n4TOZ40 zgSWTXUUZc{x?~We|8vHKx(T7nl1qf$%r{I(_qD|33vW`wY?!xI zh_Q7}Pw1=Yy{rFEaA$Y&Z=07%zbh6cKTFooSlV6Wd2Wug#imB}jUgh_{N99xKfAV@ z{X}hW=&^4H_*AtswWHQoKlt?~WsB#Tt!q{azDY5OFxi>^Q^RSN$@!z-7j(-X5V3dC z{wSPu`uqjQKhkTb#4nirL$s>v{=$BXNAJCa3XE;f-~Io!l+#@C$mBo9Q#mG{%&@j} zv3nmEqO)w}x3x3pt}|`klBT*a(4NQH+^29+M$`AcX_ut?g+!MZ-CCz2qdHS!(@8h= ztMZQ=VhR?NusOeEdOIa=!itjib6)%=7pk4)mx?dZ-uuvHsk~3{rG#yh_*}i}>sQ=W zoV?s(^8@)QYD!nmWhbtfTg!9ffmB|rNnhl_0yHq=S zy5$L9S(ladpM%;jnexUKA4^X)J+=9@V&16aB@}u2m2TJpLzp$komfLYS z^Ri#Bxvdm?P7{M!l7?mPqJT+FEhb$bJwLkgPMA7tk9*vW`%YHZKD=7OKQ%haY6`d3 zQs=D;wuwHPlC#`&YLwBU)~|kt?6!oz>za0D$&WzZ6Gv)}|KO4zbJ)Z; zy)Qnde7e)p*;K`UZTs{|kD^T-Ha6K-7`^9P*M5BC+)qh)?=LF<5kB0tJA$i$XLA4aX1ss3 z;Md0%e(0%}Y0Tm)4ts;RuWOeUOx`4u z7aDfp?J2Jv(U!b&0q0uSr(XTAp-4_9=v=${RPKkYPn9Y{BiY|;Oh3?mYUv05r$#^G zZZiB2TbHmP=BUooEV1=5hnr5hO#5go(w^#;wSO|(?Oxe# zyK2wrd4czm&uhNlEIyV0#_Uta-v~by{?_|R^LOA+_TRBTn_iy^-_!p|_k2LzgTqth zEBihPpAY_bcDdI64@ytN2^-=@0l`XHEUgyy_tTYPEXLtv_b3I(t8q z`H%Bf(f<*>kLRyk`!hVt=D#N|*Sa!+w^#cX$+xku@JyO8t4Zm;Yfr%LE}5;nE#l4! zBwz8FxSUU*_^M;lJgW$ufc8$;R|SP#R!$)+l#i~=nzkae_{h#z8igyZ++tStiGF=m zu`zV6M%gO$4!6=tJA&sby$k((ba7Ur#S%YJ{}t+8=~taT?y?H5S$a;$_f^i(fah9o zuP80;ubO1!WE*)h?78;w)!b7)W^D{wq8D=NYS7Z4*hN=XrJl+vUAf86)^(>#?6R1J zVH)A9Lr;~yT6wB$t6SFj+Y5NFEDo)hF^})?+PDtgMv;5Hd3q77B@gZzF4<(&@!Zy6 z$;bEy7CV<;{2B6N!uKUbdpP1A?_D9I-zHv>zm3gg;&HVPc3-3d&ZIV1#Ph67k~{rm zrNpHpOw-Q&PV{KseUx2~mA@;zLdy1dL;F#0o7jEdT-M%QRCfNumfe?*?F@bXwa?GD zAW`i4%?BM~uBX$5TP6v8tCslE(EWvzU22cfl5W>Bmxn?ZG@A^YGUFUK2U(@B^zUJkLm=3*f9^OY|uHNe6c;Qv` zWt)BI(?3-e(I?&hek^9Wt-2`vh{K=%Y`53{sz3B?X1(BT>ExZ?GP-ppaQy$xEw^BE zo&n?bx9QF6*0pVWWqBdaR(#Tx&kJT(34RH@elX!{ki^2ghQL=-C#;TReSBqg;;Jg% zmaDxRT<$W7z6y(I4f`*3rz3VnUOVrruLWCo37f6%-Qc{JGcPQ^&HC#8gSD^TZt&kL zEEoK~>F}$&AJ$ay?^*P&jsHsZ!{D!WKb-c;*9Cqz4ES#BTHCNSR6eEos{0L&uYqAp z?9O>zY0l}a>d+15-|T&L`3=)om)}Hvl`Nb4OXv1NyW@EQ@k!fP>TkAvrTr%HYv{Kf zzodTe{$*7iRolFG)xN{~0`8~oU;Vz&w|2hz3i%Ilui9&7{4$hZS$C-Tiht$2Uy}0| z|7+F{-T&b9mGu=3EpP39B@D89N@^#UX6FnA`4NlH<%K2f^Sk3Ei%Xy=AP*k~tLf zH6uB;*((cnC~{Ak^WT}{pq+qo+wwoD9FD82ugzaC7?RQ}q4#%hs+`NS@84^C6{HSI zExhnzmu2z2%YrNTJXI$3S$Te5>iKcG`9sdi)oTuU>}Y%6et3hR+_ZnnAJ%@^pUn6v zQoC+`k9EaVX4a1I{S6EMalO5AUvPDwgE{Yl_G1Y$CShuw83jv*c^z`z`xEbWh`Q-%__`>a=sevS;S3Fl#t_jcsF6u0ix- z+2eT^+&10bst|iAr2T@>5AIvF{zDJ1jyHD&Ys}%cx+I41~<$upsz2aE@@etwk?ET5d zCak(AvU={koIvY|J2Lcq9y(2uzL~SXce$DA-x)gQGcEncVaR>9(r+r%d~fU?v}oO(l%%NvpvUGGD({`*vTJKTOecVz$f3OIQON2 z@3NOVH+_vC)LKN(`uC;qL|u4V=(g)uPwvc7wfCF%>Rj5=Pt5|1+&5!>I(9Ifj%fU2 z=bx)~{IHGXwYcMQFBW_ad;TN0vHH)ng3 zlf>d$92D%a;$?1xnrO~1FR`3oDl@CyPwM|{YuGO%HJdN#>GZivRCdZNn%?+1u|wF- zVbe?Zs|z-z?Dxv|K5lXKPm#v5bL|>MOIfBe`MZ6HpU$-9;0zo0Lxyuamt6cgv4($w zRHn9N=efSFWha}u=FeQC^I@0MGRyAkx*cqfRMsn&g}5#c4YJF-saHS6x97_=M?Sq~ z5v!Yy+tzt1YB$bX`R&7x|6VEgFFA|(FH|))uR6KMYqiQ!OS7dO#!7Op_e^{IZL{63 zYp->$DXrTWyRtf+X?o2R(c&XUMt(Y#aK ze&yBL=3@s0(-u_ay@a1d`mHGD#wTv7 zm+B@0A{~0T5Pd(alKEnQOv03czuCtf^o%Gte z{$|!&X_MxC$+x$}-;lkP&+&Hu=IX7=ZyK&0nRi{X+V%Mx-`Ik6*>^6L-itD?u)h05 zwq!Q{W#Jzhdv)d4Z+|@btAc%C{`vG5vOjY6D#x!YfA;*v-XE$}@^!uUrsrP^{`s}a zzHaioWBjj}e{BBc@UO|PV}EDrz3c9u)_=+QH`&fhO`e9sOTfHX6En0JMOZS(Vr&50fJ+=BPlDa%j zFZ7D@g=z8pnKc$#irw+dl{GAFKKsO;d6uSh+$@lj_uQFeEAmN)=jNaI53=Wv#Z0b0 z60%%I{(RX7nV>8$MV<+!K1(@%osgRLLsW07DbGh!6N~hiC4Dij(^t(=zAD{p@-)ZJ zOKVr6S8qri(>84}`#7y#YK61+Pmf7)p@YjOw;2hz(+vZDM5h(U^Jgm0j zp`N+iYQ5WRt`Tbm(^yQedTQNg6Hd7_W5F^@PtlA|Czkwk^evy1waw9d=9HdZrAC+K z%Y*NKE}!60rE&E7*7x#Dvx{Ytq-QU?DD`?LyIq2EiP@ooj(<|sw;5+G$X;3{(aO8A zuWz1r;Z~zVFVwbwB8&1x@FzJI`1r+b#}`-iL9UED~mS2RY=)&IU|5` zR*LKf>8F|<-p7{j3;B}OAU^B<_Y2={-o3DUQx5yvU&(pzUOqePx8?uz@Z6*f1qqKW z|KIe^l;z=>x6#bOJgwJ3UH6$;hI2aS?E^)9y3>~ZTk6bRX?@q}__?1gf?LcSoWObq@+RmD4_3GsxldN6prgx36ZoFA2=~l~f z`1avShAP>JUqu<~*EjV2@_MmyeN*7CNP}2=>9m*657_*TiCD6}G4)qe#LD^X%U?e~ zy!03AhS>cA?=I>$IsdY%h>GWb|8n}_<-bHfY_2m6{*pa&>GjQFztY66Y~R%P%WT?` z{PeqDWY^5Evps#Ke3Rm@UY+p#6ydM4Z}9vz3tM*o;Hj_HH&_0O&DmLJzT4^k;cc(0 zZ#Ms``_}P~p*qa|K;0Gj#Qm@Pi=yj$j=yaGDeyOQ-ctL+{ukn(sK3=l^fjDwHIr?nC;SN?wK$t zy(Cmk!gdPN;^jd}UxHrs>^mi;9BO&rBYd5K(UjG_|4x;{&l9#g=3Pcw?dTHoHZ5m zRDLa+alUAwy1ciVa5SHAs zr5FE3)$O0~ZtFy`yT`5{-#0nymh|F|PcBBa$BMmeA9=6p(GE(yG~J?d_Z{J*`!44G z*Z#F2`(B`I#TT9ef1&+d>wcfz_*!nMtoP=Z7aw}?3$q`eZtjsX#W3~YgCDopeqMd2 z`^8Q5Vf6GA!TF}nll=O{)m^p3%^k{0o~8!&u9R5cmG{Pa|HinyOqX z$JC2qdR+@syh@&~(m2&I>C}p2rajkP%wdAiHd@=!KIJoUuKj7cN^c2lW~! zt|%1iF+10`@>rIJ(YeN@$94v!uW8)ev$deF(C&?aUhD1N(+}*DnQf9{`1+;852+t> z`*CQ8*uTu2run^RFQmjAH%aEsPR~j1Joa_X>W!0o&GXVG_eMu7EYzNz_^0=*ab9|M z&+eP5$MUvaym9!Q`L|8&z124rAJ=_bv$20~ip`PI$AWU{Hm7b&_J2-!tnfZd=F!e$ z_kXH9Hn6`~G3mVFyhrNN{-56-EB>SQxUz2dN2UA8dyagU)c>q}EZfO6PJG+PWf|Ff zRIQ}mcbq%6d(rHV^Ijy@Xc+bO|2h6(?t)Hn1+l`5k|y(-A~bKfZM*q*rpyspab{tc z6SZ}M-66q0JKyn_t~ng?E99H*@tmCxG?c%`SxkLq8K`;I^CnNx<_VJ~U1Yd?Qo_Sa zvSNW_-HV2Wf?an!RNppCxb7vOsvO9fs_n%Yb zbZ4&Z4fx?c-(S^AUhlFbudB5x|DM1)|M%NxyxIDq_Uxaa&&Piro4!Cg-f`1M(=TTG z8g(8=pD^9mta*$-pzB}IV|9zWKP1l`{lncGpSbs<^c$19c59!8ziQ3Pefjhg|L_KX zexGz~@w`R8cD9$#KWy!(vtDf7ayR|g$pF3jZ5@Z#Ubz4N-%s8DFaJf?+zR9`7yYHJ za89N(@EpI)#_xNVPrY~Swfp@SQ~6nJO^f+U7HV=|bo;;J_Opc(rsKx`ll!UpRe$f515*8qmAd{i_RgOedh*N0Gy4rEI5oz{Jlko%NOAef z&GoL+o_)FLwaxdt;LI%WyJz_GWpu7&nzsMUnygg!Po45M#my2?QnM*-=5^YCjK*zA0`)@)a3Ow zzCT-d#^Cs7zh^Ah?#?;!Yqt7^jn7of4$nE1Hv8wA6FLdpXIJLztW?^4V9v2?GmUSk zKC{_&aL(Cv2G2J!KRfxRu-LBpTFiks!|t2aX)9w~)EhY;->9gxU3tJ|nl`VFsM_X> zbLx(SG&4;*{4(9Aw2XC0<@7mkYX2X4q5J*N3i;cI(@&~SJNfR(7w6@RZXTNTxc1bF zHOJ&~eu?aA6?I8ZQ;c$BF|8GUQt*7@tH9HmS-QNYLCtM8e+s5OU;nPL#gSj{$>Y$p zB$jY5>GY)=wzKTb_-nV~1KW>7tN%D=J^9mh&ML_3;)9A=3Wli#e@^9Ys^>bF*Ra<} zV{Kn`0@$gJ;Qkz@oX}~}0Q$)6TkHB`$=b7(TpWtE_-SXc!q#^5#%b})k z*Ap2Tb1uwZFJzKdk-pgN@k9%!_8Gfhs(t6S>f}3V_+o+e@h}bMk9?2stStXA^YOk5 z-{d^i-6i~TN**-tnASPFh;MO(<$T8Wn?`XS`?7tuw%Z(;$d!>B=48E@OXzIy!rhU* z6+z6AX}Uc|;Rhs&RecP?4|YDwG&pY~?se95!;Kj3<+Gk2Ec#p%ks8O5K12NA&1Y6e zrheUUbC1Y6bN%M^XI4Lm{+wlzbWK?;$I<7;!L}LKHy1whGrPKn(eR;LS=Y@I(`Ihp zr1d#}&AB~u=O*4ec+Tv7+Vk1p70lkG%|2_JGkfRsw+8Q1d!N<5@%zkpq?7qg=x4`o z`+f@iK2Yc)OF&E=nWK68{a;{SN?+5MjupE>O=*31&0asGqT=Ty7HHPg-; z&3~SKCj5u%v(W3|d71CJ-=9!DN#@ZCAkz2wvT zqvanaKas2x+aK|0HrL`Gu0r=`&3}0QvHI?`n{v+sCAlZuIUT>x+co!$g0|^Py<^`D z<~?BW(Gd><~UQR)BhwlK&^anTF{?N>%_J@ z^+^@){AOr;pC}=>Pk7VhN6+({A5VH@yzO9viq*FF>b=u$yt`t5!!z!+0%KQ=SHR7~ zCO?1IpRMb^@`rWnqxA`qJdbqwI;Szk>WEfFN%d^^nUND2ka4qMlW4=$Fq=8qss~uZ zqIcZjI`lNGy5g2s%lfq#Vb7D!5Q^2>E|}fGCT24Lg{u_%{2To4sllb-?6s0TzTd1)&@h*=Ev+7)=LdN zTXswGarAg9-8p-Q?Ql|s+w$gr`3WkC#-En=ME_W&d{tY)u{`2Q#=OQopE%7oq}Dx( z<=Jj*qb9#}Rk+XjXYzYhy0182^m1Px{rS~3!+8u|-3Etu2OpZH5mU0O_mZi^-iv-b zPqnvM{^no7`KT^gI^@U}Q|)DIJ71()8BV!2@x{UgLFJ!iUtOLZEdQhVoz!f-^48Gu z(79q)nr9vUanIdw#j-};&ZL^v|4;nb@qfO={;nkxJhI+S*wTOWeBifDX1fzBlzWz2 zTdj9+TGH@=>qyzHZxVAa9pUNFyvfJSa@AJn#fivHDrBlW(-r)*~E6Z=(UB#VJWukwGTJklWJrz5?Z%;Q^NA8yxXHJKlGSN#ceuv zAo^Owk2|MQ*RJ7Q(`!1hbc0`l=Cu^FJEzjsBU^J8n)XhOynNI5n$EW+p-1Lz_e;`` z{Cp!i%<%WEs1tP?4!tSQHVj{*dS}zAiVIPvg4YCB@UNcJzkSsYk=G%1hgUsQ4db=H zA9e70r1np?Y|;J4ta|e|`aL&~O#f89X7dlm*Ew}JtlIbI%sm~y`Pzs0wcIS*^+aY~ zJDRj&tK*Z1>D@N3xi%iz=A!M@#cXASe66XKTn+grZRlX4ako5>^UUAm%c^^c@q5fbMFG|%nKHeM0)=xy0g zyYR1fpT8^P-gdEH!f)2)X{9}uJTlciUtS*fm|xuUW#w^?^VV9nSATC>w6xd3@5${o zO1~D(H2(Qeifz`DXE(g2oj-EVXJvlX=l}l>w=NU?uP?7>!uvn_cH)|a?#Bcoy{Z*t zr(d$&F{RAeJzIZC#CNA8SzFaerSx#% z{N$`JnW_JmRK1w~OWl6Kl;aL<5?QGrwY;=k?aEGPe0 zMLUr*`rV|=-7Bt|%X%zxE$(VsYx!I0*7D`Y?tT1!e&5TSS+m~g$bVk1FTZ)C=EL*) z^HU-ppQ!FNut?i~W18y3tdKpj$pN{Er&nA(UHyjfo!%Wwm#kxQXCo)^eEZmEbMn5x zvO}B<+Ze8fEm*wPv1_gKvfM)gx0^W27?#~wo6vYy>QI@@nk~W)e0J;RY_L9{wp-cc zMrXm|yPV%{p8POjE>r)_xeq<9S=Qf(eaO38dQWb7JO3NG4@>Wg@7osqFnYKApM>-S z`epTOWop518P9BBH@Yo%t-|c|yaRsQ)HfOZX4!UT-jP*rPOdpO@6fqz{wd+NTHml$OYOcp z@7%lX=^N7D%zR^8ZT9=Zyu)@IpKn_KM(Ny}$rVktM*O$9E8MFM{Wpn!64^b)f7|pA z!f%c2ZpWPxzjeA|XY9f0x2OK=ezra`{^!DleD^oSeR6hYu1mQ4^pg2g{kv7shehY< zJ<)#hR?Rs!QkSvipTyDIpPW4ydEbQYEDU+z_HEA{i&Y!?-%g)zYIa|0TJ^6$$+vTE zeEwZfRxP{e{+Fds=lI-DO5oBxx6%4x_?Mu$3pUqmUUKbKb9YpV=FQ#-bGCT?i&|?E z$-m;M^??N`p(njoP1i3yUNQ4fuIbj0e^)0gdQvrG&8aBwqM&c9l||NX`271vUEojd zA5$joz4~+Rp*NpX)_y*9YvYtu@po+-d*wdHs(dWobLz|Ecf~R>i#*s|FWvR9a=moN zL&`NHahcwN`EBC6?>-ceT$<~&;qu>!pTj1GSY3PlH0Tch_rnX{JXyQq#LD+6IZN)( zJ-MgxqVbGZZwq9LYWSp% zZ@#AG+v|Hip=$jNHLWK%1){EoRUHyLWtBGBZq1i(QPbkp6=rzc+7qnx{xj2>=QS}7 zJ2!pT-uT_lPrO}O;c9vRA5A%}X;ME|s-;}9nqby+$7ktO7UP^ zAHxcf?thekpc79_lYy7LQ`$FA}+zs-H(r=I7 zH2bZPy@h|H^_#;v{eM}iZ`5_|&9gtd@4yH1Z=2uT|5hBcG5*2YH{zAnzf?LVY` zbNweHpY>bfpKHG{|0(v{_Wc#kYYu(Cms{zzJgk|y$Lv&te`RY+kI#=8;iu#$)yoQ1 zOs)C5Q@+h&|Nm2$6>j@xH)*N&<|eHDv2st;zbhNEcWzji(`$1+DaU5@Gt-ov6&IQg z7;#3g`Wn}F%KoQgcrfd_&WU)xtD>hB&W^F#VzKnsDTz6%Z_g;GmM)y4 zw?)mV_uR&mjvyz`iSGlRop}3=_=Gpg z7mnpMB{VpGXK1%$RDHF6;+L=bFMpkif3Vbk+NFzsjU^p3j!(aQReRFrL;t0DXWx|< zIrTY5kkfLeC-)TAPwW*ZB@ABt`hD-p3DxvA!PJVdl&3Qu>pAAf6TX=6= zFulOl#_}j3WP#}uwp%WLB`W59|HtZPCCs0js1X11Po~VT;LYz+?!W&tO?-|{QP3s6 zHw}@VnVQYp9q$GH{v6^{UUoV+vc7~f`O>#dK8Ggo9=AC%*}kW&_{yEr^Vxp=kJ`O$ zo?4SeOK5-%a}iRQ$Sx%Rht=-qgVp?T0rd>?X7XKr+w|*^_xCeC&(F-<{Cv*b)9=i8@D*(k z*t0{-}Ag=2Ryd7tkYF{nauieaj_m*nVj%O8SabawCw0U><)fSqWJc=me zS*u|fFIn0!BUv-$q1cJTMmO4?nq7{uTCc#pD(p_o`^)P*3Zqm%?a#mI9>@1ZaSfBK z^ER`ZRiF2X-um#_*<$J5R*%RS)0_DbTkqFiDf}UID{=ZJR_$HFe-`*g?)3{>t`XsU z_3!32*G?X)tGk_b^3Yn|jOIy4D{r1&k-9@s(IV1RS>VaZek=I6nCJa}`xzv5%@E4=h9; zv;2H*!FoJg;MnzT;b}UZbNtq7OG+Pk-D;xUIHAnBS&MmBFYcM=`Da7C z+HF(b<;x|EBSQo%)o$x{*i3#a*m6C^x9P*gzh`$YkLcYK_WEJuwKeZMe|%l1QgQP7 ztiSJW>^;$aRKAK|-_li8TvnCsjQgLqZ8K)>P1tcRXxr-i$TIuXV*L{*t~EcoUZwhf zydus( zwKO`=({*p~|KCeK%_y7fz54DiUf6-qkh3dKUp!d5gO`C}og@Q;E(7G)Osr>DIwvM) zWagy@mlS2@r5Edg&oQ1G!Cf;YeCq%EmFYh|wJ>A~&6pByC4sNwYHcWG4$4jUkhH{x_h;B_3L$|x9_fd zT`V89cK5cg_50q>J~@5Dug}~6s6C%o{B9?NxYM7%_t#U+9T!);;=a&(xz6Ur@p#q` zClzMS=X%5=_v!IvqdK*QhYEJxn;mQT_n+Q$Ji8g&F5eFEr{n z{$VN8kB_O*y^2Rz?b}k8A8%HfCy;ZhzuU-8^MNK~yx@mW#(3cmrHs>AY;+$;GwsuS z5Y6;2>cDnZ8|4T2EPvKE>=*tZ%Xt6mV?D|LaR}>6_b{wqdit-PSZrOB^`Qo|PJ4|j#zU|z$^}a~v?aN}F-@X)OhPZHxcK!PL zFLAN;>U8BQn|Xysc^4kHpVT;KysM{Y;@qQ>*NhKyIiB9}?QX8MOMbi4O3ls26Mc{O z_^4RUI-xbiy3+P=rn2#z9)67qKIz9Huk9)f7Cn#_cK0}Ef1_@*-7@o|UylR?n_pLH zixS=Q_hZVTfQW$T=QHlzE#G``{?je$lVl#`6jtjbmh|;~k$fq)SKrSsvU*c||NG0A z4HxDv^E!1zCM$+5J=`iyX;XH((QVsVUaFTPlM|j#tW%!at13JtOhm7o!}__+&4&_m zS7!S!YoFEEb0m22$BN4*CSJL>6q^51>9Nom?L}=1PWHOo zx|G@Kpqm_;e$4x*ipJkv8}7>Ntn=0pJe{W+8NI!qyE5atR8^UYud(>GIJqm#N>4ps zC?7kM;d6Wr=d`CO1t%7+tl{%5^*M1ML1*FdWOvP=2?lvv(?rCs%k{V{E-ha9{mb`W zKBH%kdaW%zuE?4T*&O-vDMKlH$;rUvFMUE^I*ufEbu6FO(=xGo+7eaI^bIwc*4{_w z-TjuuU#RurwA`|dO|kjU7IwVdJUL+Ul^kzr>wL+pkGVek===D;ST!l(HILscuD@2j za;J)?Dzw`a}85OMSH?_@hJ2uHTcA2PgO`*ijf-<8Vd082gOG&5Sa+x+;IhzWc zQghz3^W%h>|F@PdOi@a)UjEQ@oWt>`FBgK}GA#r5FvOi6CyOd{f zJy`T4G9;LF$BLB;&YTloz^Ne;H!@Y8bgw7R796WfBYA9Hp~|>)<+p zIMsKTLZ&ukF?w$ZKWzKR|8s%a?5xaFt)*LADspl!J8asYd0|(`%&UiQPW)lCvvcOL z88bg0YgjgI)r4hP2U?;Z+K8>YYcF))`HGdKdqVV@qdgfXLc}(iCnyN_#Mjtdaj%)t zm|pX{=b*}R73Uod2gAd&elX-+{}8sv_pqVN^$&7;ybsT1`c>fIyuX1z$VE5!wCSDp z$#q+ug0-(|#T~fWt|gUi8oK-rSGbRV*+$hPZanQ*XX~u~T5-Z-`Q(?=0?e)}t=Y5m zY@zaG#o2LRUTf~PpC+5nxi0CjVVi!>qWHNmnuK z(1dvs4Jtz92%^8GBwtgFot?vXjlg|^Cb3*6Ql&%6?9(|z>N zQB_&DGK-V(uSF{3YStqsqToCU@Q5J+*Rs=9s3h z|Mqg_l!*(rwe4I~BzankZ|&wI&u=75t5BGI#&qG#{Zc!mAGaF#pAv3$G1`6LsZA2`qC_MDPicg%K*eoimc zIU6-E`xWP=_78HA(?tA)Z!euI?tA&#BUZ7+r#+6YbokY;8D1qh`|tfKfhE75%37{j zzPsn(-J0J5#?z;4l)b^LkrnRAxn#?WGi$7;dG|9HX(Wf-GoH6-59N<=Q&T^^Ldu^$u_1!^N;Ha?+BH{!3A@j9Bfb+9nbz4q|r3u+6_CDgX->}^G{TnDr@P^>Gv;GOuWpkpXj)^=lRALo;uI+nYcZ}Z~Wn!T+doByh0(i zlJn@-L`Sti(?+kPtJ^ZRYVK;;&lxf~F6;Ajs~_H;?KyQP7FxONI`-_k#ShaZ>$m(2 zxB3x2DK72vbjvUL%6soT(=R`vufF%tv-Jfh)+_A2^lW{}iS;UbPd!^-^dWrWx~)IA z^400Sw3;3N{K55!e7FB>NRIDJ*n4(;`3dg337@59{;*x@-Fd-$*1~+1ZMVc?-v;cR zRTAvC<)lci(zat_v5zn8YWos3d;hg3>mTPcWqYpQ{`0l$pRY^IZ~Woq``7lX_{>{_ zvWHb7M*I1VZ^s`sZ3+MQ&vdbfbYN$g&fzexw+GDLEXiSCy8h|&zj`Sj*m@>capym= zt}OTaux#!l-QPOz&nxe-4ZiZD^vFk@pdW&H2iF^PO*dFpV{t0|RrHTlHM64{x35l< z2@ikFceQi(mAE@=**@BtHho{c{ZY#<=I^WcAKR7M`>kp(+&}BU@yg|XtJ6O$sNze% z(p};4S1kRi_K!KMIr?{;`nV!nRV}u+;>=b_{~g;BPknW(Il5IozLfVt=Jmvy!==*u z-deS#$9#Wey{`Y!?sc0hu6@{Mj#DXW87V z|Fn5Z?6ax~ajBBW4L7H4cvC#@eM!<9@tWdUUzxIBeKy~Ec<2cKyAI^!K`^05kL;nZMGl%;P<(qAy`N7kN-#DWq>g{wb zud+V+L)o6gTh_Jy+%bJtQTnA*_H|4*=DmN&b6$YEsEjSc`}56$zbf12o{bJrxiyuW z^;ECYQ`7R8jJ2oV?>KNT@L^v~%go?CzIW^`IN>MS>GRq1 zOtMGB?Azx~T6DgBo)&qGW93qfXXc@L+&fJ2Zz}y^eP^^is%7^~Nj>4}?K73-ehO_e z3k|<$(fNflPwL_QKK2LcPH&#f>wg$!xp-yBX7{ZXth(RVyw3d?=j7XZJ@(;TqttcQ zt(%Jap9lDTIg@R_V!;WUOs6UK;eU5*KN=l>R8-_%DdWX^T<=xx706xR{zGT3=)FAe z9fz*VzFR6e&sn%5R$)=`h#I`#hQ>ks_4@_t`oX6&*1MV6TN$2hrJF{^}s7R)`-{@TSZM*U4esB!N- z_cMoEPCxiN<+IR!_4)_yn#$Vdo!0)FZ%t!S-{71S8T6xJ5BII=(^hI1D-C0UezaV< z+o~os_1v3f%QsJoi#^_ax7TTFyIt8rztZHnuMWCxJwMTY_NSkp8~4b*KI_7=$M}6= zz?{Gu@s-lDTzj@LO|N0Rz4L=D+fs+BhRPekhvzv*ADXHvdsvmZ@b>FJEoU{!q z``j2*DOY`l-Rt>LuU*$21>TmJSD&(7Htn)S@^U}D_9?Y@UY}uEA<&j`CQy6jjMAwq z)Ana^noRjUGmmlK0xhO@T?az){_UE49*3Rb>|fW zYoBdxUok84y2p#xcJC4o-j&XqP@MT>rjPqN{%*;s&Kz2@Pgn8Jp3=Db=G<+o72kv% z_Sr7Mw{o^%aC>2Q*XGT=4`z57Je}mTQ)G9D)cI9`Hp`EF*kjc+e?{S+N!j&l^A4YW zRkr64pSpUH^5VK4^@1kPXX%a-hxYnxV%Ytv{Y^>t#b zqx)OJA8Xd!So7ufM+-YA-@I@AzY8w;P4q9Gd*|D)RYmXBAC%57HUE98Hgx$zn!%$+*-eT%=Wb3^aWkh`5yU-fj9B{IA=$;3!vt)vhbbv@PfB2a#iZHdK1W<>;TBEL$xa?#j(nNSau2u{ zTTFKenPxL-&a8iv|1j-yy3OfqeE7r4xsCr>WIX-mm_Pn__3FI4#pUPUUH#qvf1j(= zVU6q)$xk?nBti>A7w5Vh-LADRGo$}aWzF)3)jg>nnb*0W-qF>0;?v?TPu}AOQ;+)Z zSommJ+d7T1sH3`#!HzF(zx%W$G;*T%=V|gD|BtOowZ67Une|9=<2TORTMv6SmTcUw zv%sg?+r`S~_SE99uNHlIufFfN@A@Rm)YID+e_s~J8UDs$!SyeHQWpo7W|bZ{T-AH4 zBd}`59JB0Dr}Oo8dIeXe)}Imff5VjC{nz?Xn0rrq=<=rrCrruVD4BhJP3Y+Yo${MA zGDA{yx7bck_*MSX#%pz}-jS_BSh*{-m%H~D{ML7^^xP>PzCUvO3K!PUP-EwkNcJuCMtw>%Yc z+_PrsVrg}jUwk6gS;nFsB?<9GzcthYEDYj~>(qr6MjzMTvG~zCK4Dea;vMQ2c1EYm ze*O`P9?d@9 zJ)<;LIY-Kpb@rK!o4$+9Kj|gG`*)K{#mXJxx39;>`$Pr#y*vE!aLPjoHlsPdEP@t+ zcd9qbeV<;~H-CiJota&T?gyjRhA$4jb2*> zNmViXCh#u4lfrt>aenuLhov&L?a9}tHhq#WVojBm%|5}$r=5Rc=CN*ndA9gZZ+NUC z1EP&jdzKW~9^W3PB;N5=aPNsG{f!6e(rg@F3#|LtZQFW!j~tuaLoS`3B+l^1uhb&5 z=PjDkTeL`sJF|+k9{OOx}ESO5(cNyjH@$1L|9=_H{{LSoHE?){!gWcFd?bHJIOG2?G}c z!#rUI1|57k)h|CUEHNiDC9xzkFCCI!r-o;jNM9BFcWs(Z^m>9Z5mI1WW2kxbMyT(b7xwA|8?Z#^f))mYY$@Oqc$Gz z-oc;j5u(&Ng+07-;V)a054;c8iGK1`yw15Z z?gXzBAIHwX06wnr$rEPRw%mVyRM%){s6n3a$+w5@D&Cpv^zPGxJo&J$$=^>@tk=1( zerJ2i&$^j*3U`|0YNFNnIe+$fsI|xl72W7=PyMq_yuYH*#YAmyMaZ$^Hb3UE_iIN? z@KRTHQ@cE~r$lPv4yM%UOeJ71?D(UVIW}?<(*A3Cb8g>UyxiDrm-WmW3y&uU z-tAF!yCw3&sBP!X3ukj?&DYO-ack}StIUPAhC(llFE843`jgVa{z$XuI@$&4K6jri z(l?Pa3`*0PxqyA&M5Pw5*6;3NS+050Oj%~DQjFNb zu0#4-sqVGQ=Be#*{}G_HDk08D?ydhjL!phUU*1qsw_jLz*+czH#SE!+ZGCQaaj$h+ zg}(*GmvuCzw8&KYuedH@cF9dCsAaRS|NE26-h^y!K&aOc*lz0<_wQf7UOI3_4I zXXCcK(_+?}-l+s1IB+{C|JH@Ycf$_V)Sk?-O3LZ;EY#7CWIj7dN_6&tNw+PsX05#D zu&-#=(ynjocwHt>Jg{Nz8K2K9EXCqq-iQ_6`uNpBVXdMcjqLUzAC1 zd#{*%TVJHBDU99o^__bcC11U`Bx4=r;-7KSrDngGOWl;#WS5hCn-feU^Z^!%xm7>8j!U}!9#2x6?iaVsL z)z|%I`Hz`h(^G$O#ii)=e4HX^m}$GC$?hBfmkXE9vpy3~$`lD-@nh+P_1rsy7VStW z+ql^;lAG(CUc%Pwv>@qcj^}%_4mz@jOP`u`%j43OkUw1OS5@$?U;V@MUckb|u}5cH zEKK4MWGg?W-Ya-bn*G=0N=MDLfFEm4Bx%;Np7vf@$Mtc$#li)t&$sFM`pq|+c6LUT z+quQt^wauoIaQzgvixgD#K!$K?)z+B|9@S;^DAAe?8U^uRW~e?&PU}u)t$!EvRynh zZJBMoP3rcg^IJ6cX6y4;{r@N8|74m_@5a~lbLVVR`up!< zNzwc*f6Z1;eDQI|X5roHk~v{doi8cwU72$1Wr=9YTDRx2*NmTutxl|xVzWN<@Yu|4 zS(-W$GL2$8l^0LK4sUOXB}^p zeY&S=`uYCewl*BLl}F#rhtU$J zOP;@9#V5!5?$WdqEZRSgWw0J#@zhq?KEYlX-l?Mr^Eag*D$4<*z#Xz5tD1LUa)JCrzP8YZQs)1 z57%UyW;v%`ezhxk#T>&@E1M9R3 z$7-XNle;wvKOc8{|7Kmg_NIxm_7uliJ+EDr`PJ~pJgtfi2b}U3vOk{`pC1{MR3E+} z;&DXDN!~{}wgDo$D~$_8rx`>jj%yRwugjn3vVZ=aN57^`R~Ov2Gc5ckTlw1C|0TBAc&+_? zs?$_6-fQg5@n)6i4UnT2Z@hO;_oLhTZWZHzELA9qhv!C{V>SsI2IY0QajQ+C1 zTO!kL)#a?R?n&EZ6dQK^6U(2Y9i^2=tdyn+eyn|Z)2U|H#u;0hGvB8iGB5xHs zwB#+ld(P1tXGdK(aAMz4r(IRZ{_BIqugcK)W6~As`D+>Xod4Or<4)ISu|=_m#D9dZ ze61E_{q?ux#qo_r4){dsG5baDEBHcb8`I_apzw}(-estZiz4wY6&Tz?#R=Ro-r#(j75 zo*2JXvpbmg%=m@azLdPf)jMS6ANc)YEq^F{XYG`ScCJVNo>H=L(0>>HN5=nnbJ)%` zi^c7-coM|-R!^D76Xp8JWZi-5Cw8h_^vm2aHC1QRgE^NTG@pCPEWPI4soG!Z=8Y#8 z#RY|FnBIPuvq$^$Qn&wa*KLgplKT=KYp2P+{bKyqEFrs7QpGl9|1wQ{9i|3yKf7_` z#F}$+tJ0TkG&4Mtuyyy(d$9-BUj4AOTFU=|*S=Ee!YOaRTCO?zndex@L0|Kw+lo!> zS7+bMd&O!V9&OIpwVt>7`tk0l`9VT0=R=l%m0jfWt?E*|=$z2%=N@l=reB{YJBeG( zu>Z}h(?u0`&;s& zXB>^&GUZc}+uu7fPDfw8_F5^fy+(hjf$%{V{n!Vg$6lG59W$TNXI^PM_ki2AAFUPL z*WcaVbNAju&&uhgzvO0Ldmk}(f4a;4bkX_-k$(+)e<;s>KEFVq{@KnS7p#Q+AFsDD zl>fYcrvHrj`#0YGEBpVTeb=kcDGTlVr>!}kVV9n>sjOCS(mdfQXLa_UNlM=qsO!%0 z|IzaQ$84ll9a^ar+|9L2Y^q7Bh|k*X0?)6_2#RG{F7jZ?q=Liq`M1B~KG0iZ7xRGo zK(E-Z6TRmmGbT^SoAKjI=8cF?CIJQaT1rL!9K5r#Ud(hs>b2ahjB%;TQd7g$gx=cV z$oZ%yHF#oiF6;AKUKYzY_;TNS_n!&YlEl0%PXt>&x=x%&0L<@dcO_q2F&FgrR48NErIBw!}! z#L*&PAjPDnvx(E$Xu^vLos&7ce>Z7|MX&wJ9%Xhh!aP7_fyT5b$LQTRo?gF}bvG+| z*R81fyI;TCe%~88iD%mX_whfUygR+~dCmEv_tN`6o-96oazT)(S+m@8QL*(t%a2z~ zJ-S+T|B*+glh$vwQ21%(IZG#RnvKTED)amadt4`ldFoAlV&v&GDQC(O-$@%(-cE|q z^HgcxwBzvEz;0_}jec{_$7!D1JT_f@q^+~>(xcTn`=X@QO?(pNxo+~4D$l$LPux7W zrGD_O*?e^SiB;RZPUq(ByLtaYj=Zoxlhvf zDq61+KdJAx(U1KV?MJP3_uYTQZgk;aYIk|$p6IS>oqMmJ)k=n(=J(ok|51C&p~GV3 zk$*Zw_9uN@Ecr*mKJTNjn0@9)#+elXyXH;0ba|4@rXP|g`J?aX{uJ!)_xiU+<-BLn znMco6>m4<$^9pCmJa^6fxk^P{=ijU+`ktRkyW_Y0SaNjvl5br{*GK)~?cT4s^}5c# zX-DsG`(Z6sfBn&Q-GB3r-rxR%UF?6rum91L$9l2Kl9`Ml%4)M|3sQaQ#Hc8gburYivMr9 z`R=I_?dj1=4R`9t1#D0Abk?1`{oSPl%N!y#E^J%4#!xCV_qIW^=4Puq*93XDxK3wt zn(Mf%glB#J7WE1-^N`T%Yckiw21eV@?+lmNWD`8?ZrtUF*sUJle>}e$q8oRqVWBDS z*3i?E9-rL5cV_)M8rq#Le$BYo!*tQQZE*-(Fj??jk5TIFo?PDPy4_onxHpsrEHIXuC!NE^s&w#~ z>%<4YuIV1++aM5AYRZ0lv98bGriE1tSJ_@@>1AK%qdM25n&%r&+ndl0(UG}OEshJ(ho#FSEO-s0(`qh?ox@6pbIgxAZ=GPrH6wE#2#7pvW z*exbUJv$*5Da{<66ZN`jc9Y_Xip?G?_0&RiFX!A1n>~Z;UU-B3MX!J;lRVAIk+ONy zcvItQpC2ndc;eB4T>$}CxC%IQd0nEGzB8G1aa!1%gYn{hIU2V&8D6<3$u%|i&Lz7- zUv~j!=e6N1%BkFXc|G9@Z**!Cey@?Luyn08T(1y*S$Rl+@ycI%PA)-?y7)zn!#PS0d-#3cl_6SED5xId&v$dzI6%H$o=W z^K|7#huZvvul{*gZk%FB{M^*25yqIS*|*W@+(V{KEIyK^(X$o^Kd_3Jwox%N%ZqP~ zL$%6|b(>AE#N772R->sC5p&N%-yvvo&m5V;yun7c`L#rAx2vAYxYyqFPk-0Jd{T|2f)hM4G0o#=5~;r54w z&o?HdBv0uR)_v2nt$VFh#FgFg8UDB5oYD6yxoy0`+h9YXTb6=W;)@4YjE|%pOfb2v z7P7fAZ^z2w#c9}EM&OYZheuHd^C3!^TG`)ejgBNX1MLJTj0=DrtjOH9Wi@Z znEWlDb!N$`EoJFmRndp+p0^x&c-rc=xSPykx#(~AJ2$mYvFn~Ds+pde%dw61Vfmxq zOCR0ox>788?Lrp!_NzHL;uj(n64t$~O1O9=ae?AV=ACo3+>YtkUB0vVaTLGt_ROT4 zN2Q%@eWaQ9u9D@-%JAIz;Ow=>25!qGy1(o&PK&Av`~6p5<3tWut94WF$7bHE9sTn6 zr)3@3=$Pp+QEsZ}W0yPa$E0%nW?p}%(_vJ6KY&i1aFAu}hU;EQDI#miMI)N~T#?rDB>Zc8zpb?nBbzF?;Xl2={k zF6N(?rO13wHZ$PS^e<&^#JBF%?DkPQsG*~?Cs=+_!msPuaZg1;mtSG=Um)4&TC?-d zzUM{LR$M#&@<3UbYw(GW|0R1Lp7RJUh+BO|cEX!2$4?u{RZYK_yla7~RIa{$cW2R_ z4$(&~45zN$ezzvM#=9grBSncpgZ0DK%NlRra7<%+l(?dO*Gq=WS2o86&R%&bOx@$t z1;LrNzt3#Go$G#OR@0&-4^BF}r_O!#|FV^k(e$z-NooAH-t0DCn${ebl(W*?y8g0v z$L=cbL(ck_J^ANOdLrr>w=}=v#q2MW+T$j_+OG05f91c9x;G2WUrcE4Qw$WVE^zeF z3GaSU>Z}kVdUL^CN1G!FQ9TE)Ew6m$eCM`c&No)88piaP_Wa~@_bc1=UUYvfF81DV zcKLg)eK%r{KL2q4`mKlD+c;OuVr5-!`(@FbH>*nyImb_ayz_vmp$03TuzCJHo4U^_ zXAgS{tNCbLzMI-$!?l5<*8QPbm|5YkFPW)@;ZGLkpPsp2`DD3jW$dK)-k+E~>pa@; zXx(k*PPA9Bbz9-{-IVoKO>ScC4zrF+i*2+nJDE`2w?>QswMJMjnZIU`r$N$%7)q9WQvxBO*H-*HRURcXk z8PX@k(`WhM)~rb-Gwy6|Pkpp&+GL-s8fN~#!6Feq(~Kv!*C|hJ+R&F;BWpF|1 zLJa$JPd2c8Qwn^3fFbM*Q{z70gC6W0Gc3y<{oqV4bg;U+Jey^g+ktC8k{mDlBpwmC zp%<^jFzaR5PC3(%r;LWtPy3eKI^>ZST2&#{|Gj{B@AHQ%Jzj)wI%-*_And+njsy3+ z-<_K(W?SEOUi+XotxfLb!&V-xwqqBsmFv~2%Yy4HaU0rbA{bA7v-XhlR~%ni<}nz%IehM`#CKB zjoLeI>%d=?>Gu|iuG_m))QLH@@#}%v`f^XidS7px*jG`*xz8nWjqP}%vH6i z1#4C3bYlyD80TDZ5I^;%NWnJ=Q8A~m;8HZQdC zGy8OsO**Cd&iQ%T2UBa-WoF;soz>AU{#htzf7V=;Pk%Oh9eN%5Y0f4srIQ?<$GkRi z2|vtGJE7R->AYOyLBV#Zhk?phHTtF2E;#I({@d%~;l$h9LmD1zR*G1lk*1X`uC{Zs z$?d)0dKk8qxim{ZbrR@g*=E*yThQrO_pf{1*}*1PU(JsG@adexipAG=JHF}L$HdI^ z_N~<}9n00((KZ{lEP8k&J3;EvN8X}NFJXs}hkw!n-&(}jZmwe6;xPGIiN+M!7tQIim&VS;h4d=EfEsD!Oy8V2VwTz45`9vU6Ws$#k)cUtK0)wfK14r&n7X zJI;A-eZzB8&ox8#zFy|iHb0jCMdp**=TxXJKYqFOa!;q^YvqFvnjRc0NLg)ngVkh? z+~VV%FF8L2+u3@u2iSSIbh$137ZOyAYO>Z=3Nu*}47f{sk``f7xa2KW5c2=Y`dO!KD4+vTBFl zoAOUS{odEwCvX3CE1#Ejc0Q7I`TGw)`6%74dHH=+<0ih~`u+bUU)9G<-#qyg^P5kf zCEHJGKW6{6S@*7?hZWn+=Au9TmmfTjZGR~vY4k+1cF}z0x$ewgR{DMlWGk4&`-W$k zp~d%#2djIIT?)}!_|l~HRHoV41y-vTq?&YxEfia#wQG*pMbRy@>=ZaTzTSKx{>6K* zos#zRJKiT9WPdj-x+6O!IOdM8v;3kn$y2u9nO)z)73UK$#r4zM6y2}O8J;HbM^qUKFXX4*>34PWHgcP^agvCqk)P0h zm7|@;>yLYOMxAh7JK@k$i{GgiHkfHGDLe6~NNXvF$_2)M+Z&(HR@^H6LTQ&^(2cg8 z=E+BNOGNK>U3$Ak_P?Ef`Gvh-EN^+1U(EZ(^mh5~m)a%%bEY@1oKeTUr)!T~>5eR~ z2`@i)S#ky|z~Go)zeO#V4jXR}&2 z1fMZ1@>PDXG4)4rZ<68nf~sT61QsdLVNLo zZ)z1c82SY7vhOMnV)V0}_U(Y)yTzPe6gKRVx2cwR@s{bl+`>|eQtma+#crI>PO!{p znU&Z0b{BitXYUKQe<#X*KcvL9Un|$oBF1Ym`-|_hidjp9|1zq!&Rw^J-|+N-y66}E z8^qPZbe7m34B8p+Ph;Kx_R_f-jR#b3@4f%*g~RN{qLm3pnLJ;y#;7;Or?_=0I=84d z%nX!TKeeN6y3==Gjjg&i&GYSt$(4PQ5k>u=oPN0@S0N>?+0CsU(Y#JZh!NypP%`I z^TelCh6XuDit}2#2~U$>+4Flbo>Q#>b(W3imnr9oypmb0uyntZOxM3YY zbWW|ZDPveU)i%#YZh4`^&WZozO#U&N{1Y_!$7xdMviP96y~R|62-U({UNX1-EfeK6 zWV%#;?CM+3D=aDY$HSh#Rr$Q~%_^?sWj8u9GiLHNYc9KU+4pJ2N%z8AQFpDGk9Nr# z7@sgMdGAwSCaPuZ{9;O@QBoSWo4xZ?^@siwWY6DcpFe4S?po3MfXt9v(LZ`uruDtd z+1!65CQK~HI3lX(Ox~=mDYH2r|Jb%S)ivh}kH!qct5Y8rdAzVRRzAKoeOJn|dH>h8 ztkGw^)^Rswqw8Obxki@Fd+ziX-E>}TF}Z7}z3+n@tA6$iGT$yS$$6Z%Y>%omDE%Pt z>PgJ5uJ%i1=a%a$*H7Sn8Tc*Gynn&;N|C=DKciRdKkpi)_S`w|`k}p@4~+%)^(?Ai z_{8al$<@qe7Maek&A0!%ymnS&YV_Yat2{{JK^5l>!>2*-5;CpB1Z2W)=9Easet0}# zg4wJ?3;q^7JMre3&%~A&rKxfIZmwxveDtR8X5pTXT$(XXH)Fh#D{am`+oW zTPtCmSRFrR0 z`Gv&!WJ4q@G8nR`r5ZklpjQ}2Cp{-R!fRym`*j(-XYsXVfLWKiRnC z}S8b1y_INVyLv;v9a;ePSuxJBtHhHA5fbkz_el6^@%5P z`_%mUyr0xe?06GUB*b{ra8+zvOi_#8fxK7_^OCEU1l?cK;n(TT$g}Bq*M0fG%y89z znqRJ!UT)IMUBSKgtA_aeC0?_Fxh1xV&Z=PTeW1OtH#}G}p7~82_v>0&-G{GV2HkVZtr5n;2I_6x^-NBs}BC6o5g5LrB@%lOxnH2v3?1S0BN9e!%?Kegdd znfdf*;p;t|GbVg|_h2;0$X7AW)_zrHccdz~-VSD8KWyZS3c@G~OeA2(<|0DYg<9?UrKU05o_+Q%p zL!{@I_M3;{k3U4Y8gKn9K6$bvv(%cHg}f$HYWAAknzFp)c+c5_ISsetdjv9b^R<6O zO?q2&Jn+pm@2?F$F^O{Dv#chwV9_b< z4a;0st^aZ)tUh|hv}0lU316pbq)mPQ#xf(;Yvb&*LPGcayICqME!8HNdmrhawC_mj z(_LSJGGlk}-hOc zo{FODqi4t4cS~hgDP*?x{NPg63_szw^H9eW?I}t>u1&jb(a@~y7HsM{?e=@~ z2RdC(P1*mP75UedXrJGnX1&K(Y*XR42fRtIj@{IET(*bhCFifwqtl|h*G?_kcV|Y? zv2)q4*GunurFYEK>C29d*FOkVSCm~UkjO6QY@2Rzn7cH@Bys2VQ1_P^C)PDs>)V~2 zF7i6#5A(Y1FYmog@Uj-!Tv@YuXZyC}7gEgpwakmpK59QP`SqRYhYino&ezUdwD!@u zy;hS1{_2%K-&GvTAGj=~DsQEk*o%!tYh!||CcK;%Bfa&kL+-lO`?@b}O|=NA>bmsz z#`jCVf_%Rg=qQ+Xoe8Qv5u~1TQt$Z9U0XOx)|ba6i^opCusC#^RONG9A8{^D)?XpZ z4GvnLw_ZP6@AaaWP8?-3X1uX?zTlfV|IytUhgzEsv{%2Gv1k8;P^BlT2foWOu>~cc zT-&u`{-@wRtz}N3PQf9XzQL&`!M+PDvy{$;PhK#6a^_B}ey1#{yQ|VJ{9V=5w_;{V zOz0);EVI4qR=sG?;@i9O*`@O#ynlVqJ~t_~d%hw{ZlT_j&mpqs!?=Gag`U^G;xZ@}!UJX=FUS8H!c+Io2TdWRDsY^M_* zTA4MgFL?5-a|=hk!8U_w*%gx{qVGhle{!l!iYk>fMfRvTNVYecoGFp|<#uyv8H<$P-PsKRw!AJMtsGudNv+*>v7*%2H+=i0 zoKk6@UvCXw-p<_kOJ;4@{N<&ukcHaFfp1PZkgDxdeiUkL8ih#e7BeK2rEx*KkZdncCqe8Wuaz~VD~K#sVVHi-zL=Nek@a4 zm)D%)#gw)2^xb8~aqr&b+HqI^lJ+z>xWFsskjLBwj;~aumhbah`YP4ng-Gbi@E1&9 zwRD5;FWCBO_LU`Z&2N91#jcNAzWd7eO#5dxtLh~9C)#e&{^$E>qsa9YnTF|Wq8R-% z4DU~G;4~?@k>0xVke zTb^IOru+PUg!WqPk5~U1Zs{wxD;K@#=y%BMuFv7XKax&owrhItW&Zw6k2m!FMUAg^ z3qE-tcw*vo=6+!NFWXwjT94gVd14cGgI3hMIC<7%`-V%LNCvV?L%tY^u@;<_8mL%@a?gLe|rk~?q~CK-ZWXaw77Ct z?&Tl*ex$zNH^bL?cJAeQN2YGvGu=0N-a*lGtDSZ)UN2k~p>C~l<S}UOV)~bFMw)&nRsK50OevmJR~EJ_WMyojQR(IvTb%F4o|C>`Gu!Im zt%%tk|I-CDU+lTY81p9bbaib1bG5%B$vw+s7Wa7daF(nyoR}sxIb)uoWoPH-$4k!p zA3SpN|NoS<{+C~WO|xa@-Fwo8KgExE=4Hn;%M)`LjkM3rIW1H#i@GNM;&NpjL&XHCxmcNzqr|ZACQShD{cjg#v)Ae)w=@qb# zYs&g%Cy%L?OcY%3^w=(yQ{hK6<0m$Kn)Tyw_T1vf)8lJ;?yZy!2yA*X&wur=gZCr& zg|{&7-mcQ>>-_r&Vy?XA>y-g@eJ&!^m+AFJQ{xtQOpp7d^A=AEx6Z(FX5oua$D{IdAh?F_4z zsC(oo$h(Lx`{!mYDkUhDSaP7e^ioK&aKgTsVv^ArD>qIJlMD}-T&S9Bv~JPrp4Bhe z9!uTIi}Bvw^EzXHp{;G!9f$K0<`-8VJNso>A@|<&J6`(@^A_7nZND7f^Y%;mW50D~ zRu3M2FW9?ry1v=Bk6w=*7yRw`%sX{WeGcR5{%DezQLC zld`ev-<^%wU#~Yg%{wgq!Xts@{H5@KaQ+Fg66`k_e&4o9>ht=0_BPwa1xbH>W9(|u z*WX(l_-=Z85sSmo*e?s8{L}j-T4j3VOw->fp+PYUlP58x&C1r^{X(;6m4|Isb)llC}7_dG7y`cdg_X3f+xY4sP1kFyyrc{5x(&2VY^ zOVh`@b0psv)IW|rXDavH>9IJ6c>PJ$W9xUEu{aytbN_h#MdKN~ymEI`Dl*;_PCwt< z{5fQRpI4`u0vBbDOD{9}288X-H^fb;D`p?XI8lk!1 z6F<9_{6g^n$!^Ka9rvHk+7J|!_iowEoy&N|&8Ns+vTj-^nl87?HuHt7S;nzsx2H4H z@60wyW^H4#>^;#i$126akJm`r()gFcaW6BAu=wU;w<|juXMI0%?x<$bO%YXxV)b5@ z3_GRiAJaGW^gmSi=j7b8|InvDy~iZ#mnS`z-(dH@W%Vx>u@}L!I3>@S&eSvF-F3s{ zrO;(p-889IU-Q`1WtV%?*zTrPWh|fRS$$Tg>e88Iho3Q6rB%I5Ef(0D`0IskvB>io zlP&MhIh5Sfzw^kPbCb^&&C9(Ncw393k|RCy!-@A{ZP_1AxIbQHQL!p%qfz)rChIV_ zm6v{=30caR%2Xj5vtGCV=;gQR8&oo$p8g;IXivO-LcRGC%etQtKjPV48R{yzPQ+EW zlwA>hz5PV0TWVwcf2QNnZ`$R~S$SE1b>Dm6>Tke_V&TI79EEeAZ{(O-7NL;A3#P7|;-e(__{vN)2FyxSUA+v^vm$a$y8ly-rxNu0rF) zi=CUxzNg7XK4ZG0GI_E2`e&&kEUbAMFd zFB9$bsm_0kTf2bE+8?Kn#JAfSUcCB4x_f{07M0F@55C{i-}7=2`=5L4Z!X=fdq4f{ zrL}eM^>&x^-rs#&&-L#;+p?EI`^(qIM!o+2ZnL-d=S{WMnzs7CXZfH!Rk}Y#{+@Lg^Zx$4^zvcP#!IRbm}mBer0XqpE0)wX zoqjp1n02jb_(is2)7Y$a3s0ZjDq?#5;?y+Z*krbsYQ?s9v$kD`P1C-c8|P+yM!+^< z+ePKH8GbX~e&POHWR>gof0VPFtnnq|s>1o+Vw z^OPCFSN7|X3~{KM<)GIa$_^(^_{=w$R?M+mnNN&sr6s6!&I3$ zyr3>KuxM3unxB?3vwnnZ)5k$>f94(u<$}0cMjh(Mak8dQeHjqIM92dFHZOr%ejb&X%o%+x1QYk zEiF85g0}6lGu2&R9fEy4FD^PeX<<)hR*Hw~2D>xcXL>26ZL2e8J@;RBgVLr8nyudT z{Xx&S=-BO%?3WoBTw#>T*4fH%yYAW6`* zB0067Br`t`lA3x$vh#)AMQpQYzp1*Zrl_dD;Ajg|pL}E-rwU)EXGJI%hX>2k;H?|k z({F!uer?)Vf7pB<+ZC&zi4INtQ-3YiI(J5|uh7SFTKfBQbE@B2maqH!zP^6HvrOfm zq(>Z|Jb99@we)_r(wpJ&U{Q>o3s4{*3K3%aYv!(v9!0X|>LDmlDeU zy6J+!mUq{V>u#K^jP@i z%=HoGox8MW`7S>+WpVrgE2cIno~GUdntwOGv{q$OSN6=iyH9@J&qFJ<7&p3YzWicd zGVjMNEnm{?zuk5C5%}#~Oy+CzUF_SO?!Q?o zc;)ci^GD+EiL!gf?mB+xaIA{{i@EKE4}b6Nd}zAy{^37wnf^$guufQF{A_B0?DN}3 zNq-oaXTFi!a=j;M#)GU?cLimV&Z#W^DA1}Y!yHl~qkckhXI;+@IRWA4pEo=@_2i#Z z&isM^S>Fi-lb!^3nTQ^;Uec}{B>DHqA;V+pom?+-L-{PG{1o1Jm+xc4Et`cLYXt-Z zvx;sldU9Vxa>DeJ{|}saqqy(4Sf_gRljJ+c4s@JzmD_$bTl!dD|1Xi*rhd zDSu`QleYiDcTi^anQ}|BT{6r6AOHAZy9~c;AN&6MADOo51i3f%P49?V_E=O)rzrMs z*JH_V9#-eH*4~#CwkV#_dBnv?^5e~IH`qTc6ieQEBd5^f!{@oDAAkSN z9(v}Ya0Yh?%Py9Qg*QIi3bb9m`D{&|msG0pPVTtdZzdaDII4KPv3s>w#nYSw>5ngX z>n+nGZmzg(9saB3`YnTn^?PQ7uG{0q^gHR}reA&q?c2%@yp*o`~Uv4iI+%f_G~h7bUU%+L;J0&MBBuDQS-I#6)oF2Rp+~3 z~Ywsd1YSaq%#rD3Nvt!et*0nn8rNg@xUurUV+kT9t zKXW>hfKw z&z2Y&aVU8%f03#>P1VRqL)c8Sb)qA~k_dZsL;cwM_L|zJJ6n1uHco8pWMr8UA<-z8 zl9<47X$?<~gPa^*=?hsBbTGb9um1T)l` zCmd%&bG3(ltf4X^1A_n)q2%b2pIlN|0BfF1@b`9d6ghf+#m_7MBE7bBb2y2cZpjmM zRbb(C+A4d0_g=SWdnZ;JPl{jbD!+hVGpGL?dh7BGYj4OjI0-GZ=R*Wa%%3oW{2d2=I)na(n2?4 zMZRA)oO?csLuP&LLmrWge`5RO`!t^IxBPj(EVp=liQhUcg@8xFYyoRILN+jN2-|P! zl)J9f) zMeLYQI5}#DGrCTh?b<4uzoK?h&}20>U#SZMwG!LZQ%{zp&7JG|C)@4^^Pj+Dn>Sxp z+G?VZc}c%G?cC3O#WQcl@2~&U5*N|Dh+W_2#+L_6+1E?NaJNi4k+5jPuXi2t#qkA^ z-|x%IBqnt66g{2Hpe(EwtZb*B@2<<@DDRo$Zc^v#)Vp=s^XE?D$7Sywuz18M_TOu1 zR*A;zquqj4H&lLo+VApPJXvbWseCMEg*H5C zFMVE4$d(J(dDjL;njl3$2Wa^<~_U{z52Hab78w=#LKV;X>)@DnfHWgFAcuF?DpA= zsuYbWhFOpOd_}e9_V;GxtVmYbx%8UIWT#svJ^zFS_4?dQTmGs<@U_h4Lra2YCtjK{ zW$RD5KuvAgq`1DLtwlO(4dt#AJEi% zTEO?xXLaiC|92)T7Tqq~-qE;c!k*R3E%ytX}29{=-b<%IunJX18a5 zoziyD={lQw!FMIdrZvQyBtzF2p`D5_$psLo(<}(fk&7S%sY_dkstpL;6=d10R z*=)bOZna~W@p`d8^OwnI7l+C}WQb%_bmv}W!xO!S&s?W(i$Q+jmATTz7JR~D>au6-ht-CxE3m*H=~->+|;ylI_r z#C4JX|DAJwTNa;txBvg&zx+K1R5PR%cy1UiNIkHPQLQ;A^>5|Ug5@E1HAK95`qsbw z5Ib-Bovf#;PnoX0X4HIgQEp{dbk=0=_@0A&&C0x)(M<?#bs%_$X)Q@{!w33A-YqQ3pL$?nVS2*wSD|hMG z)vqeq%zEn6x06yCt~y#J`;-rVI278Z-Q#yR{7|xixZAbxWoM>OE!erKSsfk{5OPi;R{f73_B3RJBBZsfl{5L)*$v=i~VHWGnxxn(cOlPqa&M?h>=4xQKc0 zyI!m~aWOD>Z;HVzZFc=csd%T7)w_k7vo%+5aDOfN{NwE%_f~&ivgP;9+0*i(SSLp> zn_6>XX6VBmiw;iBc=XilJ6C6(!KCO_hBJy>Z{HK;T-GS7{_&ULIpZTbsnZkooQ_!N zTv4({Q%dM})}oEF`?OW0Umln?HDj{oQQZOYT`ZzWTv*sO42WsB`9rPrH^**6{y?zt@R?$;8= z_*XH@)tu+;;91@r?%#B{OG6$scX^G^tdeh<{LH)sJl=B^4^`RrFIE!eE<16*_OfVV z2k!))dHXU8d1Q(bZx^20(`@6s7%KTuuwp8!S{~|vLTj5Rj#j6e9*Ibg?@@?VDZOlw1tFwOR zIo~l*-Pg0y;@ouR#}acYbDatrN`E(QTlT}+!?ktc?9QX&&GyR=1hO4?kaOMjXjtg| z!W?n;sxQJvA2`+PYR^q|shP6y#`h29`7CY^IoA0f3^Wip&hVi|hHJ5e;fw>5b>{Ub z`8_)M+n`mNhs7t`hR;~aU*O!KImaq3R(=$@SkEAz^{3;Tyzu|bA1;rV7qopaKE!%x zbIWs%^Vu;ibB?L*Sn#oUhew6ywjXxAFwZARjq|W=+`a`CNUQ6v) zetG)HciTS;KDr;8uk_}_U!PB0D*3EkEvu!x3z|QOADZ2g+PmH3d~U?$iuNzz6ZF0< zVtW0%O!{|Ox$y4lfU6bqPmlksUXXvvW#h_^iZ+*iXh%jVpU!d#cb>~=r^Y{jLc0IM zna7@gnC<3Qxt6`sHI_Hl_3VGNe!iB#Wd;*&28L!i%$;RrhWfDarHI@d&%Ct!;?xpo zcOTN~S{s>JB7If#-#w3QZl45>Z+d*BQ#NJO$w|{Ktjp%Kw9K&hD3m13(XwrmZP^qL zDZ@#RTBhjS~BtB!;vg*lVGEPx1xLV(doVqk^lZc=9%a@;avsc}peKEr%NJO%# zXwA%>pYQFx|NZXoch$w8en|7%v54RLu!=vs@JciPPlbmoda7%F7}z~J?EcYE|JsAc zf8Ghp)m&=!ul$fHzv{<=^B-RC{qubDpXW39?P|_7pa1>fc%Z!RZ>IU*j%w9##!oAp z6TMIA@oJ-e$9&_bf8?$EyNvz*q>s7(zP0pBHrjW1?fs|r-%sv{KenYO_|IGBJGWnI z*>o7o)%YL#zVoBvzdsv#&nkG&#(Hx(VVViH0|1#!#v)< zOlvni4zQVa=17t(yS4SYj^kyQ-k$527xXnQIl|oe>8}i>S@#Y+;M(>ktswv6vCy_x z|1z9p&m3_NtyM9SQtsX#FWr5l%>9c-y;<+OFZ(jO6rc2%&gwjNEOhSiH{L!Onx=bi zZU{KAs>SN+bdROVora$-Naf5tnc``CWWySjt!vwlT}iQ8rJAwBTFU>JM9s9`194J1 z-ZLc+e)3)Q^4^+Hr@1uex>oBQJ$frbZJpXB9&c~2iL&mO*YSOO-!Icsazv@~WZ%Or za=t!gw{>Ql>MnB=y4%`(>dX1WudgP0GGDu;bkWQ5mQ=8>eeKf1b0@CfYg%)&jo0l_ zmX@Ss@cm8Its2pB5_)qKpUW_`$>z`AdN+XG9q|ze_{ia)9 zabEoUs^Pl_HV+dlWG`M;p6J=8o6j8X_wOd#8W*F8bYd zn#uAN;#D(!jC6E*=O!+&=-9qzQ;MnKvfj%q_x!o%TsS3c^(IK!WNM66f3`>7XwB@zmA4N`tSg$k$ngEn@avB%CEw1fbSoB3I{kRU-oxgRhF2Fk z{{Am7dFJ4ZXa~dDYpxo5g`GPY-6e z7q5LTn5!{)+f##>GxcFPzXKj#mlb{Y=jg8KO9H)D=-W+SxcrTU>#6kZp^FdYtPc$D z68G4cmOS~$y#0c2i{?gLSfaXAL9*H9#VO6=LpjfH*X(DDs=KD~<#x~-WzUtGyOY}P z7QL0eX7p>uE$OH22Y)q94z!-XYyr2)md`q+C;T&7(^uIQ=0&>{-#TmgmfiYxx9nn@ zijafnKg?k7*WSTlAHIQ4T~t5N-Wm$T+L`4cfe*!x#kbiy0i6liyv&SXwv>{-Ns$S_dCh3)JSNz$9xgnYh9}n ztbd8xbS}5~vA1dYN1nrvVs?nczh}HRi^EUh)Nh%a6-%`w4yN+RKJDRU*}OZL-*lRo z+_T8GX_tc4SFp16ymdQR{Dav!ccPo`^FR9*HmrI*OK!%k$gt?Me{8&TAX=ty113hrPC_q8N3DYW|wyR9W349 zcyRv5CU$+T3Qqm>rzhTX@~qBj+_ZY~yknt!fx>H-O_<)4{V|WV{^9A9Z6!+6Jl`6t ze_XIWz;kNR{9Mt}qhec|W*2fhZ@AMh|H|T=+oW_UyE#H{cXaKWQ1&V+=<4Nm*|*1U zHFR&8@0wcUVVI*|`{bTb_mZI2+aDwCPOXStwpRFuYp~$lbn}X(FE&j~3G(gvy#89z z2kG90w+;WSxqD4YZ~0ogSaBUK?-eEwC)VyVJG%cuZ|}5yxkWCLOwWJVss8U>dQaG4 zlE<87MIPsB_bI7tZB19%R3-FE@$r^NT-z>G9$UQnddi}hZwVXLugTZk_qte}?fRW% zRc7ldZ|4cFE8{P@;3-{Q|4t$3Uf}{KoijGquJygmJ0`V;)#1d?HrMj!mu=rp`LcW7 zJgXO{UPK96v6fBBQ~7csf2l#+hQ>P4WCpV&;n=-fe&#LefLG%q{$UT1l7j_kQE>GxJzEID$&o({4wOQnU<>eWo^W46gq_z7meD?4HCzrxv zPR63VmUs^Szw1<9XPr^}Y0?Ahy1-(V2W zl`3Y7I{e1uRjftdE62FNe>-y*HAF2p>=&QNVn5ZpPSDoT-+$8UjRP-@nS;n7tS$mv+#59#ZiY+7_x?8D`&a$hqxg`b|AHUF17Gai z;c(!2RPL4s`6drP?+)zgeDz+?C8pBl*uq&Z?@YeNWnKSmBIaSaQ+jUE^?--ZAEu_H z>iw7@ZR+DDyZVUCGslKX-JQ>GJc-MC7~91ewfN5Sq~&)))PD<ppv zlRfUC^z-7=S>p|vAJTQIkIzOFlSbgSDn z&vW4lC$1@-30pih@OA3zH7=%{j|!FUWldgieA0sBt9HCo5}n?Vb|Lx9njIC1N4KWF zwB%a9Gvnde1$MGUIg8R}=pQ$mlx%%+WAhUU70a@TGR_ev%94b4^F-e;*tSW~XVYD$ zKHe+q#kzL&3eQ}-jjJ!Z=*U{GzG%^!1s<&Gg}pPsyPw&!UVlYG`Yrtv{?*BKx3UfR zxmnJqhHU(4leu++=cAqduYNN;Gvwut=-F{>rTe ztlO@&f3?T0(j`7+>u0~&)~YQb`dO*!RpuUX#-trH5}kve8dmgszB21+o2_32KlHowS1;yJUzFrJcil@T1nz5P?O=cDcc>_s z`P|71t&4A@t=#CFF=79;Wj5Mwf!mfYd7C})2(+L&E~P@YutGiZ`Pae z?#bc*m-*fpo_JH}sFJ|5g`w)B-JbXlutN7_q@R8kTEy zeU@a-;-fobgr2wLhk))%9x=L=WEab@xA-E{E_b@IK$;8=c zbGI7}m|{;II>4|b($OTKjaTK|0X_y)Pkx!XsrU#30|PT7p>aFE{E|e(B%*KsLB4|y z0&VH5rW8$hz&gibLt&ZlL=P5E<-)tyGQyUvU97N6V!=T2RrX9oYDoHzfOE_eR6 ztd~JU8~Z-?QWkx+%~LoPw^aW<>Nsy{!pvhz22o2tN-Rumd{AXHF(X#bQz4!dz+K=~i)}7r__;2dbJ!k&DzfDjJDS&p7*b#aE)%;pp1=fp=%ne%GeOlA`Y z7SGzGAiq^{VM&on%FR-TK41F!ck-s7$=(u@EeD-u z{`@uXU%~8~5|R>E zZOfWRUwq~X_MO+Cm$pSkSww5{@)u5}-33b=_Qk1u>sM`mvaIj%`ro%i9=)mm?sB(! zo72Qes}IZSRtU)+TCr49tD|6*@(=oj)XkD?ceGc2 zc=mRW`R9*6zxCWuBTN?^E~ufqaDBE`XxQ7 z?5m%=miBJ6to|BvOMY&Pq)O;x<9)_qX>+{tpNZYNKF43hIU;u3PtM2x)Bj!-cH1v) zzpVJ2lZ(Qa3;S017p=Ba*l{=UO5&Y|e(k#@PVnFK&f@7R>t@*b!-+wfcyo>$LRpvhsN| zEBF5U`~127k$~gfJkJ7s7fowxa*tG558_N9wUDs_CFXUS&H^SC@E&Af9tPqAD% z`=MlwtvTfj=JNU;H}`$JW#QMF!*965tBp;*+nhJ8+NU08q*8Ku8_VIMotLa0E{`$Z zX?mfrO5N#uhV{Pu@0sNbQzI6=<*zt+~OIzM~hBi@&O@WjGk&HB^6@h#oQ z;LO_b+PIgiL6sx z(`9NCzOk=b%d8;&sG8ggjT4c9vnHAFZ*&dvyQkdt=;@_>d-8K{zy8seynEh@_?q_% zUg{N!?)q3_{4zA9mf@1ga=DZHUE`*^wH~|rs(eGS#@eW_+DY~T=@#yfq?5Lu-?BPk z<{O*8kqd5!xIWkXWmZ{!C_}2)q|Ms$)CQ-xImbe)t`|&SbEeGV+_Z%`#aE>s?)SM+ zEHZVuRQS3J#>u4 z-mSJ|IpNAPLq{yhKtaPWp2WG1~s!3-na>!I=J!_ zJn=);KUFEu)!fgWpoxoXU;q3+3!cAtc!gi?&nx+-AMfRu-8MTJ z^yJOWcq8MP_y69tE`EQ;{{O$9;#wO8?Nv9*i3?XO-j^A8|Z}Yr2*mJsI>OtS}AHBY(l54Q!!Lf;0y$P1vlw`3_stB9@6nT)1ag5GEVGoA;{BTRV%Quq>OHshf;UZdojtU1Ts{ zcH+0h(jyDzEo1Z!?oGUUZRgwau${@flsBlJ+cGP#_+1I7HFs>vsvxOV4x8$oCR>JY z_fc=mKRG{l*UH_uS+8wBe{5l2Vxw#Nm(CKi^qcF>EIIaKw(~0AWG3$0hxIc27 zD$SIen)5~aQh#Lg|JsS=xicd*PnU#p&3k)v*7L$QN=mV>d4&{LF4)?U=o7rg;+o0A z&S^VeY+v8IRd20T4`wOvrjilJ5r?P)qgHKJL{vSvvfnJmvlU4DqZ$;)xC8hGiI9?uX-Es#wWbI z)HBvIsylX-a{rBAXF|B9Y`((r1SY1HAm;LS!??3eUSzSv^F-(~a6 zirX3%Sv#-GO1t6|r9Wp`>hx5}Ak*JmP3=qHPH<0&DKC>cmbX&U<+)|$iESn?Cv;Dl z_Ez>u{bb>dH?K{NZcI{$%-EY7xioWY*0KrHO%}}V+Y-Bf>k3=hxeKORb~ou%4r=TBkP~<BZ~ z?0>g}9dDJySg+rCPtEez2|stC$qi|r)Q`QGl(4hErD)x}WxT&4H51Nx-CMjXV9W8R z=E0|?D}0^Jw6}R_)kK+1pSG!ee^KM2$2Y%rc0^N?UkPVU-HXY=j@DKzQSvnwS}zWE zG^KA0J~;V>XvE(GZx=qWu37(p{m+U7>oScSty7wwb4>^{I8e(j!B!Y>P_ga#$EK#k z7K;*Q%yuvIIoNHn?tw~`=$u2|Tp`yMDlUG&b6De;$ed(@38@NI+?K4*C3F7WV9b;} z&XIcj43}g0+@SET({e}aH0Eo^Ff_ep$t_t`c~rT&_GWR>tA(-8r=)G2u3fRzQ}%F# z@AM;I7bwsFC%VUpA?%X1uF+QWfb1FbM8D*`+{0cdFUlM{cmLudp-`spfq&VxcGoSv z$hB(%Q}K%=E*&RbvBfj%X78D1E`7J6XWrI~LupmQ6?39Ozua;9FK6R;;u_!YYhn|& z&N`>J=>3~nE=~{Rvg*0tm|XNpkbGFQ>-xGQHCy~PZauL5X#N|)YPa7fwinEdve~`% zb^evR$GX=2USYiv@6KIQHEtHD z2Wq9%Y0q0E`T1hU@{7+|7wZaKZp*D`bK$tRaNi8M^}H(FhM_E82iJUb+htp~^uyHi zsut#pB!v>&bSLsmQLsB~cj=^;XxfWK9ws4e7FD)=p{?(k`bFNj?KfYS!2j(M&p}J^ zz?)ZIy{_dnfXQ%v&Tm>k!XxWl6rHhG+PVWqMK;9C~5APvi-?w^&k)`;Src!x9zd zxx9vnnSAZ8i__KAbktXPY-pM6=sU}Ehl-Dd*ZGYShmv9!8lP5|nKP@}Tl0IAmUGYZ zjAeq8>z9ArG{64L*`Mz23(xN@t@<;qOJK9BtEIT4vz2%4G+E=AxG=#e5A|cQFY{L$ zZ%jFTI7M8}!7aTy=5CbWy6EXikCs*E{3*M(KKl8i%J8;I{qwV?=P4MTThC=(uX^mK z?Y_@$H5cP##VSqRmPl&dDzWT(oXh(;uKQsw|D5S|?|)X@+h*|oYvku!g|lmXD<ywvQq&@lejG8GY-}<~uC{myNw~6WHk$~*{3XQeutY=?)sQixp#&fLAXk*0bbvt!$ z8(oZ8KQD(&_gL7GFOIhJ!m7o%wfx#A|E-F;;IsYi&e%OQ8gq_#Tv)?&`nJ%= z5(}y6Kc2fMJq%eT*ZunHqhGPbudhC;QjOf}YvdRzd;9S2L%JIa_UvEUYse6`D=**1 zcER$+f{w*pOwSnHVxrv*kLLDa3gBmun}BeN|IPs%0!YEj+PjQ)Xx4Wl=-NeJ2>g4Cc@I zw0e8)b>%(hq^Gt?X74$@WbwvnWoKG0bDC|@7xF%$c=kry=DB9#FE<)INVKO|D#oj+;AE$uGM^mNa0p|a0+zl(D1`E+Ha!SkI~$7Ne*GEFSd+H1Ab zP*P6F#6T+kw$qy)6TuC?EV!p+ChyViPMPGnH7a1~)9EQ4XE*F|Z+~ri)amT?Z?50B z@bjro%G|rXw>hcTYfm9>*>^AN6BiyTXoW9+)92-&xo~sv^d2o?H&MR2mczGPm=+Xs zu}#>jQ8BrQ**!i?>D;_GI~GnTv6Q(M)%|++?nBL0SsF5p(t2B^(qpY8d%x>%J1-)1 znPu`(y_JW{mM>d;sA1;gn@jhwOSN3C*s44ESjEC-nbsZK(zY9|>r8$S7kEO-vhe(z z#J8F}x>BO6K89IO{qFjB?^?^Z|BLh^j&jd)VunHPLrdV1oE+o>8soKHU~eYtY@+?3s6{mYF! zZKXML6W??!`_8m+;aulMIR;rXU821hC+jw?Io7i@%=<-V*f-@+>+C9)4TX^wOV|6m zJP8lYt_ujr=Kp`ki#V~llkG4(CjAy zk6x)Qm-w~7Dfvp|W8c#$FC}6_`mWguFWA^^cI=U-ui!S=`^P6zMhG4#zz6nvyH{&-@}-!5_~~)6eD*w^1)PTUTgM%KzobEx$s!TV?fikA*I^zNi<@-POC!^7n$C@U%H? zUZ*B?+_b#*Y^hSb)soj~yXOCzCb8yZ-ur9cpM0D4U_sy-8T~?w^wXQXmxx3K$#*FV zUs-0!H+#<6sHO|2wWfBY)*9X8VrP?H{E|^jAm?(JWp}T>_8->;1@mY8)p>l=IQ&A- zAMx1EFQ;7Cc4qA4tNu2pIVNKI2^HmEr!?wqR&Kq!Vz#8aaCv8E(<`?_F7A_~FHN1$ zJ?(Yvu|+9H6Tfu#XFtmOwao4M1!0pvw@p8k>sMVoVq3Cy z!AoVgX=U*>M|OXIHIi#eD7o?CFyk7;N7yVf<9 zQ*S35?zNZ6_WkB9s9QEujJ;&;D&NwH1$*@7d0fxE{JKp2u(Na3dbiz~{fZu&g_cxa zzmuj4V%*g0n4zGS^{DP6!<^~za;J@IYyW62^J@!;jaQ01E}t4TBmHiI+SGN<3toP_ zXXRTHB$M!Iftnrb`;PV_hox%gygOz%<9TuKx-`e-yE(EA{`ddC-=#-rPPzDvbuV_c^9tD=ment2G>wEnD{Y!b1UuOD0 z`SgG4{hjP~|FQUgpZnKeh#sq+8vjT85by1}6UFO4a-6V#v{U}FzsWCsHGiX@n$HiP zf8pDEFX_p4$yc)TtljLdzo@TxC-K*5arw(|slD#|pG{QRfAz)pFYLYdmfL5Q2o*QI z*eq4c#$wx=5Y)2$&}vS3rZ^F9`~Oplm(Jq&Fh}i_sbQOJyrNd)q3+Iwt1KdG_B>W8 z>ximy4%lm|BvdFHnh=|I;)hvUVXyqdv^n3!?92I8gm?G8-%`W>`bzN10{i-tu8(K= z=6p_Ax2-;kLFYby%FHjvo-Q_;mF2s1(ci+h*qJ^&pKP!E(-eHVXNIzJ-=*6YPO4ke z&Oh4o^yaR2`}U~JHApjP>3hYUcP$*yaH=&d9;n8(#5517uCHCd;kF7%0HWh45?kg=Mti1jm7J6=V!7zj+$~#WV zf2m31y6O9$WbJbN^i;6;nBei_-G^*CYvXLnS)9)YduGh=V%_T_&f0tN^MkK4tZ|Dr zF^8O8EWUEhW-5 z^LwEh!e}LV%#xeyisPG?Cd~g?GS$v5t%~y5yrMVl^3oZR+|GFm)o!_+z2dLCW$rUi z)#(9-_kO)}+r_ye*Wkg~fJ@D#cDLNOdu)DV;Jd+jW`eHolIWLaYlLfVJv@1AN04NN z6i>`&rQWw3oDm8vf4$l==O}2LbhAp)3ee3hx0?~iAg{kbbIXOKpP5~`oHa%4ZcXow z={qg?s7kW>d#cTqjP8{zkdT_MplN09(Np6t^50X7{-GJl498ZTlinuZ76G^mvAo|JNgnO z|IE32dFP4S8oj3O`*OKv=KHRd=3Q&eKd)w~;gi2oXJ2YowMor*d6Vrj+sQ47cLli~ zm9AKxu}J)}+ndI(5_A7IpZ!soG4Z{G>1?Sj4$Tu3-@4onw0i#Ig#?ReUHapNe%g0p z4zTj>aC2+s=KHkJ(|1vbj!5?e~$`;)&qzB^@wO<}q#IO2x?Ll)JC89&M;?Vv1S)tWraK=VXg-2X63l z@EV&oJgk_@SQ`04LBaIM$HkjEt|;u1Z?=kYNt+dD8!RR!#>U$!v@CR~f98n?8Ja?j zKOc#0*mHG~%2KtMl~*QhmdKsnbbG;rtlnG2>lXA)SU5@lMxwSudBgF!67P=}7Ef<= zU;9X~!OP!)_gUeECyBb&jem*?*C*d}EM8%zAIKzDypu!bT<|x>HUWX{q`oQB`GDruiN2H#_1>j@D}N zi~rquXzuzw(gsQyjT;u$-F)<@;boG^cdo>PD>=AXpYHw{dE{MjpT-VZjVXIOs_o~Q zw$=Fx?-j0n+|`~r@xI!H>nc}Nj}--`XT4${h4rg^8L~;jCsji3(wBtnB1P(dHmU__m6)vPfz(F_II(!!`pcWw_ee^ zw7O5J_PFj2y-n=b_bcy+D|R`!_flkE*lrp7LYE)&W*1JsDHvDoa-jFx$sH?F3?RQTLcG`yAmn^lcxq9}A_8YbTsy_eLE#9?bqqTI-Qik)vsnzF~ zD*XviWt}I7b=t z!@4!koNlbRvgbaX!m`G| z+rKrkO6$y};DuiO5A-MNsm10`uUCDtzk+45+~W8hEl=30J=PU5{$6U8d2eaFWuxP+ z-m(p*dI|qmt5o;by$$0tzj-=wySL?^-kS>Z1D<|}Ex#jY{chRi1Ao^RKQ~LAVRqZf zeB;%n#lL%-eUcUVB z-aCEP51M`#?c4L`a{hV+t2Tilm72@K+mFub;Xp1 z$%-#SUHv+&bq|Hj>X>>+dGYk;-z420*mLaQlr9q~o8G{K4`1!+KlI2-WX9`4uX_m-ZzS=2HFW+U?xe z+OEB>A~_`u`#9M@oZax;uKUL{zq4D4r)n??OCDJ^_p|oQJbkqCt0ErxmOC0F`BY*2P;T~Si_xU7F|Z0(v2oN~f-I#%!gH)Szr?Yoy^<(}IQdu&gikaxasO5UX{I%!*16eb-K zlL&qiqu1APGSHy%VC%_!YRiw>-KbJcOlgN1#`AyD2y9k^0TCYGsC*Nt0&iq^~|2 zc|h%}xmDw9wk1Nr|9N0jvkVNPOS3>LAQ)f({YDg|nf=V%f*km{2$1=i^_CMiHL@}= zWb-mG7~vj^0_#McT=dK>$brl(9u1EUmI)R3*S7b}CY}`4#ygyo6gKSM@N7}%P65uH zf}DbmXIy>TQX3B5oO{;DyTk5f_dnLpk7nU3I5 z{#(iSGe39je|Nc-t?vKh6Kloh1RX6b(mr0iS@ibI&8K$WW_{k3EV^b(A6qJ7?IYegr|MBll%yZ3nK*;IbQiT}YbrNCstDP-yJk-LokdF%{r@}ozKP!K9TvC3)%~vJzPqM*;%hs-60d3b z&)(dp>T`F`%QG`lqu)G8yb`@-=D!Q~3%78qx_9(doXOqxC^EU`fJUKfQs2tB$g|O^ z=gYcJTiNWh$QRGek2u`>Pd20|`{On{$r*{~UR8L6XSoDV^=RI`Si)NKzh9?GyZY*f zpX+BntUsx)WxMEM_y3uvVnb&?oiug(^W4ntBTY~5emcKqdhM>sGmSWAt_a(nA06@b z!IG>-;Z=_RtfU{tygKMN|9;PXmrn&zp&O6J&0Doh;HCV7El1b3i(Y=%QSv2!uGvk2 z;=Qh(oA+KcFk3y{$maHa=7$x6lRw1FH)zj3wRXzqYoXC@TNl^yw5u#Q$X)8;{B8c3 z^^I~erUg8E+2Wk%H`ZNbOF5~K{lG-)O!k*gC%$wvnpBy72`QE?mdQR?;&zH#)N)6` zA5$4a^X8J(Ki{02ee~U;*QMv9?z(;wIYt3M%xXKSsnz+1 zzAn>RIw|_wnw_(lHYBILl3JP;vfXphvbVusBepGw)QWU1OW8K@xo-a-ne!iC-#jw^ znE!mgUUj~JPZy>>*{OX~{swcS5BsEEhnsFTzq`x&u1%lfJ7xK0=?ifSGHn*GRh;Dc zzvG3}BBkW)j?SL-ih4ou&3Z-~+gzSiKmAd<&eo&g?&(tbs6qzWKYa4X6x;016vS3A zyBEx{w)$IO(KvYu=OhbP-^bMoUO$&rRkzK$@Vl!%)fGen z=kmH(qWo60`Gs%ZRTGZXar`XV=#tR5NGEKP>Md8b>WD>hVk`Sqh4eD-mmhH5vc2QN zl(K2RXHO~Xi*ng$CBGp`MNe(1PdeLock>P2FOIC*c>Bsfoz2oIx@{qAg6))gu8JMY zI?QU66?E&SLfGsssnEdo<`r_bM>K=AUj4hlW_m1lWq)?IcQ!dBwsMp8 zf*COiyAzG}p0L|_|9X9$KJvVkijZ7;@1{@h z=DrIzzg~#com^r2BhFbZnfG9o?GH!i54~*lbud@}z?)*S7p^7lVB z$EAQm5F%D5x+Z=76X~{t`#Dl=!i29`EW22K za;c~E(h|1KK}Ql?m8AcbSiL)EQd=DIxo3ImNwcYE%CfG-O~mXt+#;bafr~I3EA%IGle&XP71vE3Bw zZzHF!=sWkzO!vEw*w^J~q+GnD7sS=JFm_2x&eP5fq9(>^Zo-pFKRN2EJlS}H{mWvn z^=$f=9K@9`OnoQ$^3LMo=ilewww`kFc(i!vn(H4p{9|nnf3}Kyu(Uax&rBxADCBI& z(zT4v_xmSv+H-!@xjajr|EERpuK=}}R^zk$PJdPTHU+q)x%^~ryLB%!W!a?_4IXx@ zJZAdyG?yt(J7?#dYHl}$$9rGblkGY(r=PS-WIKiZu@I<=S2^{pctg@tqlSaht`mLA zwt576a_9%`FkQl~qoFLE_^4&!&38ZCA4PmPqjA=~c+R;U)rHeoKCRxgHC%qfp3UJl zS2jLPWHe>JEf=@=>4V<(P#Z;WZqKBMbEX-BzCXTyoXEp-_*lJ0QSVOWr`t+5hA$AY z^=4`GS^1VZ>(BEh+uhHntPgV5)BV?-F!|p$t*s{(?o3*{@M^29>a5a=Ddrj9=M{DD zxU0P(@4e8{_@6UfyS~{Kw5fK*?JM8db9}>E_2TC|)?04n_r7EGx~_45!J!YI8BhMS zopfx@l9n{N>mCc1rm0xUBaY94wEP%sZyEmNW?;yYV_+~OE#4uqzBHV>LgcEX?c_IU zW|Jo#cWPib^=VQ{d2&bcs6IwT-O7)man~b&2FpM+O?t4 zx4OilJ&vr%$X<9g>h~7?)!%aOZ_SU6u6{T7=E=0poStXDKRtJ5?|1v}|Ihz@|MR`w z$NTK^j;c~0vrOgZU)aD?zG{be&7HLye=f^_*T8R9MJubLr&nl^PoASoPsUC&#>=2ic|-T=C-f z{kxw@AKIDU^(leeYLQxH)t0TmI;`l;3fFr5_7l{GZsB9{MN3QRQlG>ZQI+ z?HOCX6(zS{oKn8l=W>+xbG=Z3qD_(E2mfr1Os~FtZk|uR-Q>($8+T+z&vRRLxg^j( zt@PL%!(7A2yNOno+TKf6v`;a7^VU~7_V}jl#Sy1>w#_v&io6|Sur%Y?sT{LpDYNB1 zex=fV6N`M)x6VDaEOWWSoKHH-u9=J6Tywu{=gdiR+fpyf%-wNq)7ie7^JbTK9=I{h zu$BMjl!-S}ET;YVX6>A}eQNLpiy)gY|9m%=ty;5Bb+5Sf^Q`NtCH$GJQ};&B(tg{M z_VwJ9o3D*x%0f;T9-P_q_3L9M{ik~pf2f7bkkm_@9lqAswZ{1+Z%2ScT<%4O2lGGd zKfXOgr)a{&x>YP+Y}Q4k-JG^N#xeWvhg$8;{)g2~bCeWs`<4eQGPdP~O<>wJ^>)M* zmaWcdyvq*yIBlJpcIa4$xo~gu&Oo;8(=G~gW*A@1EP~XZO@A+`M0| zgfV4`aDZ6EhIdkY%8FN4u6bK@_+|SO@>?bog=0zUfMqXZ6!L=W!Qg&|IG1=g%botG^?JuJ=ALXBU zHh0R-V?tBwvN#WU9*R3&G-v5!nK`Q!4Evsz&gm7kitF#STGz7pk)`bU$E>Wy{gT`J z=08{-a>?;~xJBPa?;{Hyrx%pu96itdxaj;XmqL-E^^e7WEbWSaSs}PT?4z{o{-f{P ze0a0=G_k5O_^j&vdGFqu$Dagv{bq;FlGgql8@uewBKL!9^qQY6P0lkrwmVo&ZN2GN z-i>QyyI=mW{iiU?{?!j*Jz=pWl|fpw&o4>#@yaji>5@KO_K{Im|KWM7utmPdwboqQ z$d=3XNZEMXrE1+zWs44oN-M1t$-H6F7aP4{hIwDvZL_)CdJgKGCyg39h0CdtlMh^KNsa{|0~Ynw%^Mo_CHmR?VyCD*n%(XBDPQL z?yQRAaBJ_?`MxIPr^p%U&|RPJSjk@n6kO7d$v{JPf@{e$Borzek*Qwy?HWJZCXiT?5s7$ci-t;e)`5Jsp6!;l%$yB zZ%$qKug2fVeMTa)r}f^Z@|5)pHm6TnsWyFzroq#URYq0SzOyqUwaQ@s{bi9pg`@=3IS8D2a`?R z?N%=MTEz2}=JeB3=Ze!G$j&x1D=_F8hTPj9sp2`qH65wvGmD*v!KKe|Zo0-2TTZ*|{I z$~Yvavzq&RVp7SZOYJ916SoB#?phiud*I4B#w%stlA}th2Z`Zm)d*;~wTm!?S>P5V2 zn{yL(6ox2zO8F_Zo~^N-=`ycrX-@dVkf^ziyHmI194-46=ieot$R+di&+;8lo8IVU z$gDiZ`X)Rxw3B;d=Zf2&-_l}^>{H2eR{wqI5_^7$)vp4>?Mf;yzL|UNsgzc|rzdT4$x8*DNQW(8+JGDcW32 zF@uq5y|vvT-nxXdh4HM5og~tOzY0ygu}56^Ncth`lS$@_zuV8OT*)o(Dq7utpV{|P)vLPH3;!q2+;D*T zOUvy(_Q*8(NB3-d++NkO%Pr=z_Y1W+b@?aD`Ad_1W-U)ybK3TPb42W$Z^@;9BaYs_ zTCuq2(y|q6720{X>}^||c&-Ns~mb$0(+52F0UlqHKrN==Q%eR(IHs^)EI$aF9+`M#|x97w6)k0s&Vzj4MvEQ?N#txj;v?I-;RH z{+!d&mK@Du%kB+%^;iD0hUTo9a&3mDb5H*CWAc+PhCX<_X>YTY?w+6(n?Jp;hmAc$ z`Vq`2pM0(}GB8|aCTav3x|9*6`xu;^eZ@hvX1^RdsDnv zYwLo9(~IOc-&{D?$TQ1G^X>jD$Cdma*nccOVr}T5t(BJ0wlh8N+}@eR&u)J|AAj*c za*oxELu)f{?8tq*uGuD83Jh4zxUhAJ)*!ig)u8Z15>{4gkv-JG3?}Cw1U%Ju6UFQ$%wNkk&=v+L(kL7TU zg7C*p9mkb!^B(V-P&UDBp;YTklR{QA-_1M;1?`?nn;N-3#|Uf+U7VLb`_}wPQ7H@k zjN4S#OUtylE9!3vsflyNQ3UE*0?pQy2V z8k32Q@~r>=r@jz7tRZ9>xyD`X^>yPDtg%cpvoEE*Gdi^4&0o(w?>hG6UFKF;d?`n| zwZk6b@Z%<6;_#yCvcY=^s;}P}`fgO>@7qLdItW1t_4VI3>0jlo5OizbQ2B5E;LYunc53GmI`K2J*B%W|^&IfaR@ z0$R>#`^y&iyClEsQs;Sk&)QzDGh!FdQj=*tJXhZd&M$B;tbg(4QNSyyw&WuE6QmtVDG+q^y9 z;LSB2%j)#DKhLlHmb$1^dH6?z>7!DyPsdB|w8UMJx@NrQ?@sxw`vDcX{+wA}9>Gsc z|Fa%Vo-zMl)1L~iP178uc1*o=d&k6SSpl;@2QRpPMyv;IXVC_aIqL%$85qo&NKR13 zkOVa~(AS^IP@pzdTs&Na<>>lmISYosu7@i=I+-XcR$UNSpPIR0$!_m&%|AAOSrYh| z|AV_^`gPkCotCZ~*_fiiJ4O9`a=A*gw&A6g5Vn2FUjkUPI*l*%{`FR6LfZTH4>O8Z*WEG?7@&Tto_PM zEA0yJn z)_pW)^p0>`RLA+W=gzqk&y; zb))fcMSnY|>~yPy2b&r_eLiN*TJQg|Vn44j+b)CKZQ54PN@6Y-#x7X9=~dO$fS*jY zOLw1}6`bR?E__aR$O{+83daR|FY&yzne~yyZ%R|a(j9T1U-_QbUN=`)&-~>!uI}ww z_j;CRJ#jNm;2~bw&uU^DG9EG4Xe7wjWNSs9TN55)Tg3e8d2x*JY2VMO6Z1~uKbEy-C%Sc;zcF)EuW9YMyMC*}+mB)CbqCrMM{eqtULhm@Y z`Ey+M&pBjh**m!;&pFv-PuoR{TM1`Wh1bvM?Fl~zj0 ze)3e7$3wkuqJP09iOR0?Ml&8s-@aNod6A*8GT-IHiDe%{&wnrtTElbq@kd>eB<;OC zg;Ry5{**>-O8m?4SC(O7V3^0kz+lI~z<}fM2T#l@VU95=Rs-;k-YUsB1<3ToIp&)sK-e21euq>N7SM_wm7ja49+6&Xw zX1&;Lf9l6sCxHpa9-KZ?ZvA|=<^40~$$&a`7 zTAO|1dAZz)^KXyhM12_}mG6he(j!)L9_9b#*zCUkPumiYbz)|=G)RQY^m!nif=lq?9USWaxY(s z>!$|oSk?0Mwygex?|$rMw!89?@qUoEsP*ihPH#`h|CskB^mg@4i$}h`S6_CpKWwbBD1&B1n%a}plbRP)*+ z$1R_)=d-IrC*|rYY`iczBwLEjf0H z*5MB`jIRB7mGklHc6V0c0`76^2lk<@qE;D*M8ZX z_b3Yk!*)&v27l7hT~KOaX=YJs3N$T(4i`_&D?v?&YeS=hWn4x6&5E0mD#`IZqrpOD zh7R}1reiKDooq2vHV8bO)}WR)BR8R-EX~-MTO}?@(p&zE<7yr6Rab(%we{^*q#l@| zrLQ0Lv_HuIyx#O!y;)k>*}MJA=YO7Cyw9%o+4=o-za(XjKX54!NI!M4X|tfzyeXH0 zMJqU-Ke@1cLHxnXLF$4B8Ri9k$dPCk40*7pXoK%^6`In4E9SFkd@GJE!f}lcs$uj&I(Vd6d2O;j4X* zzZp%Dj+}n(Z_CjFigx$*S28kLfR6+oF1DiJMsJa^Kru&Ktey zf5&pu?xeu0@}Q=~_)A-kdYIWN-zls;wrK9HCCM+YEqOPqXzqQUpP!cWzN?pxhg;ysPGO}To)Rr~e3cR47tWnC2)k$tN}L`;zMi zo|_aVvUmT=`0aN~wW&$F*!5PV$~~oIziXyP8Z_1k=VU|o+M72c9Yw2C`PFyrWqr9%hHcrl$Jtt9A2xAu zXKD)WSAXh#=}b*%c1ZM+@C>)7qPf?7^cha+lM{d-mTMXYkN^<^HZSw z&hljen|I!RZZYjb=eG+#Ubr;PODMW6*&3E!C2r2Opnh+P6xXL4SM-!mm7Lc8G(Vrg zNH5jz$79ESjmyT8iI<<2z7u)1ZEcM2q;3O67T+mJ+8s(a-sm2RF5fFHS2|tn7l+}m zB~vf-SMfR+mrBj@etEyG{pA-9Pvu6o0MTgX?$SGsoSc7lJN(z^-O9me(=4#Rt?1pN zN!1;f>XOfKy?w#LaPh)lz-|W)Ky304OPkzJav{62wNPH=e_74s9>4qxSb#3u*SNzv5T)3@J^f%|f#!#CbUCfP#?yo#M<$Um^2i+P^ zy;nRuo$=^;VaI)kp8ZY9e?sRxSii7+e_!z*wYDF#XFS+$aQ6NorF!=K59Nj3``h(@ z{Ql8ZFAyNAELFu}+^6@+!1qeYW5G$YKA6cJfQ zRr5y^tEO1DtJV%%T>l}RiVIqocxIpN)C^e$hITKa7NbB*M^H_zmzDS$|RqeDTmH1$rTXrW<2N` zBOcq7v@xiwD=0+B+m$P0^@=yEI(F<@_drW;*FJ`itZ)8Xe?K#K_Dhu|D^9<^UVZ*f z@wxAH^MvQseZHtax%*YG17l@{H$(qgqg!GNcK>jak5{qZcKUJB%-t?-)5Sie*6g%d z_u=Ez)|4U_xwm%jI06LAe_6bGtS4VC9<=hni;snCCY-8lw^=vIJw|Ttrx)A-tm-v) z^na}Tb~&AC&XbUa%Qg=l&$YYv^zmKBRnHvl@-+zpP3 z@7XH%96w&ow520oL`uo-UbM&ZPri2hZl2%H^1@`_z6o7F_tx#ZdVc%QpS5-W)a~!< z72MO$x%con|B2u8pMG?#{L@`fw!FtqrJw)zkNCee;SJ{mg}f>}Dj0X{ZtYTR{48i+ z9};#?xx-Fvac$kc@O$#&lfM1_Dg0^Q`Qz+NC4&5?&iviWa#cYt|)TtWQv$ND0d`09?FKU^pG@w0g9-G@$fQ~FOl z_O`1#b3R^-(eB;z#dUQ`4xan&o%jFsQ#10HsD1s_^T$&vV&vi{AHQDvr=%h8K|Y6P zp?tlugSpe2AB9tW?rZet|NJRw|9>{m|GVe!i-qlb{QUi?-;wt9=g*%%dF8un)cMno z+3o)IGu%CC|K(#p)0afKFCXiTe4}pPo-dc<={|X4iPFiGZC}2+PG0?ZVu;qPPaped zrsa0U*$Q~OSzkZ4c<;r1UtYPyORYa6CjVbNxOJX)Zr|5$#}`Ky-23Mtbbscvt!k6* z^6svF{FnJ-#o=>DdEYW`tVlPxSrC{Xqu`e0uzSwR1^SnyeZRU}EqeSu^R&)kx$SQf zwKDGt-jvMjaR0SS+HCpcjr;P>R4TnJF*<5;be4?A@khLCH-<re%JM*s{v^ZQ;Vpmj(U5G+x*= zD~f%k^sRCi>lV51uUypAx}y}kwl&`SmQmE5BXUegcH)n19!6^WzaDSm)7^TmlXH8p z#k*dv8y6RC`dG~KKgz*|)$q5STskkh-$kvrG+I=+Z0F+>j)%o8yFGjKx1_D<*vh}eHJc}Ky7iOP z&FTvtbs5WZY^?ZVcJx=$OVJyF#fwa@7lWr=hZTAoZ2@T-wxcUyVp#C*Sq35maF#EE*kZdZHg-8kiO z`}({8WKK$m|5?0fnOS{*S4!@9{r*epp%&4bk8aKADLY!G?0GX@W}j8&6?c_K7pL*GO*p#T?ROCK$rZeH zr7W|PUpgMyGE1!R@u7PW@!ygS_iVFdKeLHbDrMe_phBg)Vr$pk6+Cj*({^IWW@VNg zSy>C)n>?=FJbz}owVKAU)AtMOX0C`VKl*FVL`{LhjN`%^ExsOGTzmJW+?jQq-_#e& zPTX=MI@9RZ>>HA~eaDM`w7YeEi4ps-Bk-o+rwx@}>06ZhwmezzB%fd7+?7XfubWxs z$&@YW;yg5$EqX)!`amC-lBXu7^E_fqFXW$JCw1n>(UY^*oW<^1XLIfg-JN^)b(Z{#rtNbaF0X1_ zrx&_9EJAE&lzCC4@|^w#d&OOU1+sm3f_wA~6(YKeA`*AKnd?=1$7F`;q1zLC+*E%2 z-1^etviSeIrf&txH@Xz}+CTnj_f$9gqW@9#Fo{2Vb#})X?`8d3qB1kjH6bYU>rwS^ z%egX_rtP;3w8-IjShJ_0%_e?E3 z8d2+GC$QK3<9Nv~viIctj&!EHjk0rOuSDGc(zVjBWyXGkY3~G=AKA6#viaGFSBK_B zSnaxbinmfa-r$|r`>=)Em^oEL_hwvFS|+MqsxaX>!+{&T8mzO~e_yh_Sh7Z$yKZB{ z`9Bt+bw6fXJ=$f;pX0})V!h~fQc+KkmDFjcgNc{aUk0nqKdq}~>+pT{7s+2b!5NRX z%-Yib&h%x&o4-P~X_p*#-9K(`_>;Bn;*XoJZ9vnHgq<(S!%rEc1p_=zgN#q_e|-N`ade41 zr~dJm*=qYwo%a33ayR|b@y!3XPanC~n`(IcW#%%={CcOPPuYLkH?9@b4v%zZU%vfB zXIy~$^@R5hTCzF)Y)>oHZ>&6cYx9Hc>YI#b&2`u+(zB%Lnq!`T7mwSc6G3yE51yNG z;`C0*h%cY^9Pq8`+t>d`Yw1(fjH`OiI-Ivk)%HyYS#e{9-^5h*ISuj4#eJ$mb?$Di z^8Gw5ZF1a_=}$ifPxiJ<_$2?*RJc0c=p}Czx6_$dTUF)y%iJHGJ(=g)?yR!;O`W$O zgDg91VYP|mn`RH+f5OTKuJj1WIGKyFZhmgAq@$fS>reW}S4+)|9j{E8_O(Z7x6oPz zAr;5fs||KG_)h+>6c?bDljgc8ykVD}@4RJsLVH@(iv=%h-aI;M;yxL_uGE`r)&$)8 zP@1g7_Ao8?$de5mA4Av83CQp=(hWSBB@!+v&3O4OAESokx0RdUZirwxGO?^^ikEso za_7Yqm!%G~V~z&By1@En zLqd?+e#K>0+PikkcZNtfp5rcy@YI)h`8wpv;s~yHPH{YX4W{}Nay~Zo-#7WGyW+UQ zNvUGlV;XZ$wKaZeTNk=Q-#VBtTjzwxe4!|wG%fCJ$9L^sc*w=5L&-!)+%YNEpgJ_L z$DcD&ZIbtwqwI$9^8^!pSDA4f*|&AWePqDf|8Q#R&G$l#eoHpo z>AWhad2Pe>6mc!a!|6X}PMh1};U0Ufvx3{tPjz|UD&{pxf~p@p9VGr6O<4IeWryVB zO9h`^IQ5^L+Z8Jus_v>=QRaR*C~e!5y)KiPt6uN9+CS0mjCRjbv-E!3?>iwT*7nk?lG%M-7c%v(N*`ilvhW9OF<37J|SeNS;?fz-? zOtJYq+E=?BMbE17-))_=amt?*kJGL#=3V(;-Bj1zdvWN>?*(=C3+`QBdOSG)ieSZh zc9|`eGwL})GX!@s&VI4%0>{q&Ny|;o^iMFC;cDOKuut%ly_4N*_r_m=34aS#%C9qU zJN}~PdZckm$qXi*u30HIj}1hnu7v$(ndux7TR-KDTlv!82EI zHthXgHu;*@8$T$zZnFsw5S({4p)5v#&Jw)0U@UZq>+yn1PqwP0-tTdddV6?4xm ziOX8TcqXQOr*x*MZO>irgAvxD7oWfQoKU2#P}AsOGS~HW#tgRx&Ya8QFAeV1y{QpV zUZ(ux>9K0PXBRJK8SZTptvjF zG5BR=Q`h@tPL3*ORrJry%*L_ zqPGtl*-!P1ySyfR>DO6#dsnq|NN(C2y6wEoqGev^4bEy=wzg|VygXRE?TnsCMe%m| zg|FT!o2*ayXP`Tw)O4zyP20+{7N`ExO@dtZaq%y0eM;G4cUxGg=ruALlaE2iE zgi^*RbrzYW^1=Ln=agN@-|*P9OZ(~D)?4S>Htpk?TFJPyL2mitC(LWkY>!* z^U)WIN4GBQQD>OHlD}1cVJrXbHfzb<9ltid&blxsWKT%M>f9d2ryJKK)WsgY(`UMA zvj+2K#Sn9@f=(st-scPq?^zbba#dWomBy?el-PLHWkI{OiUEeSl{?@fA^_}DwkhYvER~?&K0e7{v5LOqEYSSeM>LiYuLVK zzI+z%;UDkSZX}0FJlijqx4A{}t>?7`Umry4TO2wd^R%S;L$>~meVsnLTDxBEUGOL@ z<5w9=$M@S;L@tY@Dj)h#<2$`k^t?jUqV$QvYQDbBzkHuKs(enHSb6ohwW!18s09Z% z%<;LoW`~HjqDQ{o49n7vw1&ySt&^8_fPOGUGw{i``vQC+I$!!1c%J*XA#KZPu7P+4Sk?o;5b^ z_pi?me|7fhrk@f%{Ob>I>6z$oXAbupL-q|t(lB!_yHl4)H$;2R+sYz7q3%_3qIg<%}6?8p{yVG+k&W$oYMJ0 zr@J3qbh^N#q1W4Rxb&yMpOb$(gPagO>8a5Rt0EWQYL3u;m^mv%R%};M|0*4)srM42rXF2w( z{o&~Hj?^#SzeHc?9sBpc_0*oOdGXm*d57~m|A{W#^(!&z?hEdPBCATmt4{4*yd)ve zc;=rKsjahouj$R6#Wi~tSBS(kHthsEF;h_krS4f=zii#)Ooeu_{7$K2{qlRm@6a3V zuHoWg+&z}lwoYqVBC*Cvwj=1Lg738T?kiVh&A9d?B}h+IetMwh^kCNT3pw+i8Qixt z+gtpCe?{Ptn)v1RS#xX`gl6b8$F*hVM*lVBy0V(LGNw?0~ zb>>(ATksZ@pk+JE4?I-)Wo)8g_E-GZ>SMd2tapDpog9*7X|?K3|HSjXG7qZ?wrGB` zXq<`?P7i(^Y_f^z%QUtvp6U;cP993T;TNSe zCuRjN-}d5D54PW0Z)}uc8FBq-9nb2cU3?4fxLet$SD)OeeC5-QrG@@#PaK7`L*CjK z+sA6pFL|oJ)w-)bbcK19_TEzV6KjO!t@1l=ITWltGq>L|KB6nX=4)6LU#s`5ckb&M zSU$a02tBP8^5OGO?Pubdd2 zVLIP^c7<*3W3ydN<^lZqOIH72x^;4P#svMv**}YR&0N1Edgt7C&-iw+SKA-7&bhve zDeSnw41U@0_M&Kho~KD>AI^4rJ-b`hS$jb28{^ZwHEz>3q+gC>nIKrSF?wynw++|N zRkTID=3OZI^{Gn1yXHT_eAcH`3ex0%o3>PMtz5HNBuzAB?W7+UR^8fl+uzh^TU6+c z$G;S{uF2n;P|6q7nB~2H?ox-Ti|4d7FBSO1rg36-hT;2VYH~}a{@6Qh{np;KQ>NXB zeP$Ic^g84;Q}Qz=zKks4TRT@Dk_U;v#EOjvv>Budhq?cpg(S*FtRz0J69cckSz zgIvex+-CdJTa%vk{0-5TPs^IGn^)5QwBr%e$~#m1ck#b1J;*R`y4gdCD78Hy&-KC{ zxqfD<%rSLyeW0q)`$I1yo#WO|jxC=9Pdt&Eb^iAYlYQdnz86Nd-(OqPp!fLq!wUWz zH-DQvahy0o`kY|cvd4>pSH$h?^w@NLT8h5^Mx{kD6}=~aY@3`~bMvG@U83DFd*);I z+{gGEKeiwI@%+FK@dH1U6J$Qh?T`K$Xi&$}WB<)h;<~R|vFE+6Eeg>S#h$G8`02Uh zUGRzC__N^?i&pBMz1W#mn&(wn)#EaC;kql|-WDI6>awU|%gu*YTW;>$Ft2cly8GMQ z-T4l?^-uR^&J$XaP{J9MFjavyd5XgQR)%@4tLykxPZXcvKHneTR8aYEMa%^0sr4Nz z{w&@TxU*?_7RTIOXV$lMipeePzF?R;UE=X1iN|uwx>MIA`<&a!zTZRbzX<1tbd%}v zMHganFURy2y%KaSW)08knf}_wKv&#bbk1yJ+*_Q^{E}?J_}Ir(W;oPCdt=H(8^4!l^aMEU#}r{iN$1 z!NH{VPxi{UYKv>y0e4q>99Vx*=O6Q&*QHTM>K&v0Yp;E{{f$k4mvLpA73WMo)AJ!O z1zK+Qthu_#=*hO5VpB{1g`{1KO}Qv*wsI;%sP=Py1O zziMlo&??~%p&pB6jdIqXZjER9v6Q1J)#Ru~`HMYA+TsrtZ$0NNqUM~q>e`H5ThDoi zu6q}<(@JO+`>J0tAyX&ZEIF~JtE*SCuO~KZvDQ}Wvq5(=e<=i8UM$jF^7+%TWf^`p zhE2&wmYsQcjJZ;Ixwf=r>`$fTfxN$-JBDz7ay$2PPlHkV;%kMjGkLeXz99JOo}ARe zc`-8*53FH6)5`FqS19qbov(9uDqr5C!&?}pRx)U(dnz0Hn5ypmkb;mZaiaxRY%9R-zTg$fk>t021^KEBNJy>)8VC9Fk z3wH&wyHCFy!sFlmoLOi#H`U~_RI;enP17yMGZseo ze15_AxMtVsjHR{5s&?JaSlr&jTXN%u$MIh77n#Yze-Ga9syEtpv$N7LOYwB|_lvDr zD(5~fyLLf$3-_HGuFd5EP1%-9<|xlG_uT2Gt~q_`l9->9rxbEp>=xPh)Tp4$@~qjq zn%OF*vtMsnx`xG1{~2GDh3%ZJJRx?W@(ZVi8s7))KuCro@`sc-ZIZMtsel_0w zDo!iF+$3!F*Ef!*3VTMq5$n$>Hu*0^fr)fdZok6qnz(qj>u@5Qgj z{8X2SCj2k?GWYV8tH)!q0zP`rdA>k*M{~@2mT<4~S5m9z*qz`Gy?gXd!;59J5`HZx z+bycKEB10&{hVo5Z@=- zVui&%3O3H{ht9N~$@F@Awd}&iRXX(^bN9qfmln=1I#Ra3_jvx}kLl}lp7b}EoUtx- z+$%6gF;Mf`fwy1T8(H6%pRRpo7^;0JGs;FU;0e{pA%d)&wOOih#Aey}HhdF8R+ zfp=>T6eeuHuq8Ryk%6m<%*?w6PR}kR{85a zmX~^b;^Wjg#=g_`S6)h(e=z^!!fku(RPG+m{B-b@oD0{f^i^7Si)O#%JneW?ZujI5 z5p$22%Qgjy_7?K{{7p9aT5EFmhlSGhx~PnrCO_kMi?1GIobkhM#t-$}$!^^TmOC=Y z&FWtk-E*6F;+%!ESubRLzP3y_R#W%-G2K0L*Hzx`xSy2sPq{=nry~EH#O2mcs;|7P%Fm7PIyy%CI?8Rq~x9?)M4wU|OGq`-8 z!nLee{pzP`-_#oZrVuDy@9oc!nZV&mSwoo=fv5{pI^Dk=d`i(srBNw+!1UN}qR>JjiYFm+g9h2QL$}? z?C+F+PL6)kU*a`d-mCZjb}T*jS@?Kj^R5-f7jz`gH=TZ-G3%pE&KLHO=8sN~-M7hc z3nm%2+jQqxiQhEM556~$lhkuBZQb45 zYXbK2e)h6fxnb_1Xy?e<{*(6|i7GSuEQGg& z-I9F3vSRxar`lAX6~BuY%sYEl>Q#72?f#>7A(iW8R^4bnwEdOxwA=5loxT10%f^}9 zRzK96!_JtzyK1~WJuOXX>Yn>g-xP=+>N!y{_s%i-%uOHDwG96;O|N3!`QLXH`_9ub zLY2H$$Db}ctJkmWlDqNejYF%aoIIJa_0oN#Sqn?F)@@X)7k_{1<)hfTA6}04^y=2$ z)u{8heYYrDt^A4CLuSdi!(LqJXB|xDuc=*^8&bTUFCXDB1&dGAAJ$y@X_79t{-(&0S=0}%iKHH;E{xptb znx+1U%Do0hbHi3W(o#zZ21$mdSCbi9ScT}6Zct)pi)82nfw|`92 z&}}-bT=Kk3ZXti|ydNC35-UH&+xy4z-xknGmRacJ;?Zo*nY%}F?mUMNiwr(5c+Pdf z{lh#I;NG(}9_E{)=>3AuXJUVY|F^O<`bggg{;l(eq#I<5}oUhB10V6AkXgA420r0m?^nft$GZ>{@lws!5^KT*-q(J%ks`Tp!| zicyP4{m;Vtd%w@Eei!-w`91SJAAf$<4^*4_dA8oJR=Z0t%=Jz!xS#dKy0`AVlJsB4 zS^cKJ>cuTzCX3(osh{%9|K=5!`6-v@ztlJS6H--w^+mo^?XyKTzfO48Pk(N|$>9Ia zJvP0Hle>#%nw~2tT2_+lx9zd2^_(>)FRv_|HaS)*{j^7UQQtASxG`e%P$ z@|mzK;)R;kPM60);%9{$PtS?byX(0@DBR*FQ?Gh@u&)2fXDe-dXT3CFK7ZkdQ)QgW zS)X3*&6$$ti#{D)^e0FC{KX$am8C09icRM|f1)KGUfkO~Yvx>=c(y5Lr+;MX&EA|T zn{$eL^U7B7lTVV=^$UyUc5FA9{bo^)X_F1c$A1!vevc;)yKXv2z6SIp8V67cUEaIBBQ2Uj4!o&flpyezz9A{wug_Ww-OgZ(X{wjprg1 z0ybtAxq6?G&-mJ^{YQ3gW7kyK##;%|8jrfq886E=}a!_M5ve0O>@_pm2MYhjli-XHkrS7liomn!&B=hDO{fx^gVU`EydFlU}{&v=xyB8(DZT-33 z;HI_hLyjBa-#2N7U0EEh|Nih_-cvp2{FZ(GmDYMDhqrR(v9JA0b!E5|?T&vtT^{K* z_tuK_&6%enL%n9?XP#!8A8z;|mudO2wR1OcUVkJ%Px#l9;LS&0zwS{I+-Mb_D=)rJ z@fD}@C&q}#x1ZT&J^DE--Oog2X>Oxpk9|nsZj+hGeUD~%TwYaw(4W)z=HAza+KCT1 zwYSx>S}ZVs#aPw#{LR`4IkpFmOLKGY-qGN7)r~)(^)G`;`~tn8e7A@&o4F0ECHFbh zv@XaGnfZ8;{tQ*mjStPj*lI6!sm^nV*0}R9J9cAD;U>n-%+K7k58bdfR(soU#3-Qq zdZN{S^#}La{C#xa$%<#@+!KgroW1xAkD1ufMgJUF59z<@Y&#OyW1;U;6)MWba^Ql% z*=ON}v*zpZ#ZGg1_=R)o7W;^ZE7La5c=nxjGUxJnvF6bMQ!mJ53oGw8Qr_e}&vbT2 z8UI=@+a#)G*Y1>w#bV&8r}`~B3yNat-}@}wi(e$_9_ zmR;H`6Y}WN2`in%A8ViJc>A!QA_C&=Cf>kcdpTFF3ZtkAI z#~aQG_PzSD>CBOe%QIV9wJ$4$ZBo7*RG1RCX02~in#lvN2E7eeWx3sw=FVMMaOL`| zu!#44OmFU5#ak}SMI-sYG2!u8pFyBL`t<=iXUgZnkFJ{ zcirG@(B{N)cO9Y9?LY3!UDy#H<}@KdK=|#y1OcPn&bjZxE@$V7a2ySIKDXz=jGcB; zZ`Z8p@HX~TX?9Uq|AMF8s`lZE`{FaV*xeG8;GPubQo6I!>&nlleSGP&L%0oN{~WDU zn{WQPO4ht|U!0A(*<4GVqX(=64!vu>G{ttAVdvJw`O8i32F)$+boHu@wrZTQVPWRo z438Y{W;d3;JAwYylLYf0=)bH{zR%)(q)AuKt?I=vlXy+&o-e zx^M3L1f{wA&y>HYQJ1~+Q!j<5-nFgVySU%vyVvK?%x@p-=I%ch|6Z}|km9td1r@$} zT^d%k)5E6h38|~#UHJa-@6x&kevfOBEFFs%tdKY9nqT3$;-E@w?v)XLJdDiaPLS+O6dm9wy4f zYbk!5vDtaQpZo>=(q9wqu~sVCXDy81t!`j`XWfcPK9Q4leKyX$ou|ay2a~P#pE)kLf90be6T(j(+N1R4{N+3&@#oefj$^YrpQ|LB(ZwNB*C#`HUGPmIkMmcNLddtZRLhQDpmchyO1 z^$+ufe2%7Xi}5d7cB=Sa+{Gng^B+%@=~n;pKI`_9b2~3qOqw~PN9Fzb%;2#87po^Q z|G!mxan_Ny`kO11-qlN~N!LbiIQ!sH=gWOQmNs(_nB7@=>QY?trTHFD)UMu$iND;y zw8KjFx>HVc{yhFwwSxIK@|9YRrMkr5HI+y^ot?Dr?9Cl^GPiVAq|bXhwdm%i`GIe@ zYjuBH*;VR&^5~|hmGe5+d-?f6v0zI^M|PnVkNDp%G|uMxbqXNRB0yKLW2SFdtr zuIkCocbug?Pr}*IY(1+GuctWMS(!%%CjVKaB_gbub9Aea&d-zUAN6p3(3{)Mt|q*m zR+GoKe3)yP3O`1R9SFbQpi8dGSaI-N?#?VSKp=6Qszwe z&Mt&d*&zK=ec^U{qFKA z|2(aY6Ssvj`m?k*I@&%7n7sK?Q@Zmdb*|`;Orxdt_ltt|$L&aY|KnoBXDRl+%}dKZ zT|cuh>Ew?aJ5I0mw-elX_xN+ef*)qbY#L8UG9*l$D5WVsd$SU!PXSBK!7|5fC8F7( zw{*(gChhNf$#Gf3(`f8Jv6qT%p)wn?BB5q z4`Sa7OTYT((VsupmPzNMvTNU_&_~AJh5!9jUL{1EMXM`MTy_bs%@eo~`$g1d=DTIpXO8mD*!pm0?Hr?7-ltFX2R(ZzBKEY)wbLnpH>OULRn?t(y|rul?FE}EcD`T?j+y>w*1{cf7Q)x#gyL%0 z*4aT$8E8T^`o;SL~-%6^y+4RLO z!|R8EZBo(KWSu-+jmDC7s;W!fHFmFCvuE}f9xd;j3F|vvZMYcJ5@gG}=&7e+U_x$~ z!~I3>2X8qtzcj3pSzUT+k@^cyQQ`KNn#H&I1xu8k&AOC#Dsh(Wp}K}mDW4hJ-80xv zw7zUyAips_P4ULV$5U?C%=t8{KdA2C0%emQnOYKtto{OGMhlBu+GLh4RG-F?_|ITQ z`VUUkXt^c2O@EZHezuvlVBXP;rtJ99g(^$B>9Q)(uG;-&e`nu-ab%myZ1XIXH$~$ zC5@Jk9?MR9e?I?uS)gReq&uZce=a`zZ-$rjz5b(5Pw3r@j!V(rq+CC}}S*7 zeo)}{#!p!R?;P@%8=WpHYAWNA4HekHaj<|#>edE5_k&Hct9gP`SIN#>nkUL)t+V*; zhMsMAdE}@0Z(sJaE@jqx+ZQ&Bj>$I)&UyT14w;p6EJ@kUZCN+JX!5Bn<*S#NP43Lt z)ctl>W)fR$T*~?_rE~us^*GKL6_E0Jnb5}kiGNFX9k@|(gxU6P*sQeqO1B>>{r1!n z_ABqd!Z&B%;uC2srRG!91FpZ!?)8|vMCs~_aFs>h6!x-xmvPeem)y^4e)5Oz=~!J8 zk+;4bM`l0V5j|n@_DecfS}H5F0|MALuWLxW&h%z)!|&A%*8>&4hFPSF%X|`GcrD=Y zv5?_LvBX}c8&$l<%NZo{8I|t2-MP0?$&$NT`ca9UN| zXybRimZy_1Je)FBG=KKGX@4rt7k#Z>wmNd(k=cB8oQz96OO0%@{WpGQeR(egozb@zhIC&2Q{{{bi-k)_t?njKZ!RTI&=%)A5z%>6*A4yY$)TEuVd! z#1j#>xpboL=Gl5zedhTq$Mi_d$Uf3=;H&&ge=(=Ci(|Iyp!kSD|OB_%^l11FLwX_B(gV8=?}l7?!-Mk3vcX6Yp4)S zI2k+r%>}8#6%V~zeN|K4GyVzgdQ-r^id8d&YyT#}lB*6YR+^mk>M-PxUo=1AOYok0 zaS?lG$tw1c_l>XYGp+tQNhV5Nk>OCb@xI?!bUF0A!=lu%3cbbeSFM`a*z}#PN5kWb zT2F>XxzLp|8}VuTe762xvF3}))oTkg^jLy!uo#JPZhJo6qulVFpL>Sq^_TJG7db9m zf4jI*H6noBU;pi-Uv1aFb_UN5?78QzRMU|3#am<{zu|XnnL~DmPChd)IGw+;tFB$- zfAi9_2`s0yzc5S098dh%{w3dQcIz}V?ui%jS1*Y2uwPUkF80SzCpF{q%$KX?PRP4y zH;eJ%w5g?+iw=mCekpKme%N*FkLZgji-o^)hMCQ*GId;kdUuA@vQuYP8u_e!v*_aS zl{b^No=VtyE1_u0wK)?uH?~#T-Y9w@vckC|zV~0x&;5_i#s`Ghxi0)A?KgG4du3`xWvcnwvjV|u4nEmc8={f8Y29oaChKFfD=RK<>gRgm>up~7)OYn8GsBx9 zb~oRM@VOd@H!L{DkOTa`|{wD^t(;?j7g28r2(`k1Q5o%9A>^xbUB(^WsXG-p3z9&ON_eGS6Z92bUAU zPQ5W+;YE_A1(qcrlIRKCsPX$*&59pLq-Zl7vqNayBEeQ zHay<{V*f?uHD)hNJU-j6n!(#D_n+HJ+GM`NfA!}7kd#(1^BB+Do{PG! z$~>p?nv+J{dCs`qI%g*SUZ`-$>&{f0X??~8Z`AKO{Jg`sW+zt_n?Ql1%yMUrZGO8t z%rANMDP1sQ7EZo$XtqE>v`&xamYfTdQhFDp_Ac0^wBgm!wI37j*zU-_c_lTmf7N<( zhl~22i*lWxyEV!fe>9u2dXMmlKu`14C#Fp)*;=&zRp+lsFWN%8-mnRro|^rGGqS&G zdbASHnWpE59J;EnzuY}V#N1OMWI@eYKgMUPPaeGfplE}z+pcMDAHQ16ez%BgpCCgm z_t|H&C(JZw)+!I~$agk-*I57klHNY%U3Yx9f3!W7QM}-T>W(#CE8pyRvF_l+zqvCO z3T)Rs$z*t6?sQG{nuJ@)uHV;R(R^|p`Gix@@Z|bmGvb1o85pLrF%WZPHT=GJ#K`2- z5YPO}4kG{5X3u?)_mWMwyK&piJF~VraI@kisx>_Vwo0xA|i&d#3s{oePzsec|nZ};a&mN6HSXwiGv(X*Jdr9h{T zQRew07bbN>&4(dOMkSKVe^$92o6?iHZr;=aJ;yTR-4|LV^J?Obu}X@kp7GkD^3ce+ zJ$cI6uU&yApO+Y@h|5X!^WHU^6Iy%nyH0xPEXA;oXC`fRJQaC*;~&Yke_!U8&9vs3 z=>3k%^<~rs8RK|eai9E~8aI^IMVq{_ZcG1I``6&n`pDNj!rbe&ugahD*6ZrFugBfL zI$hlq?&H7xlxXz+=xv{G%RGPC@MFoX1>2ts$D96*F8x&d(r5RRT_;uU$F$7&xgzVv zYu!veF}Yc;^;X666<__zUUofZ?Jra34%)0U{<7rCq}MC=CkF8xUdiTnv9#uJQO4F- zcVGWhkqn7Mi$DCVK?YrdtjP-|d|b}>Zl_55m04*s=C=l&m)P=urq-h>4e4|mRg!mvoCTF_DcFY~X7 zDlLv(wGMYWB?34logUBdQIK7wyjStO!P?U@oyYq!eG7Kx&p1=+e^sG1&f)QMdByv; zoor^VNDS}Wx5UZmu@`%%lohM@+RmpEk5t+@vS)1B`$FN3<0A{7J8qq^^PUJr7GBFb zu>4?EWJm{R?`QsrbrQ;@|E$*j(VJD2vjzvoq6*ZcguTGh69<@k81>?OpS@pK|ZYOUr(7?cU<{g>6sLyf61TAL7(H>UqmQ zNYn4Fhy7&_`(~5cFY7LN{(R!{D@wWfb`t-R?Yl2|+df$O;O`6WFB;c>H{aW$X1Q7U zWQp?0!bH=L+yT%T1O^5%!spSaSDr=ESDZ-`Xg8<~d7}-U1tCX6Xq>8IexS0QrBy>%zbm z4h9AvK?Vj#%ss}0{0|O?fTH~5)Z${u7P_V3zsp0eivBxyX0ChMI-4fWJ|UmgNi#fW z2#B0gSTG@hNwHIHfr9Q_AH9;^G|))%piB< zeDe=hIiYWf3*`h~1xkK9we5J*4^=twZ=8W&m+<@l{8+;JvzGD7Bgx2{`iD0g*{E~P zpI)oa@q3ZH{?Ct_}Xyw^LW6hexpv`QP zt}I@EQa!jfHmxnHHP?GNZ*i{Hywjair(WK>Jlgkmb!vB_+iYudGtonlkA3dOtm@5s z9m%=b`u^=?sVzGfzB_eOL;mlf?q$(IZi?!S(;l`=+&XDy$+>oBuHx5v+LGIKHlH>r zQ)yR~wtl?(@x>6EyYg?hok`g>Ur1D`(0>+ZT2$-1gfDfoH#~py_3X!xd0V_bT|f3q zO0#v<=^0mal5Z^1`?+Pw8>uNLz7~YF$TY7r>8;$%`rl?ZSDr!nm8Ax|&wDKKm_Duk z<<*p;OWKT`=M6W{Q+%URu*Tnk`7NfMJKa?H$-I(0qo;MVZ>Ja) zikjW@$+nXTX*l$Dy2MYj3A)>SceF{1&RSQeU;9GNGn%<=s*ixw-I;r48eCI->T_mq z$zdhN?!Ma_LyimGS*G0OCfj{+NwVm*9|a!Ar7hMx=IDE6p>}w-#i?c++2rg`)@2gQ z`nG?_US_xKajwkQ#E|3pANu-yZ>)K2aqsHK=s6cY&f|IixP@Q6w5FlF_D8^R_a70* zH6ty14Q2NWzbuptOWewzUNYzC`_ey-F&6FqB6&N5UUs>+e>kk&S(BKE9=W417tDf6_cy{?O$ot?Ye zrs)WY&ytdsUi_`-O8a_|us`y;u8G$|lB}!CFJ&iPpXZgfD0i-EVr+S!{>wWNo32!B zoe?;rd}+$3keSEU)m&0(dG+d+-dp|Y=We)fyuV@2yCPld@R`e&h_i4l-`Mo#N$00y znrTo-6l2W?Yb1G|9e3bJfTMys4c(!S(Vc@xppKcbjn-}|R>miCnFH+5Js<>SQ4 zb5k<{cYNJ_^fXu4v|00Y&h`Z5PTK$UzGr+Tchs$YZY-R-MP}7*n}m`wC1k&?l`mn~ zSk^EhG$P=-U2IBJ;~P!(X4`F@l74qv3r+Miy4M(OpRqjMJDmIRoX-wBS8biv{qI!y z^(i&y8)iyQ`rb8{$9`_^Gex-quJ{%o#!#uk%g;oX?g+8bx0(Mj#P7gWyS>e8B2H|- za^_O(H{Y!zCJ&|Z>d3T~4kGA4>wcB<}T`#NExz?pw6mdoE_$Oj><)y8q z?_C2MiG@wJ+jd2zdpphzc(i4cX5sBtYi_;kkM#QXyp<_lr@z8|IeYsddFRg>rhN0+ z79M{Xo>9JntF@O;G~h+X42QFkEZch=^DTO_wbBbZK9oxtyDxN2l5(~Es_1pTNckD( z>2nj(c{bnm-WwX{?!WZW-(OQMgx)JWQ>gfCcFH={$MaKGod{{E5$0&CSsqx+u-SRu z1p5%fzMXkJNoSYbDV@UI!X&(7IgO^b!>|aWkA8Wevo}1Ox+2e5Bda1n=%3^lS zJ7CqLF8hpaUW@XK{NH*H=N#KJV|sMvs$;Fc-Sqz~)p%~*WYs=%y};xjIUA)_)eOyc zK7J5+B~P;={HN`&FN*u(LoPk}p|MBmu>&@;?~;^WFLX!`!#e zt2cJ#Z=YS@Vfuh~t5AEWcHau$n(sNs4X<{aU#;s|#e7A{p?YeAmA}JUSL1`ZaRmPE!w zS6>yD6oxJgv+bz%*eMWZdB^)yFYl|klF64gxSB3*|7CNT;hgyl<^od(P5bM8#!KH_ zHxMfOT;Mk2=4{dUnC&|jd|kULa#d)i;P$g}uH{R*Tq6(ss?1{97rJz;t5d8Bcs3uJC>4Fj z*WzFHs^T^7s>i?2nE5^OhnBB7=qXvy6&@zjYnnlx6OE5#=0!(OxU#ps_#!ZxhG3czLiv(GHU#p*Y ziQrouX0vAM#2MQcZn8U)oId~R$}J(610SsCd6w^U;Xdc_dIQ=2Tby%BeMIlZ#Fi9q zm2Ek_>`0>dcm1RMxe}q9?*2dF=OC=OH#VU%&>~?~7577(9xzZvld{@cSEhaBL>VYqkVo@ZSep# z_y76*mw$!3%<@AS&63AB+*BpGm3xyX-C3o$SaJ11E@cj5j_5v1&%DV;IGvs2nx$P7 zdmdXTo~)3V{-DhB-GnFS6y9YWxx2%CQnl(%f4%$qCp~%jTQ)NBdleb=w13#p=H66c z(s8`0!lp-K9>*h3UjNLBsKdrg0vD=vPM4IW_SD=v^SL-(ajv9X!J8>FpFdxuo2mV_ zjQh&=^_D6EH&Xd$GkI5Co;Fh_rQPTG0>dc}y_Q_}lbq??HhJ3pe@O>qy@QYMin}f8 z+mmj*(@AZz#XdX3oOJU$HY$Za>Kk|O&p-SyC#|8{dhb%bLs8E<@0NKznDfol>(`NE zQ$)NAHcl|rooN3p5k(@;M20n`k#Jf)y8>c*==)p{U_-O z|B}r+dk(Z3>%M&d`r#(cNM7^Y=#(|a$5|hlmi)T0J11MkHmX46zufVSOTU$x{$Rgi zF}crJoaD&WN>9&(@6LJ*CyStt>TDDOa6u zMjOxBGn;QdW_6aEzxW&LEN#^*8<%z+-?r!5!>o$1gly56+-Em?R-Asy5_V3!@9*2< zVjUji2RF89Z^)UJ8|zxM!Jy=Vq2z_r&koMxU0N5htZmwCze{VmeKuTuYN>a2($4og zs`IoZX4br%W87vsOQK}%7SmgydzDT*UXVFIQ}epaG}Z-O&JF>$ll&_$l}+B#uu=2+ zJ^PZLPhQhEq-%ZLE^&Fy#BCL!x7M%EbvaPr)_FN7Ir?z%o`UAGgPt?1FK<$r8!OxE zr@1Uy_mR}R>mQlou6z`VyY`{0FZ|=IzUzf69dZ~w&t94JI;Q)i_i`=Ydyx{76FWC+ zrivRK4m@49STlaV@I-I~+} zbu06IZQoM_?qC1Nxb5|g4)4-8hwNl;Z-`C!Jmd8G@Q?rw#{;E=O+G(R=t(TRvAfBd42B^v(;7<|FzS)kJF#2kU-{D7&JR@ny$E%@5y7 z_0D))JAbMsVRyuW#jj^dT`t|SeE*rr(l#rdhL(zeZWd_KK5;oWtYmnPz; z%{>ZZ6OP6t`7GM=^4-@VFA8dB$(*6Q zfsOIVhCbuF$%m?P9!N}BI$0*@;QAQef;a#9O80K$yz+hRTn7cuO9iXSxo%C=Wa;-& z65;e;F!A><7P0OdDzob5Y`h(_b2U%L0@m&;JLJ4;F6%v7FZh6~DbI;x_Qe*xqTGW) zIjYyqq;|y4yyJV4VX5BXrzUnT)7KrCdgY=^vw7gUc|zg=W!&ZFD{h88P1*lGWqZ+- ze9541?Dy}=#!Q^ny61pDK?vI3Z&9MFHd}9a&D#bACq&UJJu=t{rK$l;q*!g3kRzcKV**>#hjjB*gi#5 zcHXWHnekX^H5cFm4uL2J?z%vQdTGO6j9=sWeHnP}ncnmcTdanDDUamSa#9mlI$L`yM}&kG^eFz2ht-=5 z4B}{mD$aG^m*gJ2Ion`Ab{-<46 zJm=-|ZOP3idMv&*8~+RJ+rL|S5`*ZgcH8gsir-s4FOMt#|L^(s`+?0WWrt=QIi6_p zc(TDS1J%cA5{^Ds1C4Yahe>QYf2N~*^Vc1{{;V@67rJMh<#wC8QDKIrqZn)Cq>VCe zvjhI}l&7v~xv1IqdxKWl{PqoM*Q%TD@F&air`SvpICFEG=EBIk*UGkp95g*MCF{xE zLe5Xqrj_NUhaXD4T4uCTJ<>Sb;Ov!i9-bgtcYJpin|wDufEmh>DisN+_c!YnCIa17RT=!Cr&r0xfFh`_>;#0n|I|i zg{*kb+jzdVyuM-Wo*sP_7p44`Fordozr3$m+49fhi;rzp*7+BCQx%V&)2rUe{iH~! z>ip$*1tr@bM2Oy8^>#J?9NrtwQfIQ-T%{+r*Uj}gdumBo%<&CI|8_o0nX%{Ebs6p& z<))rfKPJ75+Q{t}HQ&_boVA~7NyLlOKfDeq*Q|XYtHQd|=knBlGm2z#!%aGy>UcaP z*8G@b>}$JtX0YF~30HNu$4Id@O9U-YR84kvl$y;|lH>WpQoDrpu!rV}2OC?C{LkcO zz25R@_YS>{)~>vnY$xz+!A8&NfC|MaP#tC@6SoIgFooX zNLI4yam|_2_3F~GZS%@3_0w5Hztv3%_TTDd^@Z)N_QH94t<#^zp7q*1A^iNyd#8HL zI_Bx0RyC2EU%f5p-jBHNZWVpPZ(l@Kx93!+=&hR9Gv!&|9+T}XE^ZHJktAFbx zkNd~iOU?PePp&98UTL9j(XV?y0^jzrA22sox43LIanh$HNe%j?@(m2}HGKT#j)qb( z*@~T>k57sgy)T}+qb+H_(=F!*JC_$vH8~@?hMh`E2bb^40l|E<56PdssI-}uA(O6Z-E2TYm#y;~P@PQTC_$Mo)V zOxcfPUV2CUS`J(j{2axVFlCSY0sR%)N`Kbm?7TLae;)I{sGFC#oA`@jr-a;Eb=Q4w zi+rK;o9p8L)(Dk$*Use6d*{sgfZt?AXSlucmKhaOZBh;_j1cV2`Xjk^d-9z6h!3+r zg~{I+y)x15gzH6*n72nv9ek2|@@DPGOJ>np7oM85vBlouXNP!YsY*?&SL%<0+SUTL z)4qGje5^HLEm+U=-BL7aX4yq~XA{GcMK`N3+%cfs>tZrqLtmvHZL zL0i`ttwq}}nKkRE6?B)d@y>ANzI?UQ^4lZXM>Cd(d^I>$^ZrMJVF%lclqrTuISDq& zF47Cax(a)Ro(CEo|LI#X&+eBvqAUc>0Wlx}5zMl%II+SAX(`P<$_c)WWM5M6SEnZg{=z_BHqIzYDI%rf$z(8+tqM zZCUhh>+kn=7N;9==KO!K`~Ksa=fCHEu6_P}ZvLUy^}p_Yp5l77X8P=^ms2MHyX?y~ z_ta9kU;3JL@0R^v#=qo8tBAbUef`fblz&YXk@x=CDKg*c<@G$R1;Igo+`8_2eY7|F zX02&geEI#$`la94FMg~K`n_1BzUbm}J++XTv1@i%oIc7Yp6>15H+imK*-ne=NAq4h zwlP1Oc=hN{Jw1U&CvowU-*v>rcW3g>pL_gyLFW}UPs7MAO+UTTk0Iu|k1{J`j$NK> zbJ$mWqt;O$_3Ak`r+vL6OF!j^pBLP@bJ6D*&1XA**j$X%>-`xNvSUZkzRkp6r(fKIN3D5TQ_C3jg>G^$*%8onG$=-8v);5@LYl8!@>X7%*4gI0WLu0& zm`CACk*|7jFPGiCqtNT7Zf;pQb+Uf(rz~}IMp>pk<6w9~vaXQa&x>b)DWV5j2Z8&7>$_byEE;XW2t-Mp_stdT#;Y2v?I3lBQv zUa=0jJ;8{pI8;}N(^co?O3h2Y+YC?aT2#HrFn7bv<4al&`xpxydRUT?{-S)OaL+rG9A(A3PIH~*zT>hET|+zwB^g<6-7?lXI@aHeSC z&T}iCwJ1l;mrwHhQu>y2_Tpy;CueP5_-xOojom$O1ykOzAFE(V%~RN!=GpdBY3|<3 z68nFg`I5e=Bu?$>OqS0He${)_AH1LXx!CXL4yU=-6iB_>nMr&3V zx#~RJ)S0qyfBl+&k~hwAsy}58Ne*9p)cA4`qiTDJ!MVnkJ5xR$>A9R3ZfVNv+_k}C zsba6>p9 zm8C@AaVzJK9_yFA4!xK;H{`}_r8|-PJ?&3_Tg>x};{lhUUuZ!0)phSP9Jeplzp?ye z&EEU+t74z>XXGxtu`0mkh4)PPDGnc0o^P*Wu3dOy!Lw$i-3fjsVi)%`cSL$GXN-G4 zZM7Tsx3(IKpGvEvkAzsQ+bD3zW`4!G6G1l?e%C22dTnt1t%c9k64Gcls}!t@;qQ*sU z^0ipW_pgo33>Un;#8haD--#ywOw)mk;1>DB_Z*ZP*qedlu0)-9Em z$@t;3P2foKoyDd`-b-yuOfTo`p3J_5!9z3Z_KL5Sj~kCDhHE+nPE8UiFPeQSduQy^ z(mM-_)XP_HncX&LPux@95{IX~CEcfHFL|*n`oPYadlxoYZ}%u{Jr#aQaq9X>lF={4 z)boRHp0>TTBYbJB$%0GQ7fgS*ocY1^#t5C5aP5q2maPi!j9=JBWzX!fUG2@qHTU(Q z1tsw-u1{Jo*}=>9tVLPTrz1>Vef1Xx+Y=$hZr-8N;=6Pr>z8@EZ7I+YT77OwveWN^ z*FS3GlsAdbdzp07Q_I$cUpdoiadcm1o>ZyAFYCvNj_ZErBwc>!ppxG_@9w`F4eTxw zhvlk$c;sB%=elp(8Gi1Tv=6tDgoN2UM|+SXaC7$^_x3q&f+HW>P5oWz;JRsR>{Hnijy8Xf zwI|A(KCC*PVC1jV+a_-D;EChZW*Mggw==_^8ss0(H8z~u)cIyk&f@@wSrgU@zEIfK za9&yM@R0?}`IDaQoB6|rsbKjj!9FiWp;?MGn)4>_xz~TVWa~a_xxa#n)zZsXhu%}2 zxIbE{WRjVPTbR;55u?vX_Wa$n;FNPmQPt#cE7wdu_oQ$}>{9WUsv9z9mFI?qsEhI+ zTWsjx|7_xFhWHBZb@v|fbKlF@!=88XhyueNH->jlvqSg!bv&!m%zGdmy3g}O`h1b! zuJ0Na)J*>u`*gOX()LfeTKDzNHWyWK3!mGtW9lU^VtUwx} zqubeap1$|p)Yi*uwdGzw{e}8cF4;M`vG1~T_ZFXfFt=^B=8Cs@*Ww=)WzU`N)8Y4d z&t#wVx^J~gr@Zd0wF^;Ot`KP18d_6m-W6#xaUl= z>2(g=-;i--(O$v*p6rztZ?>I$de+LR@{Z)m9dmrOd(KD*uS`omy>_9?ACX(u*H8N^ z9<_2v-XnT(Y5p_Gf6F!<{84(W<9f{B%6y&4(!T$+mfV_dB2a#i{aU8Wktb8~Gw131 zvzM=5dp&aB(uI-cH+c0UcGd|>WfpKBKG1yk zrO7RGM5ii0shU>Q|5E#{t7Y>Xjq6V3%A!HS7q&0Xmp!|XZ`BgNU1EzT^ZYH5ja`3U zV}bMww}c&@$B(GZt5uVm-+A2FZeG_Z5uV8!(od_T+ju`*a=U#?t~hsPCx6iOd;aVJ zr*4TqIQ?Un_Pm960*j1gH-??O=)UbmRH*Z*E2m0~?cp&d{`G*q`=r_mz-o>jSc{W}AFx5k0WBHB>ny zB*fe)UG=)tc@9zevs)?*=eTbX<<4zBy|=`U@6Xm<)n8S06uf;EDktn=U zb@iIs+T|PHuPdB7*ElTlf990CzV)eEyV;GB9$s4W^zz!x9}GghYj^Ei-O)11+2rJ# zhT>IU`rf>B|Cavo!*lz9N$STf=HGs}fPK2k#2Bk5txPH3ChV8(cP0GJTA5^CE%)5JFGrM#dZ&HGJ#LKE| zX3<&89|xAyaN3A|NtNx-d9YWl>$k7D>9Sp!g}Y{_E!(~Fz?%uO*Z+PslJ`Blo&N{l zf%xs0jIM+~`l0f-bmi0aZrt&UpRRX19bbMa+-HgTtbb8cUac>_{5LeA?8Sj2h55aV zW#$dFjD5SW)6!5p}w-TPLAg=lI=J%e-F6o>e;Gk)~dgHyxXE?RSpg(x_V@ zr+uy(ebzgD#@qMPm9IsqZhQG}EIP2xyz$fOk`q_LQZFq$^wLUh+r(w5n({EYZx-H+g-)(Bp`!mW@yzeg&`XzSD|8b1`QAbNQG3&hB z^GvUOTeI-+uWxsjL@&5-z3}j+KR;3)JaszK>hkCy%e^FK{k~6YbflJDV|fy3b|SU? zrPJb>0<)(Z9GdaY^ULeRz?m6Sx6fT*dH31TKpXMzENY^n^Sc*SwLE)x$-?jX-U6TU zJpM@wZZ3ORy5m=8T+2iKgCE{22UbabKcvm^Aw2O%_LfE z{6}toW{Uo)H4$fj5ooK$7xm6EW0!kY|3&i+ajO=$y(}zFHZ4w07o9$v=cIf0^XSmx z{aSCPw+nuI&+u*kqfI}|t=jLo-mky$Epr-cO4za5s-}4+)Bj(%vwUyo)ASp$k(s+L zeMw-t?qbBrv{Guf;Z@;jhOcRS8=m)?JLLv6B!vQB+|u~u}Kq~oV8a+lm+ z-pJhfl3#M?%iCY(Ue^70yilyZZQfOJX~&#-c^}Uk>-~$BGELVr?~m_$vY}6f>1Vy+ zkw}XsKItwQZdbklm#?Aw-PMgItWoCWc%Gti^ZASQ#*8QZ*e7u-yen)x^0jsTa&aSu zlYZ=r(pWwzX*l$EtWo;1>q6;V8@;S~OHU^G9@V(|D*V&WwV~=S4Yas#1?a3P4w=71 zI(O1!|4Unhx2)LxgULo*Z_Q=FDQW$el3M%bF6iFn`otnOrv02?)Fm^Y-qM@u87pt@ z=aKDxRpjERlrc>?EagJShOSF<9&cH6ZsqDysVvn;tGs0wm#l1w*=HhH|CLM1G`B2N zdvaoUpl+7TE~)mVY^FA;z1FKHW-boQPC8}h!~J$vnB)S33dz}-J7?Th`?ZR<^#A#z zuD3bXnmL#lAJSr9Zzdn~#A=W7;}iP&3r|*@-pkY%!G8M5Z;Nj}`}RNb-f{0yU%s+b z$9W}j@nFOG&XX&Jd>@~D@=(*He|ZqA|KipdH}487pV^*o-z+S(nZE8(@yX*`ublsU z;`~#Q=TcISewLVqU-%p2dN{_n|1(onoA{-|I2ZkoK~=5#OTsG+_a66tQg6n1lIvH` z>&S;}dQaTqj!b8~ofUWP?TgsM+CPi#9n0O*e7&e)x5cs9Kce1U^M4Zlb^D^O>=&tX z4y;c2uf3yf(?nN^TE9ILvI{il9_f(3e);}|`$<=tdH$6CvNpJHvn1A@;lAYy+u}Cn z&)Ph3Eca~o>UVEfb*oHG3h}E}S`hGC@vp5?lF|3e{V$H2UCTKdsxk3XTV=V)%bofU zPt09ldUxvam!AZ)Jp1oxd;I$`z2{DicG2gr^BFCC|IY4g-@aJ#*~7Z9vRCI{dl_Hf zxx}bY>G_-mVXrH;p1hl6-FH0mE%(jyJ8!4#S@FVR>vhW=db3{_DQxLG`7rv;nYcGv zg|?Xmulh@cCa7C{g{56{W&ld9?sVtet}%jJn! z>#HKxz|6eTFyF0<)26H|VE8|K!~5uZP7y;r<5_{bE;+WH7Tf*M;QQi`n9MDc{Z6m; zvA9|wGl%W}v*}N!Ke2TQ=Dq&(^*ZULt3__j|NryD^W|-sc}20i=l+^>c#5&lWUbO? zQE!Z{Ot+B#dATRCVC&q@ZGDMKTejW2$x$7@U`;?+)SOR(_x2o#i*d3IIbdhQXtHYt zdxqri!YA7O{Db4Xk$7g?1{fA~<_x_XbKVSaCDIY9S7ghLivzJ`I#^)cF zsdo13Lq2w&V*N8|Q&W6!*r)GP+3F%ce$wtbwq7gIZe{!H*C({+o5kPL3cBa~MJ#LQ ziIv}!dH3-?{3{aibNPy=AK5F6J7VmV)k-bR_qT?hzy0gQ?-S=-{GML@Vt47qe8E<(d((ZUzcHbqk7s>P3$|+g z%Tl&C@#(Ay+cefNL~fs$C^n-%e&xx-rl)_ot{44(@3QH>|LKS!IWe^92`oc$r6rlj z^AaE(zY-?xozKC*a7>tiL6rgH9v`p{=!8UQNhWkEVsF^ra*=S!y0r4m@sbKUzFwRy zjZAlIHVSBjDO{Y=VkYX8vYlmxsQPSAqmr^RYwOu9+rAxMCc5<&ziVSy)74pFt1sPh znppgXzpkLq{-x!-$Nlq?|1;YjU%&X;^K(3#Pj8wqmfw3`Isg0K_jm9A zd7f_XE--h8fIGX};tQ_~+X@YRTo$+&m^8IN>gZ5)wkb|BI9e5VhO4mUV}VFm&$LAb zw+p8jXtu4ixMirDcKpbPj)>Y2p8n2!m zxmi^sczp4XAd&dKN7|3xS{l}b^2B#P_{kABLBMW?oQ2VhX%lV~hs+HLZp-Rherem% z*^jMNbZ$*G@7|Vmi#=rVlEUwPcdxva<2;!1nvrv1?9afc>2EIj=5ClGeQ`~XRZ_;G zNW0Zi>>=Lb@8=n&_`h)F%>MRd?Z+!6=QO79D4XuI4Bb?!TxyWc^YL%pxz61sF`KJ% zH%VyToYj7J`kH?iey4eC?V7dqan!%HTb;N>xP+qe-gL$In!2B<`XnKBVujdzp^a&0iAQWLs*rwqZ@q2I zCEeUDyL4Ww-Az|ne(NS@@5&pt{Jo8=A1!qFdM4L(UOqBq>Fubf^P)U!?_Vfd!^?Si zYr`ce{<#b7HyxXiC1w z0VA`-%`D#zPfw}giJ$(Fcg}AmlRZ#E-diCK`5M7MwHnqwJgsAGP*rKbE_r{kXcq zm*@U;!}jZ`dlbX!6{cidTgDxA?V8)vl4a%f&peiWm%G1|#Z`tYRVQC_-ZQT-)48Ta zMk{CZ@c4O0q*WeWy&`*%VKNl;uF!=m-(K#anCk$UfY~oE3L&lEVelYo4W)?tNI?h z{bOZM`G<8qpAVgXRr!wPnxEz5-)?=AR1X*VRN0wsSg-U{J3qum=$+TS66pyy*?yhQ zomsB%yC_0e#H2XH$({H3^<9Op^WCqOnJ<#g%@>ljkH6yDQyXQN{W5OHc9ra+-dmOB zdJ8v-8D4y4+}`%9GE=8|!L()$#=FXQ0`{I-zE)p&eof_Qy})Hf$-;Gvg^C?Iywmi%4Qf}pZS-o>Zjvu! zF0~HtJ>?nx^4#a#h~m7+H4o)4 z@iFY-`Z>K^dA{a8)(vvr7Mt2HUvl;C^50f`WovB1muZ_*xsPv1&N&j+w`n(Hd6M*e z!?M4-_w-l)sJA+hzDHLuV}D1&1l?y@{Ac^x)yl=~`rbAe-+ma-m8iaJr(67`B&C)e zWqaN#ISG}WQ{4Go=uo?!V$lnxMV&V)k652uJn!_TBVr=<{jTC0e*Asb_Cn{DcDdfe zN4GOF-aXn}WqyCU*A3}6k*@Kb;TP|0d}T9jZ+o=g#p(&4j};p`X1+VjZ*|V{kFr4h zox2^oR}*${J8m!;i(AUtdgEzHYa~>?g?_)KUMD4#=flDrg_H} zJed4qX;IJBV3nrj1?IUzwQCOR{o+~B|4l$Qan+Ugt5s^Q7CwFQ-+8Hne?jyL!zWr> z7cMp|aa|RWyds^iJAAIo_53TN!HX?UJ#-FV)L>svtUrY0-yXbp{3PnWEJ(7mrI+LrbBiW_pqEUd1+TCM+`W1FtF)Rah*br~J!lTLQExo+-Qo6#}XRmG$|;KSKfU2^L!gmrsf z+dcQG=?b)zRC+&Q^6himtUPyhL_2f5&9P%{ouxPZ!d{~qv5k^14_jVYzs9=zLyybD ze<5}ixl1^@1f3@;-WFMZRovH8oc^Xk7AD05!$OWXc2Z|2dr(*vGgKi-x7YSGWXMrV)u^%$%C=Fya2 ze__d8drz98JIQ$T(wVl7R=Gq1Pen&MRQ{j#^sn`#pMUo}t)0+&y0!M)M#kH-?()@&t(D#H=FEQo z8poQg%Qx(q+~y~h_njwivEbTmcXA&2?fpOTGyfm&mbhBaZ5w^RS@mqQe8e*6CZGEx z-9NG^+`K<~{XITQdOa$tIeR*~JoIUY&N}X-8Idv9rbW!ZF=5AJrAdlBnhreG-<&-A@89Ro;zl=)&udK=n-_f}?Zn)j zYx9rmIsbeyDZbGC(}CicwSNv&AK&mNQGmyk|BL4Ii6@fPMP()_^vAY|`oH)->%?7; z?kR^hB%hbM>U+;sXnVBmx{TC_t-VW}imwPS{HEE)Iy=!&RJQ1>j3908wUI#z()%>M&LDq250`7kwZcOKq-2HFr!B@q1cMCYP-Of8(D$zNO zsi?>_eAnT6#mAI?U;?(A;xFul8)}{_#Pw%RWT1^^DFEl+=n=W?cR-!~f zmd7Tw!><1)=-b`cI;HI9)|xK|<)0SKpULc_#wqZ*;Mts+pWp61Kkxl-wf}#9KabBi zl$&$>gG=*)p0>!Er4NmY3iX~pDT?FcE{d3Pc#_P%X@@t-ggx0Hb$C{rcZHVt`J{;J z6OOp?+~+W{pH;QvSA;_jhl*Nv>!xgxJEqF}(@h)g?QV!koDrQn_szq+eOsoVPyhZt zZT{P5;+C-{b2dHokzN*EQYR&vp&h|)EZ36BdB>Bt^)a8!;*d>q9{*VW?~{mvRpgzd zl zV6si{Gir|UE|g_2y~-h#bV;rAO=P%{Pxhk&6RH<$^6TZDNIj_ad4X)1<(}1=s*ie~c6wY#?&ts)Z!DFBIJF?%sxn7yKs5Nrw zpVaP^bGO&;+wf-6Fb9Hrlm*R;vyiVC{-X_*QSa--@^K z=y5LJ5RoYk_bqu_8~;8RZEdpMx>7F3b(7wl$!mJQo?K}9<@4>znei7mKEL4Fwd2*d zuyDbtFOODQKdLF~{l2dFD*yZD?jO+|r5~jx-rsZn{cf4)3f6RU3pdO9^W1NO8^23x z^f6!7G7#dBQ7~KZd-jKY$74B77c7{@p&GhnxpDpl6LrS?P=>5MOwKHR)eCC>mDMyj z`qx<79q~Q1O=FQqgND{?AHls3LY2M-dMK6y?ynEBehV=q_a7gpXMMhO$klmZ?nvf1+Wy1!br!Q+JA;lQGrbvj+u zGVU3FO;x@JKKS!d_{WYW@lElXD>@afY*jvTIBWieTQjW+`1jlq-Sk;=NA;;AVVvbG zx;i@IA2x<6a^5s~eIk?lbY#knAX&@#nwE>dK6#O7scrhSr;_dT56OKBCntS*EWC63 zjxF;ld%qRj$~X8^=@1swxHagS#r=xZOT8cex?h?7h4azX1N#o0wMt*hHur!ttJUf1 zqWb0QcQvn?{91BaaC*cyGuC%35?1~x*)vkaS|yi>9Nv+1Axq)+u6mvYY{Gk_N^f=7 z{DCiYgcMt9&m<(pm>3v@SP4~t!Kp==i8+~7sYTGj3sPz=4T;Scaul&m*7RPvRX#v; z?HA|hgIv*Hedlu|IbC8pX|^^0Y>Djl+dQY!%EUVC&$8Dy*q^G9%yPULA-#F#nZ4=f z_kO=)z4`aw(AG6Avt%e_hF(w{;GW*Ri_%H%87Eg?Gtmt1{hv6f4+zM^wIQm7?qP z1s#gotk!7$XK_)_gUo{*5=6qQ>H9iZc}|@YNF24yY;F3BK*z^-&|f- z((-BNm;D@H3weTCbCO?l1^hM<;nvrA6J2p~!a`2X+B3FoQH!qK7WM3YFxfM$^COSM z5&P25` zn=5s)lXr$3DXiMK$oadfWvfc&)rovVa?Rk^0&Khck_z=3nlw>w8QVZ+mxP*5c(2BF|3xF}FlMNniGd_0#6ye@bG( zU*;bWa`G0^6OLOg{Qgmav35p>*oPNfzQI#v_?K%eB6jZ(jNb;M}4tkGquxzJ@Y08EG*YXynRR$2;#d zHv_|dX$A&+!kU9i5=%0Zq17yw2HV~qEK-|yVH^K9eU z8{eS=T>n6*#eUPmARsa8Uc};z~+|T#1-w$*~)jT+CRHHoouzBqcuKPlV zx7*%$c+^k6s9-C9`U5G$811(APaaym5jvcwzwY}ZHp9IayJy$zkgPs(`+e8L>ZBd6 zb87E5@3ouqAWZ*T=li;il@4d}gv;v`+HM~$XqR~RC6S?7PUg#_2@5KwiRAJ=c=rKbJ)LN{_6is2fi+p`1M}&_;>Cs`>zifZC)I9 zu87c)|M)2IF`j>S?)mEjc6Gd7|8=`9W9rXl^kL5$ANZKB>IW z-THZWeMxtboAceEcAfN8l|5d!FD+Kz^38{B-_qJd-MkkYGcGmX*RW9KK5W=ELEFXS zm~-MJUC-p5CW}h50~d$BHfEXkY4T$A=r0{i>7ifQZs{*ntya1dw&TRjEU#(qLfW&V zoH^@6eO8=0bh-KYiLiUw9FmHx^WIGme0M2h!>U!w3^g}7c5Ko0tMi)7Wa{-Y<5bH< zsaI1jT}o9m-K#F27jBH7P9kiUgy~q(b+t=O#G1 z+BhjyPW|-3;i9hZ+0%P&PQS8LUxfE%{?0Qq4Aop(w>@+JHRHjPXeYUsm({x0@0_;& z=Z)=F*Cumh_wF)y@?@g%VH3xIqv{3Ay8M%-vhNSwD78O$Bj0&#in zSFe8k_U+tj*4eWj*cAUN`4n?+&#aGEWLI5X?5Qjwy6|n!D?!EOJ#$?8pB?1ZiaX-1 zb*LiJwOzvMqq=DQv86sNGZ*N-d->vmkdxZ1nn~BhuODi=CKqvcS4QaTool<#akMOY z8&oA)e^6U1PSk0iTHA~Rnr+uxADmx(qsiFz$Lg;A58t~~PPwq)hOcg~);$L0Q>)#r zT@K!GcLKZlx#t<4n|l)R8>sJZ>8-%(d4oBh{h z&A0JdCmu0%wY+>g__l0Nu;}fNl5)$q7rFOdS$rUaP3**_HJmD;UlM0`c^9ng(&yOm zOs@20utD{d?T6z`FEpNAA}I4PvP|vq(vM#wVlQ`@G%w%j66_+-$Kkm3i?n1+6_4$t zq>aY}G*>20sY~jaTKHCQvZV6EPyNc;#p`trChNEBgtgAx?Q>B@xJ$)&)8DRbzCE## zPU(Hu%2S2+6;5$a)_Wiq8R(i@%H7{_DSdh0%6y~Zi#3~LBPG3G9nzQDl47wiTJMo* zlBU44fA&2VGWSC^ZqM4h^;A?)&QME3+fFahZlOM(-Bd7lwIvNS8+D!})T{f_BDM~&@;&khvz+3k!zHDBrY zg4WGKpT)uwmi}4!NKACPiAdO{ukR9(;J&tUda{qxvj^uZ}&tr7d6oedk!gr>$#xa3%AHYdi|&>>)cp zygg{4cwF&`W`>|~dBH2i6?#|rJVTxP4rng16#p5~;B+X)sYa_n%J0C-8BIrL^s&v; znL6qEj61m}#cxg0-=d;_#AMFdna0lS-^`}*&DNcwHHW(;xKh7EjNjvvXND_bR z(3gF1$=+=psx^FeBCDOa0^OcXej)4Z5Ek*sV#j_#MI-Ga7F$voje5$wJ7!)|{joZ{ zqd?%FkMr88UqV0kxyD~8Ds+Dwf81lrvGkx@23$-1N+PF(MISpWJVniSt!)QqwQ)zT z&t|82xve%YZm699yu?}n4 zVK{Z>a3`0A`~j7Rna+lD^f$g&xHPw9qHku@IgiUh3G zGY_!O`leuaUi6Fn`IN1~tOptAJg^T`)4R_y=g8$y$NJP;k)f~F%u4_2SZ}a(-l?w} zX3Rcj@i=ql{&PG#W*J>qSSG*jmjA{YjfVM4uZ66bm&hJ-^selk{hjL8hK%8x_r)|v z#hlKml|B7Yk7?U%uh{ckMa;hnH;2?oCTb|CGTSb>=Cb4L&8lj%#*dq)CO(*ym)fLq zXpyhfqWR`$st-3l<`?GP(RoKOe>*>O-my!zH||X0-5&GbXWrTqY|-oL#ro5h2b}(T z{@JG`B^REhG=*s!SQp;sm~r>e-RPb59qPXgKuHVaYR-}w_IpDW0rZx z=Ynvlw8=Z|=3kNOoMIWBeEyg*FV~rt&w1Moy(4DKJudxhZ^;>%N9hY&BU#IBY+jdL z+pWb_oPXrw!B3yMdYM<)cT9dATYbSw-+e{wG~(MV0{k7{z*l#z-g$`yspR+_5FU;eY*C>0tG-Kx(@l-Pp?v-i< z@>h4vSLgruCUBnJnNzJ>fUB`J*W3%rSmzxM?O!weOU^G=Vq?xu{@Gr z%Du%{=fbbK%P+A$(_eEVQ*-BmkVz42bD3t$c{JzHt=JWlPE5Nf>3`4qyzaviVZYCt zR^M}#xM}(^uR#1x%#`pq+c@TB-CM~yEquy${v^ePWp5jn-^hKVTgEN1e52ru)WbW( zj>^P8c;(9VLumEK(>jvt6O`hf&b`j@^mWZgw)j1rmKEKy*KX%5x}W&^kKFDn-){!j z9hmW#f0qcW=g~&Hqn&TP`gq^V%7?*8eHJoRMyho@V%xw@1G zR{eK4=lzX;z4039RYD%CQ(d<;cD;!To3b@3u5W$Mp*4x|Y@0Px!YV_~?CV~H+KK2 zh4Gi8neK@#xnHs2L$vtcbH4k(U+}3q?)!e%Oa8oHi*qdX7^45V{MX$3U#RCh%j5mA zU+m>J{M+3A?Bf4jFZD0YJHRt}wj}emoj3dDzA4yz`D|ofnIlVCwZ&}PP4gZnoprXW zQg2WH;KR5xuPW-64dYLSFY~_aaY~&wJN@*614oOO*m6($5X0EI$;SWLGj0u`03B{s zr8k8y=jtv9Il#oo&8k!M*>Y3q#s*cUQ!G!o!ipS8@_f4dM?rT+TW{mH*}?$mqTa&`Up z0_OM^JAO$i9p_}=k3Z5Yw{OPmm^*KtiSf6-l(o!R-C)Y3S6DoIOLlC{%I@2-d-^i} zwVnGa9c8t9d(Qnm>-O$ky0W%9ygb>xIQjRlJ~6fY-|G&{bMUngkX-T7>xk3D#;ZQY zvldOPP!jO`!g9!SuIKA_IXPK3?_J%tb!{@QaQ@cr7N463Q*@TO3&|F$-S)5#F0D2( z3)oz)VKaxPJ^S07Xk#I69{$%GXPtF^$UEbeY1`Vy1SP54?0&|-lN%cqC)^O|IQms0 z_$qsV!MB+u4i4QcZC`#AD6H7-@=GB~I(VJwR>fuC+rJr|y*2ISl*ub*CngXF(?o*fJBx#Ka`B~!5%mhi-v`bXPvQD|GG$3bg-ZN4)p2g3tR4`S}xQT66Zd0mFG$Kk=V{wQPO4xiY*3oG<5zsN2z z|FeZhe%<#uyJm?*HM?EEuvPEgx{rK(8G9zbbLr7Mws6JCZNdCsUK)zCo3b>mJNVc< z+vY&CR1j~~ZkJhwR;RX{y3({VVkw)(c73TeFRwFmKDkx#H6cl34~O)xF9|!cjQTTb zSe>3Be8F2kZ>}9>44o zz1+Tdqs!D%*AU5<;q7YcpR6|dr%~_oxc~E2sd(iFQI>zzmz2+K_{(Ic8NKegbixDG zhb?#Bc{4d5E|S;dRCu}KT;7v~SEcT2{LX*E+*BfbDB;tK+=m&0SPV{65p`Ps?NeWM<}k4=sxp^@3kD@J`im^%w$9JjP;Kliu%T20cWj23lW^!8mzB4zh z?gdwW=1=wq^IER1Iel@-s!#JBk2bv473I!-efi47kJEfsYpb3VotwoTeExsCJ`OQD?YEp`PS=!q^S>Vz{xv~l%f^U=cmu1RO{b>&75_aKTV=3um+s*yyGrk@ zICEBt>*)*||AMXjCk(uEl#e<*&AfUeZ0_Wx@BP>gebn4K`Q(-)?RouAeV03|x0xKy zze%I(VCH&@WwSm7UtKeEUhO*{Zg`$cR^&h$DZt(5b8a-cyqacYTr&{ z=@%7jC9hVz-PBZD*ugblvd{ISTSs%P^t(?HMj1<(!VRoaJ`_K6o0!e}l~-bmRQT!N zBCiUz{VDl4YthM&mmL1zm+3E+<7bUH{iW`V@BJJbXM<$X6KYSJzU(wv`^0zd{HDBX z*VhFs@xQ-2PBXz@<&@tC;j1Sm%Ab zFu6F*Hm;${=JL5MlbD{aw_9*LgHct()2CWv_Opu|Z(Zx<1#f0p9t*z|dhN&JCliu{ zYTHAmt#aMFBuZq-Dp$r;w_cwWOVzlw+2Jw6U6u-Cx9iQ*{`7CT@?2j1+s*z7XQw}} zn|Gn`M@_uiaqd;`O@5uP>u6qIW5UMM<~My)KW#X=6l%ey!nQ|Rk!cG&}Gc~Igb}Fl5McrW^+AxiKomBHusY=pRb$UUl60dAo*YM zyimT*Q`1*QNmVb6{U#fFyyVvEOKy?hoZ3y-OgXV`(geK{g<0y%m6AE0CLXLNEzWKN zM*^+d+XPj2Ka4h%(DbwQ@V`A_UYUaB*T)h?FP^sTc(#yx!JeW)X{3-SSJ< z1kQSTd(pKC>%Uq1_00RxnGGXk{jB`!~Usnp?TxrC< z(tuxclfH@l!+;fihU;N|rRD|_hYI4c9y^B^I`|GFi;;dKx$_L_B4&N0| ztvwTM>c*WCaw+;{>;F;R&<4SOtrijZR=>tS6sEy($?|$qi1#;hgTgmy7Qdf zwaRs0PR}`M+mEs@Q$H+Hd-RU%`-NBHyQ#+CXbq=*y~+QLAH6Gm zvhn?4%esb5FX9C++P8iB&wK2jX2>5lp1;OB4xCTyh;R6@enD&e;VJtU=3mNpd?@=% zyh>c+_{-^E)Yt5BuU}#xZMdKDoC#z5hUQnIr*ee;9$)*yp0CxO{qRnQ*Oe0WpB~NP z+#Yl?KZ#m#Z$6gswvN4j-j9nSt7C6vuh&1cU~b`molifz)iUPxE_rD%>#NzD zTNifUXL};=ceY<$gZ*TVobl9+yoTA!L*Fb&D{Gq9YjxAt@ms)@;uj3Jq90ycwA$~< z&d?{J3m>#Bz4Jw>DsqR~>Cgoh1@18nXS>@@O(@*IipkaM_~9(Rs*NR8vsYd&@!j;N zIwNLwjlh*7f8#$tzuJ58SXI4T(UoIt^>RVCk43fHulI>cpZHectR~wD{Q`z(A{LBu z|1A%eG(WF=vMhAk-fOCdqskjsAE@nk^p9!Dj&rx(G`-Q6TDWbdM`W$yzS31b3Rd#* zlKZ;P8m!Sd^Qp{pnM~;2yv4g2?)tiw^X;;AGhc47T0vTJXZe0^wjHiKk%o~iUmnD% zs>q0+Uph0!qqstD>W7-BbFLw$Rj-`lUePXo>FJ)1Jjbe?-P14F#`y6Uv&tsf+a8?y zMJ4>Q;JSs2YxIO}FjdW*uC4vmIe)rxUfiPn&MPmsrMIf+h@YA(_^bNMokgLi`PR?8 zx{*6uUt9ZZi~asZI_B(hUrJN23*Go5AEK3Wbg!1CAJ^V+-RxS%>VnG`tJR~u*F4rd zmm%1G*Wg~{rZ>7JKe*>4U&(&)?xoW0!%svvY?4T*EWD_=>R@g~$EPc9F>_}3?moZ4 zabvRkT49~mBh%V03j2%9?}-mt^PxNFhwJ1Yr{=h_)y+M$Ufy=&Yl+;uN}u{Jo4t46 zz|1kdQr~@fgw^ef6RU;AWS7h8dmZyJejB!;cL(3Hwe6YG8G7*@4%sU5<`2*1I7a0y zRf}U5UVQ3_cV@?J&6BTRc)eTjy2AGE@wo=M;y$;1&wo;@>a0$<759rdKloX$fk>|8 z2U`{I!wv7F*lJFjEa2x1>3O<9OX8Pjs>8JUfU{0-8zN6#pMG}P0o(5r!$Yq)F0PYX zTIBOR^77x6nP00kt-Y9!9htIR`Mrucd!gu~nlEG7Z%C~zfwjWu4eCnUI zTfQIjDGl88=WsSxC3Dol$h{}5E54gZo))WgE>9OLycl?EYUB})HNs0HZG4XInXP5v zHdV%*%V$~Vj$1;7rhkR?7Q9s2*H?XAps-hSQI*R%d#`Yx)YWmZHL5a?R+)&s>_2gM z)v6^Xl2ptCKbM9JMy_6MXDxGIe&ikz~9x-##&!^^2YE%|BK;?`Y|~ zU9XoF|FwG;(VTroC&25{{J4ExhM{Ys)~{YUhkwE`^FX`%JYVlk`Zhspqs8H*ucE1TmWln5ySAFW$+Ju}Qri7h&%T}TjL)N3eaX((JWB)@`%i!7zQWc|mDlLV zyI{{^{Utm3FWuqKl(S&Y@MY9F|~ABavnpsmvwz9e-2OQ!$3AAOm;AHJ>;vO?qE zkFG1#YzzzrA`A>xgy+9eX1-lgb5he2OHv_AHRgu>E*Ewc**ANc@9dmP5smyM8WTjk zTo@hWuGyJHc)jGl)wRjDt>koC`R?7CYnAw2!GP73)z$TA@t2BM{QnsL1y6&@zFfy-eX7#ma#pUf`b;yE>sK!qU(ZZuJDnxxoH2Nh6zb(?uE?w-@5{FQmiCP6^i)F4~yR_!)so67PEaokWzA70bGe5lGPJq<4#~Xxy zO=0pj(9>wwy6u$8{b6Dl|8uv`a<89BhQCg0^A&v+F{3lhn`@iOf`rb8Ou?tNe2#0M zxy{HTdP1y<{FNRXyLHpDLK2(>q9@#ux_y56wXfF$6DI!Okf&NbbK1wdVwZeFg6rHH zt70mzHF6#KuN(cH}+PH4e@1#5U)q zt8dx!aC_#C3pbN)G57iXbDHe(Af5G9#+CfJDJ!Z|**2R*w!eINuS9<{FVFMS1CeTY+>e^y($~sqI;)k_{8sBtgL=pf_TQluQnx}f z+WAE1wNBwWFDSEQLY~DEC;nc(%Q5<2RvIugYw=tZK5Ve)(B}f5R`Ca}qJ6;~ZswO* zBonlHE_y7VP?%)N*~TT-o5MA4!EU}r?*|v}Fg(fJnkgFBG(FV9`QTobbVmL|x&NPE zZJej0zlCY_yYEH834j0QiO2I)1c}C$tw<5;?{&;L#NW=cp!1`>Pbqr?uSH&?ZinZd zS1khn7+q%nXIWZ#Szh25?|P>X8jo!LurNln`Rlb?MKDHxT(wbvxBuW{h8mYOKe`sQ z{hD+8hiTzIzJ-!2f*#~e+}w6Q^6btlPE*b=ogi({C9$h8H7Z)Q&3AR0LJaHXU5jFz zg^wi2K97v<(9H@;RDbj6j+PDQZo?}6zQY!q#s6Q-+js77-8;j5r+7lzSr)$bp42BQ z-f=Ma!l}tFx7pNV7ay2ww9X~r(u+#_mzG6g=Nwd20=9TODLG_ZvERh|j;qkED8tih zg>Er3>ApL?;E`hPT#wzcPPdO{ir%_^@Mw2e-%Zg78ygvwy*-^rZ);BsHt2S0_t_v5 zI_0vI#`6tZtiC3dT4{x_`&)VDeRkP2rDe_i^Su5(_xW;DQ$9N%I%n0f|E|y><$^6| zgq%0&Zg9B6)Xsdmt$l~e<<_KS%%ZzG^qK818(H!$nzN9>a_P#4)~=V%yDr+>7y9CP z;mf`lowSvReE+><>$48D{&G>e{Shn8<=ugd(*?;<{upH+c0|{Wxjjt zkD3bmug*t}^NwD;!(8i=b|7_8n*N1`U1y9>ySA<5nkPAZu9EhV;3p*ur2aU4yvn1P zEm8k^XQ0{kGY3v>2-sHaQ7})~RJ^G>b#=SUcJ5xEI5Vk>ZY#C2Me6uj#S^A#{Bk$$zvs4vpSm)CV(z)Gi_Yh>&srINY=dLU4&f68VYj~YCS86XmhsC{ zuKpj|sl@fi)~)GaWnkbJB(_RJOwyoNY1!qM-2`NB9`nm#)oA>XQ8BH_lVQ3E>nUwj zzJ+|5PMj=Kw}K+?X?UfcoVrv%L-)(yf9iiP%)2~q-WTV3hx!oz_a28h<22P|S>*2O$s{@w3xzgekt>zo; z^YiPfg8ZydZvSn*R=d*9W?eXDRW;+In9q)aU9#Wztu~c9r2BH(wVYYMHkTc1QRO)+ zT%dbD$2L?Wog?3CHFH_@#~V);ZCf%`=d78R@x1I?Y+H8EKD7H?S7mYJ!o{wU&UJ?b zQ>8CX%iTD`Ovg-m7T@Nd+NXDAR4hw2FaDC;S9j{-`Gr?A?`Qv*=hQqg^s;7ae}nRt zs-~w?nDc%;D%ZH8nsaxhWB9_R4cWVzO^=Th>ac}88 z__j46oi&5em+!}Sxe}GEUAr_&^p9+2{$#G3IZc1wQteAjGpp8iMozHIkI9(#-f64; zv12mMd0AB+TbxeGRJa%{`~IM9hPwyv^^2@tht0IgE*IC%jSOE=KK1_XbuV2_XYQ3h zBYNxBKQWW5GJDlZxVJsi-Onk+b?nxM{v#8`#UsUIO83r|x@7pRX6x0J@2h+dm&N2w zxy}8-Ol#A^h;}Q7r@f{*$FDI9uc?d(sa31_w$rJC(Z7mmR#^Ae9s3pr9^crK#-4d5 zd3Ee%o#SoIdkwuf4o2)h{5)Eyqt=&%5yFz>YCUtz$xuxy#=Ha8eMN2Ok9xJ)( z-n;DGw8Kngg3Frrc||j+&YE9X6E6N(ZGL@q692ZzZx(y8zntUs`(=aI@eGHhj#ld~ z8SGtnY4)E-Hn9OOg?d+f5sM9b$)$h%o88BfW~hbEdUmt)-bcN=0hw)it4g}( z$1aJSns-@t>b=F5Q{P?st#x;Cf5@s$rgOjQ1^F&~C~i6XV)k3xMFIb-_9!m(pS{`W zu9b(V)t0W{jS_cuy^(VKxF+zHzUJ8v2QOY?nErQ1ZQ;#_^Am$QGx(pezZ7_)9nl-i zWxwdpf%qjnJpVjb-rwkTG4aIN9S1tglAi0mT6Xt~R#@#qGuefUT@J3{2uYc>Su~N4 zJ^R?7<{<4UVK26erI}S+mQPr@Fsx;1!K#HvIf6Hw*%s<1VYAO^rpel0Q~7HwTv%Ti zoofu({&aHJ`M$}F+q_iI1bvZu7JI?U)%lCny#@0o>Euq*m2F(Qm~)!n<(Vfg-Hh|?D~rEo~c{iZg{z4ZZls*UrGL_|&A<#hTd6W83#takVE zqw1Ty7slTfvJ{!^u_aAt?_Hz4%O9Sc$x`JW-Kldq!X;lU`NhkrE=r$_1fLtsyDuPU z5uSATMf}1hnPaLCdP;a6=dSWd^igSBcq*|cNhe>UC%f~5tc`ju*~m*dN(8 z|3HN<<3HI8I*0w$tMyvcG~AJuum8IW!$X3{KN5FVbk*-vv=z+ zU6Nd2cJS(k7?FKjm7c5J`kfIS{85#dCZye-ow-VY7P6QkCp;IE4go;NqEC~(r%+ut>nqeM;Y=ILUIm%-E!ky z=Z$BPlAE_Yv9+_ExZT>UkSwl#ua?tJ*+m(~^vZ7rrg6`lbJT3;%`o zS1)BF&76LigxIHjsV&~V^ZPlE`v0Hb>3baD`g81HwA`|a8BY`Y+AHPWeJq&Y&Tgag zVZ({TM*K4!C0N^;r!)5-@Yool|Ex@9=^SBICZ~rX)|WT6rES~P_54LzcJqPUi$<~j z#(QJWe$eE%YYcQf8+xT`pGsKyl0{w(CpZtV^Libv=}(tYG`KM_toU}%4%dccCq*9t zk5!lI6lSYT5c=Gu!j{o+KtO8Z)u_EnxrS=4310W~rfKaw+Z1xYT5?vFlBQ zS*cCQmOJiRho16dCB4&q zug$c-Ee=X&AI(Udx-n+1snfh&yzcx}O#Oy>A_knU`)gh1`tdAE{gJ!*bkB=-_d4H| zCOcH$P?YH^y<{S|uTy>2VpE$}J1Sx>w--GNc+;`*$Hy(~YcJmX-}25Y_xA1yJ$}2k zh8_1gclbwt&HCAgUuuYZ9e`F(!VhigY|RNmhZMz^Y1sk&yig!z^%CBD#x0; zYdC*hZMf=ZmHeCAM%~v(h<$&O){WU&dl$~AJGU^ucJADxB~kk__kV0`RQMh9;grw` z`&@}#E33~p2`vl>+A=Gn{LQ9atIARojF&|0ySyOUiuq98ZH?b;f!AcFTdh7Lucr5| zjK!|?Jjb1_bHcvu$-S{+f^GGby^E?{*A{Q%-2MByzs>o$^JQPdX7XOijp}_<_*|(d z+<(uujS1Fa&AZ-p-CS!@7W8h}DZZ_`$9G?e-Po*_U^eg1eTIUip3-cM$IY+WT;|!k zvefU&qk7eq<9o{_VnB1{EVOM*)z>baQ ze(){&$+55dM;uFi$B#Oe`j#J#EcHDVicR*8dn68ecT88d=(P|&$S;_D`oV9Re~mHk zl0^&6n(W>GXf(+y|5Iw(-=g~GPd#hA^5>Sn+zvE&cW%zYEqi2we_wKB@JF z?el7Z_pP7nEUvH1lxs2LH~-7!X5z8-|13e?9NVWv1$k&}WmH^wCFoE? z(BqaVL62GeR`xDuk#qNxI5JVZtw2Vktx!hDZHoPaZ|@J@UnaRDY5I9@nY9ZhzNu$K zEf_f$|Ef8}%)pSt&cL8epkQ>ZNKP#%$;{7#l#solvBkGtL~OIIr!CSbeWl{JFf*HL z;Xkjr%Pw5HA@Zo^Lepg_A*aL7HBMa%oT%EbGJTr3T*Wg`@6f<=k67ktN#6ae*J#szRa3s4@BZ*kFc!cC=NZ8%_X!$E!nR} zR5H|XpWfqN0{0HgHhPwN#U;b>jCgCL(1}#m{D;RB@*d?r+^f8JT>|TNl{?ceBzWW% z`WySSef(wnc3+s8>FUV1+huR`xA;yDmw5EVXv6F&t8S+)Pn{TSs&ja^-;6tq7gjx& z2-`d4-LhFv*>yMH zH9Y(2ulG}H-OY}9&c!kBbZjqF-ZswZ_DWipV71BkaBoLdU+P+yE-|4;y4yXyZr4d2 zzWF#)g6&K0*#-A_%yw><*jA!6{eS;5zTj(h#TnaU=1I+2^rGqh<2;+IYP|PWEq^(G zz2&TPe|=W@MQ*k3J2##8;{MAQdv|GU3o8_R9x~_Hk}l&JDJQ;|uei3edb+hz{xn9d zdq=CUJY3Tiy5^$W&TVg{n)3V({nonXYB24*5zou0<4>b+o^3CVZ2aYL)OAYe8II2~ z^QITLA5}f$btB-TqFvC(jR(@SBD;6XF;;HBu;!uAPjxN3qn2$e|Ce%0xP5Px=}_BR zHaj|+XX%R7l`0|~%b6$IT6Y-qJnefHc!| z4L^}_q%~2(a>D-!yRI+lJ|MgN`!%Vuoq~5n+Ou^R1+JJQ`fgX$M(<%HbHUGADGJRYk;nJ~3 z@ZnMJ%l-vL@vkaRq%6OnxH5jymGu*jU)OjbE%H&f_Qd|>+nqf0Sr%1Yl2?50Egm;7 zEO}q}l5fW~qm)uEyxDYLE@8n3W50hlZkxSiPp`=`_25`Jvw~Ooz()CB2R2W9Fw>)8 zk9>`#kk9&Rj(>eAM>5xRZkq3?`9^VXv$lO=$>M+NH$U)b*r@qs?y2^YoVdTc^+gc|llqGS9$+Ui=(R7bPM(Zx^&zo0Q^*i)hRcPEd z$+(~5s71lE`2C7oxEUBiBp4Vp2^0mW+l0fb6N0Zw{-67-F#mG&N?k`~jzk_0?p&7G ziwzmvYsGGT(o*>GXwtoHjV7HZeWT-p&6ZbN+)cQAa87aG+S!j>S{E#8vPuYEUcGbY zPD}aAAoA|BxJ~zWntM9WZu;E+WM1{T?|*9hKi^fqSN%cIB0yV4&Lp|JNxveXNq1?+ z5pmHwPa3(+PtVmqxqt1a(%D`5)fF|}UF$tIMT^#i9?X^NIVdI?Kl%Lar~j@$&Dza= z{GqhmnZxFxJg1&-7pb|;EdMT`VEL4ti+Rc)T7P<<$7+4rK<>N7j_{y6%af+F@7@^e z>ULC4^Ie_h4@Ob`RX0{YP!#3&_LiHt`?t-EmcLqZQ_mj{vC(s_2|von&-Z9T%D;Ir zweeD4TDsIjZI(V_=bAUoEq9{N`4ju)y|&4p+$*pBqqA$i7i+YN*?FxWGaJk^OC&v& z#mvNxBrh{e(Cc`$a>{{^Yo))~*-BhZzU!Esz0Tq1rKjH0%yi#J6c<)IM^AQ0WjxDe zEZJG=t#rzBX4RRPrS6y8y5<*WS6=Q?)t;`Oaq81l^)D%>Jff0%L*o2y^|&;z?%5#e zALN%BA-DbYUbj`Ia@Wp!*to4eA$D=`#M4|muIf3PU-sPjMU2BL-2dy0U%ys3M!u_@ z7;$8d{sWGX^+E-D9pzF?$x+INi*4`jzUNWq;grc?>Kgv?%E9k%mR)|+8Sw4OwTeyF z78j&_mn^=RaDUB%%Doqt#^`OW$-K7o=JcTH>Zvo%ZgXXN%%rRsl|55naWa#hQtBto zYm+u+D&(jvnSbTkOT7t?bd{FQl38cRxzQo?U}3V2oavVvG2GghoAej9_qNr=dKIzh zyj{AhrdQJFg6PC61zL+-UOs)2&Z9i{h}|{c$-lVbws@@iu*N3s3xA{H1O5K|@7d4}`#MnB0i zsr7E3>O{(#R9~BKXiHV)2{m{uuzvO|b&T+11|2q~&@k*to zWu37c-!m)k*#4W+b0>KmJFGMFR>ZoPAgL08e{B+mHX9Hn(oK_wU1Hi@%m1y}Z$nOZ9X9N3Gk>H@vxAAQ~q;qrhak$sfM; z+CTQT*()r1$a^xt@Qyj}^T$mw8T~pLy!A)A&zzeOe{lYfIj__ES{}d5SpGC-#!c`1 z+{4b(rsroxs7;(_%N5|x*!l2evSG>lo+FD_oNvAR_=%txU+3hoxK>}Oa|d@y{nJri zpp%o87jg5}!iV}5-HLqMOiN9!Y};bae)5`l;yu=t>-DDR)^2{)724Uh|50vZ_RN|v z$H-T@vz`C(t6jXmI)BMapY;7V7th}Lr^Dk%Rqu>FoPQ$i>JMKMt5s_xnA&1-#A%ID;Tpev7*pFZC{caBBH{U1fk zUD^2ZFEW3#Dr?(TaVcZd)a7fcYYv>*w<%@!+sM})>#km$-1Ggm@sl&j4YoYmt{-v`RH7o+Z<*%#BIH>fk{bF^9FZU-Ie?85!`Nb(qPhM@E=3Z1PsHgVT>U5oBbjzIfP21+57e4uNb4lugckQcl zpE*{fr3F}VEKfdeFjY`$-$I9_`s*)E4_&!XCb48v)B%U^u!~7nsy|mRS>)CKCF_qy z+}F#`7`=~L%vN2$i0keb*RpSNQc2wNQZvRCGiBWUi9fl z&s6EqniPXF{yo1{0@)sKll3e=#Ifs*=jz92I3uUs-Wa(~Ev4X5-H%(@#ul0O$J*xz z_3@~+#MoVDwuns7es~)<`hoxAv{RaYJ_r74e|kFPw$;D&`a>K3Vle73Qzfi1^;(x4W)AaU6d-aK(;{6}r7J6A6_}?s( zTmDejCh_ParYV!kv>u&FGPm^Guqoo;)fxRPdWrMCd{*fcQT}YfWH9lWR-5+4Pok5 z_uH}}vc>66|E;-U7oT4&dapbjzRdR`M z-nOXT?dcmHzufhyHB>+RMdE{&V!#{yTUbh@-8jf9k=r1uDxp>Z+2S6F|}*L@#XjWH#;xg7nJ66 z!uUjz{^zN3N37gyXP*D8aqRZjbC=nc?=^OQw}Y!(YP-=^zJ*tw-|RnDmREIpTVhb6 zlx=9X%GTzmMqHsQDe8{K&*c`_;!uGn&nh zNLB>5{LrmX+dt#nB3s@=nqnP68v`;HdG?5&P1u6G8SBCKN?lMDv z9l_ot$(3OrmN=JmURBaMCtSEOY{`+IkBSNx-cZpKy58A+^lQ@GPTSUc0*X zk?*xz>XFe)k9;nw>3Vxvd9&R7jms2v?wYq;=JXNMYbha3yFUoodC%la`7p=S)beK5 z{Eg34#b>__-qC1vhqZL+qhn1cPwrFJ-sBi}MkQw#f9YZMvs>Yj_kf-396V&z&3{eAz+d_<5(yD#kS;0V1w~ zilT~5)2G|5oI3NI!L^Nkg|9A~-EA|Nos+gXu}l6eyM2TFsgEkXxm&YO&zN~eZNKgR z&FSZ#9zUOdKT(eF!Bj?fHW^`!7N5BWtCUW1teL%B{L7@MIlua@hFtnOH*5DLr`6@P zvsq7x)@2+hW{y)RI@XdcmAKS*LB~CN4?%;K>p0$It~uthxcFdBXzzJ#tH--y=YIE8 zY_yTP-Vq|8$@F{W3DIi|3nv7wUsZV3=z(hECyxTP*rO>zCxce32r*4`m-~Eit>s_7 z{FVnjznZ-b_FD^`n{9YnoiovPyS=gO;THiRxDZ9yT68b9Dh}9(_yh)uYb*XxqPFSLGS90);w}u;dI(> z$%X3IQF{L$PtbX0%W1VZLf*K3P40Y2zp^v>`LDm;Ouuz7V8d0+5@GsjoLR5Uc_%%VTl z9j|&7&k89Ruz%b<@nBvH&p-VK@nV@`JSmnZm>)znJUVhuNk_H%bb<2S(+m59cJ1ad zH!Wd3CAEtyTWS~Ic9Sn=IZI6X%(|cO-SK^Nqow*}m3{2?2aRt31-6-fnRu;?T`v3O zy0&+XD;J9D)-GE5JL*@6H~*F|1v8FYn5)+4{+ZXDzMmn$n~_O`nTr7g#LiwcM-ao;HN;WZ)6Y#Gp$)P+>1mUFbsPf&Lly%A1L!1LkYW&C(x}eBzyR0g>*(j{ z<{BKL=j(Q?t6w)$W?*1YWME)01?zX{_Rhq(p?d;0q!48Zgw8%QS@gZwywk%1vAu`ICy$zPayAkhTU1k$qwuBRdw zbj~j#BEjJSQU=9{7y;?KgQ71lF*miiATc==$z~34j4XSnTj0jZz_5dhfdO<}3)C=% zC5?PcFxP@?Mwk%jmzWD(WQOc@A!yja3}AsV7(mXM$j-o!UzCnyJ*LNbP;_jD>VQTc zicVoPy97~m-bK@yTTx($q@9BSq&;oZd-nPu28OH^=>F#6fH)OqF<85A1mt)bND+ zL>B|bg`fOzvq7#rHVf62WylE!GsTLdX#R|;IjJ-=Ck1>EB`n`Ua-HM8<#9a>3=B&c zASZOg%vsW?F&o1LFhe{`Qi~Fkaxn59#K>+nkrTR%3=AwxkTFJtk>7C}8IhagSW;4y znN$i}H;M==h_QLy&%d}bGca5OtxAQN337R3_FSAUjmXV$g7_S5Vgudu58hhIhZz_c zco-QNv`|d{w*a5%&KZeCiOD6YMObnTs9cmv{qlYd0|Ub|1_lOD-2^jwNh9YXyyoWT z=BDPApeJrfQ1m)(|FV#Qf#EL$dI&GcYhTU>N&oF@Co~ zjD_B-h#s^a`mu(}j0_9{Oz1%{V+lTEUGkGlDhqHVvfX;i-_B-YV9;S_V9Wh6R7mxX~LSODFr4_Dwb)ioy-x1m}Bml;gB85o-7&|kI3T~3Q+ECXtL z+&6G7vl2?d|?<+KE>8JQRu z`dQG;FZn`}`9|2?zrkb9`T#}-26HBK^N)Tb$$Vq%<{vz0mGhOEf#EMZ1A{v(?7{VN z>JO64hlW*gFenqGf?J#=`8Zk_|1$iQWtbQk=CLp^*r8au{|{-F<`$Gx;cMma9a*4Mb7NAoth!bz|ii6o;NdD z7{T3ISYNTsP#@M>2L+&hL~ah$B2enXj60C&rYdKzdWkSFOw~s(Y~_^*nvU99!EMR< zUo+x@nHdj*=W5zpbqnxE2r}5y`>)7YUmqX$e{pdS{Wtq(cT@JU5U@$=}L*I{#u*`89LCc`A4M|L>Te;9Zg}zA) zVUNgMV(dY92e&=gHY*_P`LYPZ9u)VZm%EV759n)r5$5r(z;7O&fiaLn(N_#243=1f p-(Z}%3fpWX!dT%Q*o;MlE6o3_;3*A120sQ*DF%jRyBHZ57yvh?kt6^B literal 0 HcmV?d00001 diff --git a/moonlight-common/src/com/limelight/nvstream/ControlStream.java b/moonlight-common/src/com/limelight/nvstream/ControlStream.java new file mode 100644 index 00000000..75bde0e4 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/ControlStream.java @@ -0,0 +1,505 @@ +package com.limelight.nvstream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.limelight.nvstream.av.ConnectionStatusListener; + +public class ControlStream implements ConnectionStatusListener { + + public static final int PORT = 47995; + + public static final int CONTROL_TIMEOUT = 5000; + + public static final short PTYPE_HELLO = 0x1204; + public static final short PPAYLEN_HELLO = 0x0004; + public static final byte[] PPAYLOAD_HELLO = + { + (byte)0x00, + (byte)0x05, + (byte)0x00, + (byte)0x00 + }; + + public static final short PTYPE_KEEPALIVE = 0x13ff; + public static final short PPAYLEN_KEEPALIVE = 0x0000; + + public static final short PTYPE_HEARTBEAT = 0x1401; + public static final short PPAYLEN_HEARTBEAT = 0x0000; + + public static final short PTYPE_1405 = 0x1405; + public static final short PPAYLEN_1405 = 0x0000; + + public static final short PTYPE_RESYNC = 0x1404; + public static final short PPAYLEN_RESYNC = 16; + + public static final short PTYPE_CONFIG = 0x1205; + public static final short PPAYLEN_CONFIG = 0x0004; + public static final int[] PPAYLOAD_CONFIG = + { + 720, + 266758, + 1, + 266762, + 30, + 70151, + 68291329, + 1280, + 68291584, + 1280, + 68291840, + 15360, + 68292096, + 25600, + 68292352, + 2048, + 68292608, + 1024, + 68289024, + 262144, + 17957632, + 302055424, + 134217729, + 16777490, + 70153, + 68293120, + 768000, + 17961216, + 303235072, + 335609857, + 838861842, + 352321536, + 1006634002, + 369098752, + 335545362, + 385875968, + 1042, + 402653184, + 134218770, + 419430400, + 167773202, + 436207616, + 855638290, + 266779, + 7000, + 266780, + 2000, + 266781, + 50, + 266782, + 3000, + 266783, + 2, + 266794, + 5000, + 266795, + 500, + 266784, + 75, + 266785, + 25, + 266786, + 10, + 266787, + 60, + 266788, + 30, + 266789, + 3, + 266790, + 1000, + 266791, + 5000, + 266792, + 5000, + 266793, + 5000, + 70190, + 68301063, + 10240, + 68301312, + 6400, + 68301568, + 768000, + 68299776, + 768, + 68300032, + 2560, + 68300544, + 0, + 34746368, + (int)0xFE000000 + }; + + + public static final short PTYPE_JITTER = 0x140c; + public static final short PPAYLEN_JITTER = 0x10; + + private int seqNum; + + private NvConnectionListener listener; + private InetAddress host; + + private Socket s; + private InputStream in; + private OutputStream out; + + private Thread heartbeatThread; + private Thread jitterThread; + private Thread resyncThread; + private Object resyncNeeded = new Object(); + private boolean aborting = false; + + public ControlStream(InetAddress host, NvConnectionListener listener) + { + this.listener = listener; + this.host = host; + } + + public void initialize() throws IOException + { + s = new Socket(); + s.setSoTimeout(CONTROL_TIMEOUT); + s.setTcpNoDelay(true); + s.connect(new InetSocketAddress(host, PORT), CONTROL_TIMEOUT); + in = s.getInputStream(); + out = s.getOutputStream(); + } + + private void sendPacket(NvCtlPacket packet) throws IOException + { + out.write(packet.toWire()); + out.flush(); + } + + private ControlStream.NvCtlResponse sendAndGetReply(NvCtlPacket packet) throws IOException + { + sendPacket(packet); + return new NvCtlResponse(in); + } + + private void sendJitter() throws IOException + { + ByteBuffer bb = ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN); + + bb.putInt(0); + bb.putInt(77); + bb.putInt(888); + bb.putInt(seqNum += 2); + + sendPacket(new NvCtlPacket(PTYPE_JITTER, PPAYLEN_JITTER, bb.array())); + } + + public void abort() + { + if (aborting) { + return; + } + + aborting = true; + + if (jitterThread != null) { + jitterThread.interrupt(); + } + + if (heartbeatThread != null) { + heartbeatThread.interrupt(); + } + + try { + s.close(); + } catch (IOException e) {} + } + + public void requestResync() throws IOException + { + System.out.println("CTL: Requesting IDR frame"); + sendResync(); + } + + public void start() throws IOException + { + sendHello(); + sendConfig(); + pingPong(); + send1405AndGetResponse(); + + heartbeatThread = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + try { + sendHeartbeat(); + } catch (IOException e) { + listener.connectionTerminated(e); + return; + } + + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + } + } + }; + heartbeatThread.start(); + + resyncThread = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + try { + // Wait for notification of a resync needed + synchronized (resyncNeeded) { + resyncNeeded.wait(); + } + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + + try { + requestResync(); + } catch (IOException e) { + listener.connectionTerminated(e); + return; + } + } + } + }; + resyncThread.start(); + } + + public void startJitterPackets() + { + jitterThread = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + try { + sendJitter(); + } catch (IOException e) { + listener.connectionTerminated(e); + return; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + } + } + }; + jitterThread.start(); + } + + private ControlStream.NvCtlResponse send1405AndGetResponse() throws IOException + { + return sendAndGetReply(new NvCtlPacket(PTYPE_1405, PPAYLEN_1405)); + } + + private void sendHello() throws IOException + { + sendPacket(new NvCtlPacket(PTYPE_HELLO, PPAYLEN_HELLO, PPAYLOAD_HELLO)); + } + + private void sendResync() throws IOException + { + ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLEN_RESYNC]).order(ByteOrder.LITTLE_ENDIAN); + + conf.putLong(0); + conf.putLong(0xFFFF); + + sendAndGetReply(new NvCtlPacket(PTYPE_RESYNC, PPAYLEN_RESYNC, conf.array())); + } + + private void sendConfig() throws IOException + { + ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLOAD_CONFIG.length * 4 + 3]).order(ByteOrder.LITTLE_ENDIAN); + + for (int i : PPAYLOAD_CONFIG) + conf.putInt(i); + + conf.putShort((short)0x0013); + conf.put((byte) 0x00); + + sendPacket(new NvCtlPacket(PTYPE_CONFIG, PPAYLEN_CONFIG, conf.array())); + } + + private void sendHeartbeat() throws IOException + { + sendPacket(new NvCtlPacket(PTYPE_HEARTBEAT, PPAYLEN_HEARTBEAT)); + } + + private ControlStream.NvCtlResponse pingPong() throws IOException + { + sendPacket(new NvCtlPacket(PTYPE_KEEPALIVE, PPAYLEN_KEEPALIVE)); + return new ControlStream.NvCtlResponse(in); + } + + class NvCtlPacket { + public short type; + public short paylen; + public byte[] payload; + + public NvCtlPacket(InputStream in) throws IOException + { + byte[] header = new byte[4]; + + int offset = 0; + do + { + int bytesRead = in.read(header, offset, header.length - offset); + if (bytesRead < 0) { + break; + } + offset += bytesRead; + } while (offset != header.length); + + if (offset != header.length) { + throw new IOException("Socket closed prematurely"); + } + + ByteBuffer bb = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN); + + type = bb.getShort(); + paylen = bb.getShort(); + + if (paylen != 0) + { + payload = new byte[paylen]; + + offset = 0; + do + { + int bytesRead = in.read(payload, offset, payload.length - offset); + if (bytesRead < 0) { + break; + } + offset += bytesRead; + } while (offset != payload.length); + + if (offset != payload.length) { + throw new IOException("Socket closed prematurely"); + } + } + } + + public NvCtlPacket(byte[] payload) + { + ByteBuffer bb = ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN); + + type = bb.getShort(); + paylen = bb.getShort(); + + if (bb.hasRemaining()) + { + payload = new byte[bb.remaining()]; + bb.get(payload); + } + } + + public NvCtlPacket(short type, short paylen) + { + this.type = type; + this.paylen = paylen; + } + + public NvCtlPacket(short type, short paylen, byte[] payload) + { + this.type = type; + this.paylen = paylen; + this.payload = payload; + } + + public short getType() + { + return type; + } + + public short getPaylen() + { + return paylen; + } + + public void setType(short type) + { + this.type = type; + } + + public void setPaylen(short paylen) + { + this.paylen = paylen; + } + + public byte[] toWire() + { + ByteBuffer bb = ByteBuffer.allocate(4 + (payload != null ? payload.length : 0)).order(ByteOrder.LITTLE_ENDIAN); + + bb.putShort(type); + bb.putShort(paylen); + + if (payload != null) + bb.put(payload); + + return bb.array(); + } + } + + class NvCtlResponse extends NvCtlPacket { + public short status; + + public NvCtlResponse(InputStream in) throws IOException { + super(in); + } + + public NvCtlResponse(short type, short paylen) { + super(type, paylen); + } + + public NvCtlResponse(short type, short paylen, byte[] payload) { + super(type, paylen, payload); + } + + public NvCtlResponse(byte[] payload) { + super(payload); + } + + public void setStatusCode(short status) + { + this.status = status; + } + + public short getStatusCode() + { + return status; + } + } + + @Override + public void connectionTerminated() { + abort(); + } + + @Override + public void connectionNeedsResync() { + synchronized (resyncNeeded) { + // Wake up the resync thread + resyncNeeded.notify(); + } + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/Handshake.java b/moonlight-common/src/com/limelight/nvstream/Handshake.java new file mode 100644 index 00000000..e3249af9 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/Handshake.java @@ -0,0 +1,133 @@ +package com.limelight.nvstream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class Handshake { + public static final int PORT = 47991; + + public static final int HANDSHAKE_TIMEOUT = 5000; + + public static final byte[] PLATFORM_HELLO = + { + (byte)0x07, + (byte)0x00, + (byte)0x00, + (byte)0x00, + + // android in ASCII + (byte)0x61, + (byte)0x6e, + (byte)0x64, + (byte)0x72, + (byte)0x6f, + (byte)0x69, + (byte)0x64, + + (byte)0x03, + (byte)0x01, + (byte)0x00, + (byte)0x00 + }; + + public static final byte[] PACKET_2 = + { + (byte)0x01, + (byte)0x03, + (byte)0x02, + (byte)0x00, + (byte)0x08, + (byte)0x00 + }; + + public static final byte[] PACKET_3 = + { + (byte)0x04, + (byte)0x01, + (byte)0x00, + (byte)0x00, + + (byte)0x00, + (byte)0x00, + (byte)0x00, + (byte)0x00 + }; + + public static final byte[] PACKET_4 = + { + (byte)0x01, + (byte)0x01, + (byte)0x00, + (byte)0x00 + }; + + private static boolean waitAndDiscardResponse(InputStream in) + { + // Wait for response and discard response + try { + in.read(); + + // Wait for the full response to come in + Thread.sleep(250); + + for (int i = 0; i < in.available(); i++) + in.read(); + + } catch (IOException e1) { + return false; + } catch (InterruptedException e) { + return false; + } + + return true; + } + + public static boolean performHandshake(InetAddress host) throws IOException + { + Socket s = new Socket(); + s.connect(new InetSocketAddress(host, PORT), HANDSHAKE_TIMEOUT); + s.setSoTimeout(HANDSHAKE_TIMEOUT); + OutputStream out = s.getOutputStream(); + InputStream in = s.getInputStream(); + + // First packet + out.write(PLATFORM_HELLO); + out.flush(); + + if (!waitAndDiscardResponse(in)) { + s.close(); + return false; + } + + // Second packet + out.write(PACKET_2); + out.flush(); + + if (!waitAndDiscardResponse(in)) { + s.close(); + return false; + } + + // Third packet + out.write(PACKET_3); + out.flush(); + + if (!waitAndDiscardResponse(in)) { + s.close(); + return false; + } + + // Fourth packet + out.write(PACKET_4); + out.flush(); + + // Done + s.close(); + + return true; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnection.java b/moonlight-common/src/com/limelight/nvstream/NvConnection.java new file mode 100644 index 00000000..54ed4f3b --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/NvConnection.java @@ -0,0 +1,321 @@ +package com.limelight.nvstream; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.xmlpull.v1.XmlPullParserException; + +import com.limelight.nvstream.av.audio.AudioStream; +import com.limelight.nvstream.av.audio.AudioRenderer; +import com.limelight.nvstream.av.video.VideoDecoderRenderer; +import com.limelight.nvstream.av.video.VideoStream; +import com.limelight.nvstream.http.NvHTTP; +import com.limelight.nvstream.input.NvController; + +public class NvConnection { + private String host; + private NvConnectionListener listener; + + private InetAddress hostAddr; + private ControlStream controlStream; + private NvController inputStream; + private VideoStream videoStream; + private AudioStream audioStream; + + // Start parameters + private int drFlags; + private Object videoRenderTarget; + private VideoDecoderRenderer videoDecoderRenderer; + private AudioRenderer audioRenderer; + private String localDeviceName; + + private ThreadPoolExecutor threadPool; + + public NvConnection(String host, NvConnectionListener listener) + { + this.host = host; + this.listener = listener; + this.threadPool = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue()); + } + + public static String getMacAddressString() throws SocketException { + Enumeration ifaceList; + NetworkInterface selectedIface = null; + + // First look for a WLAN interface (since those generally aren't removable) + ifaceList = NetworkInterface.getNetworkInterfaces(); + while (selectedIface == null && ifaceList.hasMoreElements()) { + NetworkInterface iface = ifaceList.nextElement(); + + if (iface.getName().startsWith("wlan") && + iface.getHardwareAddress() != null) { + selectedIface = iface; + } + } + + // If we didn't find that, look for an Ethernet interface + ifaceList = NetworkInterface.getNetworkInterfaces(); + while (selectedIface == null && ifaceList.hasMoreElements()) { + NetworkInterface iface = ifaceList.nextElement(); + + if (iface.getName().startsWith("eth") && + iface.getHardwareAddress() != null) { + selectedIface = iface; + } + } + + // Now just find something with a MAC address + ifaceList = NetworkInterface.getNetworkInterfaces(); + while (selectedIface == null && ifaceList.hasMoreElements()) { + NetworkInterface iface = ifaceList.nextElement(); + + if (iface.getHardwareAddress() != null) { + selectedIface = ifaceList.nextElement(); + break; + } + } + + if (selectedIface == null) { + return null; + } + + byte[] macAddress = selectedIface.getHardwareAddress(); + if (macAddress != null) { + StringBuilder addrStr = new StringBuilder(); + for (int i = 0; i < macAddress.length; i++) { + addrStr.append(String.format("%02x", macAddress[i])); + if (i != macAddress.length - 1) { + addrStr.append(':'); + } + } + return addrStr.toString(); + } + + return null; + } + + public void stop() + { + threadPool.shutdownNow(); + + if (videoStream != null) { + videoStream.abort(); + } + if (audioStream != null) { + audioStream.abort(); + } + + if (controlStream != null) { + controlStream.abort(); + } + + if (inputStream != null) { + inputStream.close(); + inputStream = null; + } + } + + private boolean startSteamBigPicture() throws XmlPullParserException, IOException + { + NvHTTP h = new NvHTTP(hostAddr, getMacAddressString(), localDeviceName); + + if (!h.getPairState()) { + listener.displayMessage("Device not paired with computer"); + return false; + } + + int sessionId = h.getSessionId(); + int appId = h.getSteamAppId(sessionId); + + h.launchApp(sessionId, appId); + + return true; + } + + private boolean startControlStream() throws IOException + { + controlStream = new ControlStream(hostAddr, listener); + controlStream.initialize(); + controlStream.start(); + return true; + } + + private boolean startVideoStream() throws IOException + { + videoStream = new VideoStream(hostAddr, listener, controlStream); + videoStream.startVideoStream(videoDecoderRenderer, videoRenderTarget, drFlags); + return true; + } + + private boolean startAudioStream() throws IOException + { + audioStream = new AudioStream(hostAddr, listener, audioRenderer); + audioStream.startAudioStream(); + return true; + } + + private boolean startInputConnection() throws IOException + { + inputStream = new NvController(hostAddr); + inputStream.initialize(); + return true; + } + + private void establishConnection() { + for (NvConnectionListener.Stage currentStage : NvConnectionListener.Stage.values()) + { + boolean success = false; + + listener.stageStarting(currentStage); + try { + switch (currentStage) + { + case LAUNCH_APP: + success = startSteamBigPicture(); + break; + + case HANDSHAKE: + success = Handshake.performHandshake(hostAddr); + break; + + case CONTROL_START: + success = startControlStream(); + break; + + case VIDEO_START: + success = startVideoStream(); + break; + + case AUDIO_START: + success = startAudioStream(); + break; + + case CONTROL_START2: + controlStream.startJitterPackets(); + success = true; + break; + + case INPUT_START: + success = startInputConnection(); + break; + } + } catch (Exception e) { + e.printStackTrace(); + success = false; + } + + if (success) { + listener.stageComplete(currentStage); + } + else { + listener.stageFailed(currentStage); + return; + } + } + + listener.connectionStarted(); + } + + public void start(String localDeviceName, Object videoRenderTarget, int drFlags, AudioRenderer audioRenderer, VideoDecoderRenderer videoDecoderRenderer) + { + this.localDeviceName = localDeviceName; + this.drFlags = drFlags; + this.audioRenderer = audioRenderer; + this.videoRenderTarget = videoRenderTarget; + this.videoDecoderRenderer = videoDecoderRenderer; + + new Thread(new Runnable() { + @Override + public void run() { + try { + hostAddr = InetAddress.getByName(host); + } catch (UnknownHostException e) { + listener.connectionTerminated(e); + return; + } + + establishConnection(); + } + }).start(); + } + + public void sendMouseMove(final short deltaX, final short deltaY) + { + if (inputStream == null) + return; + + threadPool.execute(new Runnable() { + @Override + public void run() { + try { + inputStream.sendMouseMove(deltaX, deltaY); + } catch (IOException e) { + listener.connectionTerminated(e); + } + } + }); + } + + public void sendMouseButtonDown() + { + if (inputStream == null) + return; + + threadPool.execute(new Runnable() { + @Override + public void run() { + try { + inputStream.sendMouseButtonDown(); + } catch (IOException e) { + listener.connectionTerminated(e); + } + } + }); + } + + public void sendMouseButtonUp() + { + if (inputStream == null) + return; + + threadPool.execute(new Runnable() { + @Override + public void run() { + try { + inputStream.sendMouseButtonUp(); + } catch (IOException e) { + listener.connectionTerminated(e); + } + } + }); + } + + public void sendControllerInput(final short buttonFlags, + final byte leftTrigger, final byte rightTrigger, + final short leftStickX, final short leftStickY, + final short rightStickX, final short rightStickY) + { + if (inputStream == null) + return; + + threadPool.execute(new Runnable() { + @Override + public void run() { + try { + inputStream.sendControllerInput(buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY); + } catch (IOException e) { + listener.connectionTerminated(e); + } + } + }); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java b/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java new file mode 100644 index 00000000..0fb63dc2 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java @@ -0,0 +1,32 @@ +package com.limelight.nvstream; + +public interface NvConnectionListener { + + public enum Stage { + LAUNCH_APP("app"), + HANDSHAKE("handshake"), + CONTROL_START("control connection"), + VIDEO_START("video stream"), + AUDIO_START("audio stream"), + CONTROL_START2("control connection"), + INPUT_START("input connection"); + + private String name; + private Stage(String name) { + this.name = name; + } + + public String getName() { + return name; + } + }; + + public void stageStarting(Stage stage); + public void stageComplete(Stage stage); + public void stageFailed(Stage stage); + + public void connectionStarted(); + public void connectionTerminated(Exception e); + + public void displayMessage(String message); +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/ByteBufferDescriptor.java b/moonlight-common/src/com/limelight/nvstream/av/ByteBufferDescriptor.java new file mode 100644 index 00000000..83fa0b46 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/ByteBufferDescriptor.java @@ -0,0 +1,46 @@ +package com.limelight.nvstream.av; + +public class ByteBufferDescriptor { + public byte[] data; + public int offset; + public int length; + + public ByteBufferDescriptor(byte[] data, int offset, int length) + { + this.data = data; + this.offset = offset; + this.length = length; + } + + public ByteBufferDescriptor(ByteBufferDescriptor desc) + { + this.data = desc.data; + this.offset = desc.offset; + this.length = desc.length; + } + + public void reinitialize(byte[] data, int offset, int length) + { + this.data = data; + this.offset = offset; + this.length = length; + } + + public void print() + { + print(offset, length); + } + + public void print(int length) + { + print(this.offset, length); + } + + public void print(int offset, int length) + { + for (int i = offset; i < offset+length; i++) { + System.out.printf("%d: %02x \n", i, data[i]); + } + System.out.println(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java b/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java new file mode 100644 index 00000000..35262ddf --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java @@ -0,0 +1,7 @@ +package com.limelight.nvstream.av; + +public interface ConnectionStatusListener { + public void connectionTerminated(); + + public void connectionNeedsResync(); +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/DecodeUnit.java b/moonlight-common/src/com/limelight/nvstream/av/DecodeUnit.java new file mode 100644 index 00000000..f58510e0 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/DecodeUnit.java @@ -0,0 +1,42 @@ +package com.limelight.nvstream.av; + +import java.util.List; + +public class DecodeUnit { + public static final int TYPE_UNKNOWN = 0; + public static final int TYPE_H264 = 1; + public static final int TYPE_OPUS = 2; + + private int type; + private List bufferList; + private int dataLength; + private int flags; + + public DecodeUnit(int type, List bufferList, int dataLength, int flags) + { + this.type = type; + this.bufferList = bufferList; + this.dataLength = dataLength; + this.flags = flags; + } + + public int getType() + { + return type; + } + + public int getFlags() + { + return flags; + } + + public List getBufferList() + { + return bufferList; + } + + public int getDataLength() + { + return dataLength; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/RtpPacket.java b/moonlight-common/src/com/limelight/nvstream/av/RtpPacket.java new file mode 100644 index 00000000..d1fc429e --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/RtpPacket.java @@ -0,0 +1,46 @@ +package com.limelight.nvstream.av; + +import java.nio.ByteBuffer; + +public class RtpPacket { + + private byte packetType; + private short seqNum; + private ByteBufferDescriptor buffer; + + public RtpPacket(ByteBufferDescriptor buffer) + { + this.buffer = new ByteBufferDescriptor(buffer); + + ByteBuffer bb = ByteBuffer.wrap(buffer.data, buffer.offset, buffer.length); + + // Discard the first byte + bb.position(bb.position()+1); + + // Get the packet type + packetType = bb.get(); + + // Get the sequence number + seqNum = bb.getShort(); + } + + public byte getPacketType() + { + return packetType; + } + + public short getSequenceNumber() + { + return seqNum; + } + + public byte[] getBackingBuffer() + { + return buffer.data; + } + + public ByteBufferDescriptor getNewPayloadDescriptor() + { + return new ByteBufferDescriptor(buffer.data, buffer.offset+12, buffer.length-12); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/ShortBufferDescriptor.java b/moonlight-common/src/com/limelight/nvstream/av/ShortBufferDescriptor.java new file mode 100644 index 00000000..44cd2d8e --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/ShortBufferDescriptor.java @@ -0,0 +1,28 @@ +package com.limelight.nvstream.av; + +public class ShortBufferDescriptor { + public short[] data; + public int offset; + public int length; + + public ShortBufferDescriptor(short[] data, int offset, int length) + { + this.data = data; + this.offset = offset; + this.length = length; + } + + public ShortBufferDescriptor(ShortBufferDescriptor desc) + { + this.data = desc.data; + this.offset = desc.offset; + this.length = desc.length; + } + + public void reinitialize(short[] data, int offset, int length) + { + this.data = data; + this.offset = offset; + this.length = length; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java new file mode 100644 index 00000000..c8fd728d --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioDepacketizer.java @@ -0,0 +1,65 @@ +package com.limelight.nvstream.av.audio; + +import java.util.concurrent.LinkedBlockingQueue; + +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.RtpPacket; +import com.limelight.nvstream.av.ShortBufferDescriptor; + +public class AudioDepacketizer { + + private static final int DU_LIMIT = 15; + private LinkedBlockingQueue decodedUnits = + new LinkedBlockingQueue(DU_LIMIT); + + // Sequencing state + private short lastSequenceNumber; + + private void decodeData(byte[] data, int off, int len) + { + // Submit this data to the decoder + short[] pcmData = new short[OpusDecoder.getMaxOutputShorts()]; + int decodeLen = OpusDecoder.decode(data, off, len, pcmData); + + if (decodeLen > 0) { + // Return value of decode is frames decoded per channel + decodeLen *= OpusDecoder.getChannelCount(); + + // Put it on the decoded queue + if (!decodedUnits.offer(new ShortBufferDescriptor(pcmData, 0, decodeLen))) { + // Clear out the queue + decodedUnits.clear(); + } + } + } + + public void decodeInputData(RtpPacket packet) + { + short seq = packet.getSequenceNumber(); + + if (packet.getPacketType() != 97) { + // Only type 97 is audio + return; + } + + // Toss out the current NAL if we receive a packet that is + // out of sequence + if (lastSequenceNumber != 0 && + (short)(lastSequenceNumber + 1) != seq) + { + System.out.println("Received OOS audio data (expected "+(lastSequenceNumber + 1)+", got "+seq+")"); + decodeData(null, 0, 0); + } + + lastSequenceNumber = seq; + + // This is all the depacketizing we need to do + ByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); + decodeData(rtpPayload.data, rtpPayload.offset, rtpPayload.length); + } + + public ShortBufferDescriptor getNextDecodedData() throws InterruptedException + { + return decodedUnits.take(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java new file mode 100644 index 00000000..ebffda58 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioRenderer.java @@ -0,0 +1,9 @@ +package com.limelight.nvstream.av.audio; + +public interface AudioRenderer { + public void streamInitialized(int channelCount, int sampleRate); + + public void playDecodedAudio(short[] audioData, int offset, int length); + + public void streamClosing(); +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java new file mode 100644 index 00000000..19a2a1cb --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/AudioStream.java @@ -0,0 +1,223 @@ +package com.limelight.nvstream.av.audio; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.util.LinkedList; +import java.util.concurrent.LinkedBlockingQueue; + +import com.limelight.nvstream.NvConnectionListener; +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.RtpPacket; +import com.limelight.nvstream.av.ShortBufferDescriptor; + +public class AudioStream { + public static final int RTP_PORT = 48000; + public static final int RTCP_PORT = 47999; + + private LinkedBlockingQueue packets = new LinkedBlockingQueue(100); + + private DatagramSocket rtp; + + private AudioDepacketizer depacketizer = new AudioDepacketizer(); + + private LinkedList threads = new LinkedList(); + + private boolean aborting = false; + + private InetAddress host; + private NvConnectionListener connListener; + private AudioRenderer streamListener; + + public AudioStream(InetAddress host, NvConnectionListener connListener, AudioRenderer streamListener) + { + this.host = host; + this.connListener = connListener; + this.streamListener = streamListener; + } + + public void abort() + { + if (aborting) { + return; + } + + aborting = true; + + for (Thread t : threads) { + t.interrupt(); + } + + // Close the socket to interrupt the receive thread + if (rtp != null) { + rtp.close(); + } + + // Wait for threads to terminate + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException e) { } + } + + streamListener.streamClosing(); + + threads.clear(); + } + + public void startAudioStream() throws SocketException + { + setupRtpSession(); + + setupAudio(); + + startReceiveThread(); + + startDepacketizerThread(); + + startDecoderThread(); + + startUdpPingThread(); + } + + private void setupRtpSession() throws SocketException + { + rtp = new DatagramSocket(RTP_PORT); + } + + private void setupAudio() + { + int err; + + err = OpusDecoder.init(); + if (err != 0) { + throw new IllegalStateException("Opus decoder failed to initialize"); + } + + streamListener.streamInitialized(OpusDecoder.getChannelCount(), OpusDecoder.getSampleRate()); + } + + private void startDepacketizerThread() + { + // This thread lessens the work on the receive thread + // so it can spend more time waiting for data + Thread t = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + RtpPacket packet; + + try { + packet = packets.take(); + } catch (InterruptedException e) { + connListener.connectionTerminated(e); + return; + } + + depacketizer.decodeInputData(packet); + } + } + }; + threads.add(t); + t.setName("Audio - Depacketizer"); + t.start(); + } + + private void startDecoderThread() + { + // Decoder thread + Thread t = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + ShortBufferDescriptor samples; + + try { + samples = depacketizer.getNextDecodedData(); + } catch (InterruptedException e) { + connListener.connectionTerminated(e); + return; + } + + streamListener.playDecodedAudio(samples.data, samples.offset, samples.length); + } + } + }; + threads.add(t); + t.setName("Audio - Player"); + t.start(); + } + + private void startReceiveThread() + { + // Receive thread + Thread t = new Thread() { + @Override + public void run() { + ByteBufferDescriptor desc = new ByteBufferDescriptor(new byte[1500], 0, 1500); + DatagramPacket packet = new DatagramPacket(desc.data, desc.length); + + while (!isInterrupted()) + { + try { + rtp.receive(packet); + } catch (IOException e) { + connListener.connectionTerminated(e); + return; + } + + // Give the packet to the depacketizer thread + desc.length = packet.getLength(); + if (packets.offer(new RtpPacket(desc))) { + desc.reinitialize(new byte[1500], 0, 1500); + packet.setData(desc.data, desc.offset, desc.length); + } + } + } + }; + threads.add(t); + t.setName("Audio - Receive"); + t.start(); + } + + private void startUdpPingThread() + { + // Ping thread + Thread t = new Thread() { + @Override + public void run() { + // PING in ASCII + final byte[] pingPacketData = new byte[] {0x50, 0x49, 0x4E, 0x47}; + DatagramPacket pingPacket = new DatagramPacket(pingPacketData, pingPacketData.length); + pingPacket.setSocketAddress(new InetSocketAddress(host, RTP_PORT)); + + // Send PING every 100 ms + while (!isInterrupted()) + { + try { + rtp.send(pingPacket); + } catch (IOException e) { + connListener.connectionTerminated(e); + return; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + connListener.connectionTerminated(e); + return; + } + } + } + }; + threads.add(t); + t.setPriority(Thread.MIN_PRIORITY); + t.setName("Audio - Ping"); + t.start(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java b/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java new file mode 100644 index 00000000..c01f7fa5 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/audio/OpusDecoder.java @@ -0,0 +1,14 @@ +package com.limelight.nvstream.av.audio; + +public class OpusDecoder { + static { + System.loadLibrary("nv_opus_dec"); + } + + public static native int init(); + public static native void destroy(); + public static native int getChannelCount(); + public static native int getMaxOutputShorts(); + public static native int getSampleRate(); + public static native int decode(byte[] indata, int inoff, int inlen, short[] outpcmdata); +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDecoderRenderer.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDecoderRenderer.java new file mode 100644 index 00000000..733d23fd --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDecoderRenderer.java @@ -0,0 +1,17 @@ +package com.limelight.nvstream.av.video; + +import com.limelight.nvstream.av.DecodeUnit; + +public interface VideoDecoderRenderer { + public static int FLAG_PREFER_QUALITY = 0x1; + + public void setup(int width, int height, Object renderTarget, int drFlags); + + public void start(); + + public void stop(); + + public void release(); + + public boolean submitDecodeUnit(DecodeUnit decodeUnit); +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java new file mode 100644 index 00000000..d1936a34 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java @@ -0,0 +1,213 @@ +package com.limelight.nvstream.av.video; + +import java.util.LinkedList; +import java.util.concurrent.LinkedBlockingQueue; + +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.DecodeUnit; +import com.limelight.nvstream.av.RtpPacket; +import com.limelight.nvstream.av.ConnectionStatusListener; + +public class VideoDepacketizer { + + // Current NAL state + private LinkedList avcNalDataChain = null; + private int avcNalDataLength = 0; + + // Sequencing state + private short lastSequenceNumber; + + private ConnectionStatusListener controlListener; + + private static final int DU_LIMIT = 15; + private LinkedBlockingQueue decodedUnits = new LinkedBlockingQueue(DU_LIMIT); + + public VideoDepacketizer(ConnectionStatusListener controlListener) + { + this.controlListener = controlListener; + } + + private void clearAvcNalState() + { + avcNalDataChain = null; + avcNalDataLength = 0; + } + + private void reassembleAvcNal() + { + // This is the start of a new NAL + if (avcNalDataChain != null && avcNalDataLength != 0) { + // Construct the H264 decode unit + DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, 0); + if (!decodedUnits.offer(du)) { + // We need a new IDR frame since we're discarding data now + decodedUnits.clear(); + controlListener.connectionNeedsResync(); + } + + // Clear old state + clearAvcNalState(); + } + } + + public void addInputData(VideoPacket packet) + { + ByteBufferDescriptor location = packet.getNewPayloadDescriptor(); + + // SPS and PPS packet doesn't have standard headers, so submit it as is + if (location.length < 968) { + avcNalDataChain = new LinkedList(); + avcNalDataLength = 0; + + avcNalDataChain.add(location); + avcNalDataLength += location.length; + + reassembleAvcNal(); + } + else { + int packetIndex = packet.getPacketIndex(); + int packetsInFrame = packet.getTotalPackets(); + + // Check if this is the first packet for a frame + if (packetIndex == 0) { + // Setup state for the new frame + avcNalDataChain = new LinkedList(); + avcNalDataLength = 0; + } + + // Check if this packet falls in the range of packets in frame + if (packetIndex >= packetsInFrame) { + // This isn't H264 frame data + return; + } + + // Adjust the length to only contain valid data + location.length = packet.getPayloadLength(); + + // Add the payload data to the chain + if (avcNalDataChain != null) { + avcNalDataChain.add(location); + avcNalDataLength += location.length; + } + + // Reassemble the NALs if this was the last packet for this frame + if (packetIndex + 1 == packetsInFrame) { + reassembleAvcNal(); + } + } + } + + public void addInputData(RtpPacket packet) + { + short seq = packet.getSequenceNumber(); + + // Toss out the current NAL if we receive a packet that is + // out of sequence + if (lastSequenceNumber != 0 && + (short)(lastSequenceNumber + 1) != seq) + { + System.out.println("Received OOS video data (expected "+(lastSequenceNumber + 1)+", got "+seq+")"); + + // Reset the depacketizer state + clearAvcNalState(); + + // Request an IDR frame + controlListener.connectionNeedsResync(); + } + + lastSequenceNumber = seq; + + // Pass the payload to the non-sequencing parser + ByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); + addInputData(new VideoPacket(rtpPayload)); + } + + public DecodeUnit getNextDecodeUnit() throws InterruptedException + { + return decodedUnits.take(); + } +} + +class NAL { + + // This assumes that the buffer passed in is already a special sequence + public static boolean isAvcStartSequence(ByteBufferDescriptor specialSeq) + { + // The start sequence is 00 00 01 or 00 00 00 01 + return (specialSeq.data[specialSeq.offset+specialSeq.length-1] == 0x01); + } + + // This assumes that the buffer passed in is already a special sequence + public static boolean isAvcFrameStart(ByteBufferDescriptor specialSeq) + { + if (specialSeq.length != 4) + return false; + + // The frame start sequence is 00 00 00 01 + return (specialSeq.data[specialSeq.offset+specialSeq.length-1] == 0x01); + } + + // Returns a buffer descriptor describing the start sequence + public static boolean getSpecialSequenceDescriptor(ByteBufferDescriptor buffer, ByteBufferDescriptor outputDesc) + { + // NAL start sequence is 00 00 00 01 or 00 00 01 + if (buffer.length < 3) + return false; + + // 00 00 is magic + if (buffer.data[buffer.offset] == 0x00 && + buffer.data[buffer.offset+1] == 0x00) + { + // Another 00 could be the end of the special sequence + // 00 00 00 or the middle of 00 00 00 01 + if (buffer.data[buffer.offset+2] == 0x00) + { + if (buffer.length >= 4 && + buffer.data[buffer.offset+3] == 0x01) + { + // It's the AVC start sequence 00 00 00 01 + outputDesc.reinitialize(buffer.data, buffer.offset, 4); + } + else + { + // It's 00 00 00 + outputDesc.reinitialize(buffer.data, buffer.offset, 3); + } + return true; + } + else if (buffer.data[buffer.offset+2] == 0x01 || + buffer.data[buffer.offset+2] == 0x02) + { + // These are easy: 00 00 01 or 00 00 02 + outputDesc.reinitialize(buffer.data, buffer.offset, 3); + return true; + } + else if (buffer.data[buffer.offset+2] == 0x03) + { + // 00 00 03 is special because it's a subsequence of the + // NAL wrapping substitute for 00 00 00, 00 00 01, 00 00 02, + // or 00 00 03 in the RBSP sequence. We need to check the next + // byte to see whether it's 00, 01, 02, or 03 (a valid RBSP substitution) + // or whether it's something else + + if (buffer.length < 4) + return false; + + if (buffer.data[buffer.offset+3] >= 0x00 && + buffer.data[buffer.offset+3] <= 0x03) + { + // It's not really a special sequence after all + return false; + } + else + { + // It's not a standard replacement so it's a special sequence + outputDesc.reinitialize(buffer.data, buffer.offset, 3); + return true; + } + } + } + + return false; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoPacket.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoPacket.java new file mode 100644 index 00000000..a74da573 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoPacket.java @@ -0,0 +1,56 @@ +package com.limelight.nvstream.av.video; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.limelight.nvstream.av.ByteBufferDescriptor; + +public class VideoPacket { + private ByteBufferDescriptor buffer; + + private int frameIndex; + private int packetIndex; + private int totalPackets; + private int payloadLength; + + public VideoPacket(ByteBufferDescriptor rtpPayload) + { + buffer = new ByteBufferDescriptor(rtpPayload); + + ByteBuffer bb = ByteBuffer.wrap(buffer.data).order(ByteOrder.LITTLE_ENDIAN); + bb.position(buffer.offset); + + frameIndex = bb.getInt(); + packetIndex = bb.getInt(); + totalPackets = bb.getInt(); + + bb.position(bb.position()+4); + + payloadLength = bb.getInt(); + } + + public int getFrameIndex() + { + return frameIndex; + } + + public int getPacketIndex() + { + return packetIndex; + } + + public int getPayloadLength() + { + return payloadLength; + } + + public int getTotalPackets() + { + return totalPackets; + } + + public ByteBufferDescriptor getNewPayloadDescriptor() + { + return new ByteBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java new file mode 100644 index 00000000..0abd3bd1 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoStream.java @@ -0,0 +1,290 @@ +package com.limelight.nvstream.av.video; + +import java.io.IOException; +import java.io.InputStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.util.LinkedList; +import java.util.concurrent.LinkedBlockingQueue; + +import com.limelight.nvstream.NvConnectionListener; +import com.limelight.nvstream.av.ByteBufferDescriptor; +import com.limelight.nvstream.av.DecodeUnit; +import com.limelight.nvstream.av.RtpPacket; +import com.limelight.nvstream.av.ConnectionStatusListener; + +public class VideoStream { + public static final int RTP_PORT = 47998; + public static final int RTCP_PORT = 47999; + public static final int FIRST_FRAME_PORT = 47996; + + public static final int FIRST_FRAME_TIMEOUT = 5000; + + private LinkedBlockingQueue packets = new LinkedBlockingQueue(100); + + private InetAddress host; + private DatagramSocket rtp; + private Socket firstFrameSocket; + + private LinkedList threads = new LinkedList(); + + private NvConnectionListener listener; + private VideoDepacketizer depacketizer; + + private VideoDecoderRenderer decRend; + private boolean startedRendering; + + private boolean aborting = false; + + public VideoStream(InetAddress host, NvConnectionListener listener, ConnectionStatusListener avConnListener) + { + this.host = host; + this.listener = listener; + this.depacketizer = new VideoDepacketizer(avConnListener); + } + + public void abort() + { + if (aborting) { + return; + } + + aborting = true; + + // Interrupt threads + for (Thread t : threads) { + t.interrupt(); + } + + // Close the socket to interrupt the receive thread + if (rtp != null) { + rtp.close(); + } + if (firstFrameSocket != null) { + try { + firstFrameSocket.close(); + } catch (IOException e) {} + } + + // Wait for threads to terminate + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException e) { } + } + + if (startedRendering) { + decRend.stop(); + } + + if (decRend != null) { + decRend.release(); + } + + threads.clear(); + } + + private void readFirstFrame() throws IOException + { + byte[] firstFrame = new byte[1500]; + + firstFrameSocket = new Socket(); + firstFrameSocket.setSoTimeout(FIRST_FRAME_TIMEOUT); + + try { + firstFrameSocket.connect(new InetSocketAddress(host, FIRST_FRAME_PORT), FIRST_FRAME_TIMEOUT); + InputStream firstFrameStream = firstFrameSocket.getInputStream(); + + int offset = 0; + for (;;) + { + int bytesRead = firstFrameStream.read(firstFrame, offset, firstFrame.length-offset); + + if (bytesRead == -1) + break; + + offset += bytesRead; + } + + depacketizer.addInputData(new VideoPacket(new ByteBufferDescriptor(firstFrame, 0, offset))); + } finally { + firstFrameSocket.close(); + firstFrameSocket = null; + } + } + + public void setupRtpSession() throws SocketException + { + rtp = new DatagramSocket(RTP_PORT); + } + + public void setupDecoderRenderer(VideoDecoderRenderer decRend, Object renderTarget, int drFlags) { + this.decRend = decRend; + if (decRend != null) { + decRend.setup(1280, 720, renderTarget, drFlags); + } + } + + public void startVideoStream(VideoDecoderRenderer decRend, Object renderTarget, int drFlags) throws IOException + { + // Setup the decoder and renderer + setupDecoderRenderer(decRend, renderTarget, drFlags); + + // Open RTP sockets and start session + setupRtpSession(); + + // Start pinging before reading the first frame + // so Shield Proxy knows we're here and sends us + // the reference frame + startUdpPingThread(); + + // Read the first frame to start the UDP video stream + // This MUST be called before the normal UDP receive thread + // starts in order to avoid state corruption caused by two + // threads simultaneously adding input data. + readFirstFrame(); + + if (decRend != null) { + // Start the receive thread early to avoid missing + // early packets + startReceiveThread(); + + // Start the depacketizer thread to deal with the RTP data + startDepacketizerThread(); + + // Start decoding the data we're receiving + startDecoderThread(); + + // Start the renderer + decRend.start(); + startedRendering = true; + } + } + + private void startDecoderThread() + { + Thread t = new Thread() { + @Override + public void run() { + // Read the decode units generated from the RTP stream + while (!isInterrupted()) + { + DecodeUnit du; + + try { + du = depacketizer.getNextDecodeUnit(); + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + + decRend.submitDecodeUnit(du); + } + } + }; + threads.add(t); + t.setName("Video - Decoder"); + t.setPriority(Thread.MAX_PRIORITY); + t.start(); + } + + private void startDepacketizerThread() + { + // This thread lessens the work on the receive thread + // so it can spend more time waiting for data + Thread t = new Thread() { + @Override + public void run() { + while (!isInterrupted()) + { + RtpPacket packet; + + try { + packet = packets.take(); + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + + // !!! We no longer own the data buffer at this point !!! + depacketizer.addInputData(packet); + } + } + }; + threads.add(t); + t.setName("Video - Depacketizer"); + t.start(); + } + + private void startReceiveThread() + { + // Receive thread + Thread t = new Thread() { + @Override + public void run() { + ByteBufferDescriptor desc = new ByteBufferDescriptor(new byte[1500], 0, 1500); + DatagramPacket packet = new DatagramPacket(desc.data, desc.length); + + while (!isInterrupted()) + { + try { + rtp.receive(packet); + } catch (IOException e) { + listener.connectionTerminated(e); + return; + } + + // Give the packet to the depacketizer thread + desc.length = packet.getLength(); + if (packets.offer(new RtpPacket(desc))) { + desc.reinitialize(new byte[1500], 0, 1500); + packet.setData(desc.data, desc.offset, desc.length); + } + } + } + }; + threads.add(t); + t.setName("Video - Receive"); + t.start(); + } + + private void startUdpPingThread() + { + // Ping thread + Thread t = new Thread() { + @Override + public void run() { + // PING in ASCII + final byte[] pingPacketData = new byte[] {0x50, 0x49, 0x4E, 0x47}; + DatagramPacket pingPacket = new DatagramPacket(pingPacketData, pingPacketData.length); + pingPacket.setSocketAddress(new InetSocketAddress(host, RTP_PORT)); + + // Send PING every 100 ms + while (!isInterrupted()) + { + try { + rtp.send(pingPacket); + } catch (IOException e) { + listener.connectionTerminated(e); + return; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + listener.connectionTerminated(e); + return; + } + } + } + }; + threads.add(t); + t.setName("Video - Ping"); + t.setPriority(Thread.MIN_PRIORITY); + t.start(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java b/moonlight-common/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java new file mode 100644 index 00000000..874a645e --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/av/video/cpu/AvcDecoder.java @@ -0,0 +1,44 @@ +package com.limelight.nvstream.av.video.cpu; + +public class AvcDecoder { + static { + // FFMPEG dependencies + System.loadLibrary("avutil-52"); + System.loadLibrary("swresample-0"); + System.loadLibrary("swscale-2"); + System.loadLibrary("avcodec-55"); + System.loadLibrary("avformat-55"); + System.loadLibrary("avfilter-3"); + + System.loadLibrary("nv_avc_dec"); + } + + /** Disables the deblocking filter at the cost of image quality */ + public static final int DISABLE_LOOP_FILTER = 0x1; + /** Uses the low latency decode flag (disables multithreading) */ + public static final int LOW_LATENCY_DECODE = 0x2; + /** Threads process each slice, rather than each frame */ + public static final int SLICE_THREADING = 0x4; + /** Uses nonstandard speedup tricks */ + public static final int FAST_DECODE = 0x8; + /** Uses bilinear filtering instead of bicubic */ + public static final int BILINEAR_FILTERING = 0x10; + /** Uses a faster bilinear filtering with lower image quality */ + public static final int FAST_BILINEAR_FILTERING = 0x20; + /** Disables color conversion (output is NV21) */ + public static final int NO_COLOR_CONVERSION = 0x40; + + public static native int init(int width, int height, int perflvl, int threadcount); + public static native void destroy(); + + // Rendering API when NO_COLOR_CONVERSION == 0 + public static native boolean setRenderTarget(Object androidSurface); + public static native boolean getRgbFrame(byte[] rgbFrame, int bufferSize); + public static native boolean redraw(); + + // Rendering API when NO_COLOR_CONVERSION == 1 + public static native boolean getRawFrame(byte[] yuvFrame, int bufferSize); + + public static native int getInputPaddingSize(); + public static native int decode(byte[] indata, int inoff, int inlen); +} diff --git a/moonlight-common/src/com/limelight/nvstream/http/NvApp.java b/moonlight-common/src/com/limelight/nvstream/http/NvApp.java new file mode 100644 index 00000000..70ef03ea --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/http/NvApp.java @@ -0,0 +1,31 @@ +package com.limelight.nvstream.http; + +public class NvApp { + private String appName; + private int appId; + private boolean isRunning; + + public void setAppName(String appName) { + this.appName = appName; + } + + public void setAppId(String appId) { + this.appId = Integer.parseInt(appId); + } + + public void setIsRunning(String isRunning) { + this.isRunning = isRunning.equals("1"); + } + + public String getAppName() { + return this.appName; + } + + public int getAppId() { + return this.appId; + } + + public boolean getIsRunning() { + return this.isRunning; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java b/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java new file mode 100644 index 00000000..374bd650 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/http/NvHTTP.java @@ -0,0 +1,145 @@ +package com.limelight.nvstream.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.URL; +import java.net.URLConnection; +import java.util.LinkedList; +import java.util.Stack; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + + +public class NvHTTP { + private String macAddress; + private String deviceName; + + public static final int PORT = 47989; + public static final int CONNECTION_TIMEOUT = 5000; + + public String baseUrl; + + public NvHTTP(InetAddress host, String macAddress, String deviceName) { + this.macAddress = macAddress; + this.deviceName = deviceName; + this.baseUrl = "http://" + host.getHostAddress() + ":" + PORT; + } + + private String getXmlString(InputStream in, String tagname) + throws XmlPullParserException, IOException { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser xpp = factory.newPullParser(); + + xpp.setInput(new InputStreamReader(in)); + int eventType = xpp.getEventType(); + Stack currentTag = new Stack(); + + while (eventType != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case (XmlPullParser.START_TAG): + currentTag.push(xpp.getName()); + break; + case (XmlPullParser.END_TAG): + currentTag.pop(); + break; + case (XmlPullParser.TEXT): + if (currentTag.peek().equals(tagname)) { + return xpp.getText(); + } + break; + } + eventType = xpp.next(); + } + + return null; + } + + private InputStream openHttpConnection(String url) throws IOException { + URLConnection conn = new URL(url).openConnection(); + conn.setConnectTimeout(CONNECTION_TIMEOUT); + conn.setDefaultUseCaches(false); + conn.connect(); + return conn.getInputStream(); + } + + public String getAppVersion() throws XmlPullParserException, IOException { + InputStream in = openHttpConnection(baseUrl + "/appversion"); + return getXmlString(in, "appversion"); + } + + public boolean getPairState() throws IOException, XmlPullParserException { + InputStream in = openHttpConnection(baseUrl + "/pairstate?mac=" + macAddress); + String paired = getXmlString(in, "paired"); + return Integer.valueOf(paired) != 0; + } + + public int getSessionId() throws IOException, XmlPullParserException { + InputStream in = openHttpConnection(baseUrl + "/pair?mac=" + macAddress + + "&devicename=" + deviceName); + String sessionId = getXmlString(in, "sessionid"); + return Integer.parseInt(sessionId); + } + + public int getSteamAppId(int sessionId) throws IOException, + XmlPullParserException { + LinkedList appList = getAppList(sessionId); + for (NvApp app : appList) { + if (app.getAppName().equals("Steam")) { + return app.getAppId(); + } + } + return 0; + } + + public LinkedList getAppList(int sessionId) throws IOException, XmlPullParserException { + InputStream in = openHttpConnection(baseUrl + "/applist?session=" + sessionId); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser xpp = factory.newPullParser(); + + xpp.setInput(new InputStreamReader(in)); + int eventType = xpp.getEventType(); + LinkedList appList = new LinkedList(); + Stack currentTag = new Stack(); + + while (eventType != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case (XmlPullParser.START_TAG): + currentTag.push(xpp.getName()); + if (xpp.getName().equals("App")) { + appList.addLast(new NvApp()); + } + break; + case (XmlPullParser.END_TAG): + currentTag.pop(); + break; + case (XmlPullParser.TEXT): + NvApp app = appList.getLast(); + if (currentTag.peek().equals("AppTitle")) { + app.setAppName(xpp.getText()); + } else if (currentTag.peek().equals("ID")) { + app.setAppId(xpp.getText()); + } else if (currentTag.peek().equals("IsRunning")) { + app.setIsRunning(xpp.getText()); + } + break; + } + eventType = xpp.next(); + } + return appList; + } + + // Returns gameSession XML attribute + public int launchApp(int sessionId, int appId) throws IOException, + XmlPullParserException { + InputStream in = openHttpConnection(baseUrl + "/launch?session=" + + sessionId + "&appid=" + appId); + String gameSession = getXmlString(in, "gamesession"); + return Integer.parseInt(gameSession); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java b/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java new file mode 100644 index 00000000..b36810bf --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java @@ -0,0 +1,89 @@ +package com.limelight.nvstream.input; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class ControllerPacket extends InputPacket { + public static final byte[] HEADER = + { + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14 + }; + + public static final byte[] TAIL = + { + (byte)0x9C, + 0x00, + 0x00, + 0x00, + 0x55, + 0x00 + }; + + public static final int PACKET_TYPE = 0x18; + + public static final short A_FLAG = 0x1000; + public static final short B_FLAG = 0x2000; + public static final short X_FLAG = 0x4000; + public static final short Y_FLAG = (short)0x8000; + public static final short UP_FLAG = 0x0001; + public static final short DOWN_FLAG = 0x0002; + public static final short LEFT_FLAG = 0x0004; + public static final short RIGHT_FLAG = 0x0008; + public static final short LB_FLAG = 0x0100; + public static final short RB_FLAG = 0x0200; + public static final short PLAY_FLAG = 0x0010; + public static final short BACK_FLAG = 0x0020; + public static final short LS_CLK_FLAG = 0x0040; + public static final short RS_CLK_FLAG = 0x0080; + public static final short SPECIAL_BUTTON_FLAG = 0x0400; + + public static final short PAYLOAD_LENGTH = 24; + public static final short PACKET_LENGTH = PAYLOAD_LENGTH + + InputPacket.HEADER_LENGTH; + + private short buttonFlags; + private byte leftTrigger; + private byte rightTrigger; + private short leftStickX; + private short leftStickY; + private short rightStickX; + private short rightStickY; + + public ControllerPacket(short buttonFlags, byte leftTrigger, byte rightTrigger, + short leftStickX, short leftStickY, + short rightStickX, short rightStickY) + { + super(PACKET_TYPE); + + this.buttonFlags = buttonFlags; + this.leftTrigger = leftTrigger; + this.rightTrigger = rightTrigger; + this.leftStickX = leftStickX; + this.leftStickY = leftStickY; + this.rightStickX = rightStickX; + this.rightStickY = rightStickY; + } + + public byte[] toWire() + { + ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + + bb.put(toWireHeader()); + bb.put(HEADER); + bb.putShort(buttonFlags); + bb.put(leftTrigger); + bb.put(rightTrigger); + bb.putShort(leftStickX); + bb.putShort(leftStickY); + bb.putShort(rightStickX); + bb.putShort(rightStickY); + bb.put(TAIL); + + return bb.array(); + } + } \ No newline at end of file diff --git a/moonlight-common/src/com/limelight/nvstream/input/InputPacket.java b/moonlight-common/src/com/limelight/nvstream/input/InputPacket.java new file mode 100644 index 00000000..dec3ff11 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/InputPacket.java @@ -0,0 +1,26 @@ +package com.limelight.nvstream.input; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public abstract class InputPacket { + public static final int HEADER_LENGTH = 0x4; + + protected int packetType; + + public InputPacket(int packetType) + { + this.packetType = packetType; + } + + public abstract byte[] toWire(); + + public byte[] toWireHeader() + { + ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); + + bb.putInt(packetType); + + return bb.array(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/input/MouseButtonPacket.java b/moonlight-common/src/com/limelight/nvstream/input/MouseButtonPacket.java new file mode 100644 index 00000000..70cea01c --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/MouseButtonPacket.java @@ -0,0 +1,36 @@ +package com.limelight.nvstream.input; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class MouseButtonPacket extends InputPacket { + + private byte buttonEventType; + + public static final int PACKET_TYPE = 0x5; + public static final int PAYLOAD_LENGTH = 5; + public static final int PACKET_LENGTH = PAYLOAD_LENGTH + + InputPacket.HEADER_LENGTH; + + public static final byte PRESS_EVENT = 0x07; + public static final byte RELEASE_EVENT = 0x08; + + public MouseButtonPacket(boolean leftButtonDown) + { + super(PACKET_TYPE); + + buttonEventType = leftButtonDown ? + PRESS_EVENT : RELEASE_EVENT; + } + + @Override + public byte[] toWire() { + ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH).order(ByteOrder.BIG_ENDIAN); + + bb.put(toWireHeader()); + bb.put(buttonEventType); + bb.putInt(1); // FIXME: button index? + + return bb.array(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/input/MouseMovePacket.java b/moonlight-common/src/com/limelight/nvstream/input/MouseMovePacket.java new file mode 100644 index 00000000..dba6f27b --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/MouseMovePacket.java @@ -0,0 +1,42 @@ +package com.limelight.nvstream.input; + +import java.nio.ByteBuffer; + +public class MouseMovePacket extends InputPacket { + + private static final byte[] HEADER = + { + 0x06, + 0x00, + 0x00, + 0x00 + }; + + public static final int PACKET_TYPE = 0x8; + public static final int PAYLOAD_LENGTH = 8; + public static final int PACKET_LENGTH = PAYLOAD_LENGTH + + InputPacket.HEADER_LENGTH; + + private short deltaX; + private short deltaY; + + public MouseMovePacket(short deltaX, short deltaY) + { + super(PACKET_TYPE); + + this.deltaX = deltaX; + this.deltaY = deltaY; + } + + @Override + public byte[] toWire() { + ByteBuffer bb = ByteBuffer.allocate(PACKET_LENGTH); + + bb.put(toWireHeader()); + bb.put(HEADER); + bb.putShort(deltaX); + bb.putShort(deltaY); + + return bb.array(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/input/NvController.java b/moonlight-common/src/com/limelight/nvstream/input/NvController.java new file mode 100644 index 00000000..b0b85984 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/NvController.java @@ -0,0 +1,65 @@ +package com.limelight.nvstream.input; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class NvController { + + public final static int PORT = 35043; + + public final static int CONTROLLER_TIMEOUT = 3000; + + private InetAddress host; + private Socket s; + private OutputStream out; + + public NvController(InetAddress host) + { + this.host = host; + } + + public void initialize() throws IOException + { + s = new Socket(); + s.connect(new InetSocketAddress(host, PORT), CONTROLLER_TIMEOUT); + s.setTcpNoDelay(true); + out = s.getOutputStream(); + } + + public void close() + { + try { + s.close(); + } catch (IOException e) {} + } + + public void sendControllerInput(short buttonFlags, byte leftTrigger, byte rightTrigger, + short leftStickX, short leftStickY, short rightStickX, short rightStickY) throws IOException + { + out.write(new ControllerPacket(buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY).toWire()); + out.flush(); + } + + public void sendMouseButtonDown() throws IOException + { + out.write(new MouseButtonPacket(true).toWire()); + out.flush(); + } + + public void sendMouseButtonUp() throws IOException + { + out.write(new MouseButtonPacket(false).toWire()); + out.flush(); + } + + public void sendMouseMove(short deltaX, short deltaY) throws IOException + { + out.write(new MouseMovePacket(deltaX, deltaY).toWire()); + out.flush(); + } +}